4.3. Composing Web Pages in Python

4.3.1. Dynamically Created Static Local Pages from Python

For the rest of this chapter, the example files will come from the www directory under the main examples directory you unzipped. I will refer to example file there as “example www files”.

As the overview indicated, dynamic web applications typically involve getting input from a web page form, processing the input in a program on the server, and displaying output to a web page. Introducing all these new ideas at once could be a lot to absorb, so this section uses familiar keyboard input into a regular Python program and then, like in the final version, processes the input and produces the final web page output.

Follow this sequence of steps:

  1. Open the example www file hello.html in your browser, to see what it looks like.
  2. Change your browser view - for instance go back to the previous page you displayed.
  3. Open the same hello.html file in Kompozer, if that works for you, or another editor that will show the html source, as discussed in HTML Source Markup.
  4. If using Kompozer, switch to the Source view (clicking the Source tab). Sometimes you will want to copy HTML text into a Python program. For instance, I selected and copied the entire contents of the hello.html source view and pasted it into a multi-line string in the Python program shown and discussed below.
  5. Careful, note the change from past practice here: Start Python from inside the www directory. In Windows you may start Idle with the IdleOnWindows shortcut that I placed in the www directory, not the original example directory.
  6. Open the www example program helloWeb1.py in an Idle edit window.
  7. Run it.

You should see a familiar web page appear in your default browser (possibly not the one you have been using). This is obviously not a very necessary program, since you can select this page directly in your browser! Still, one step at a time: it illustrates several useful points. The program is copied below. Read it:

'''A simple program to create an html file froma given string,
and call the default web browser to display the file.'''

contents = '''<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=ISO-8859-1"
 http-equiv="content-type">
  <title>Hello</title>
</head>
<body>
Hello, World!
</body>
</html>
'''

def main():
    browseLocal(contents)

def strToFile(text, filename):
    """Write a file with the given name and the given text."""
    output = open(filename,"w")
    output.write(text)
    output.close()

def browseLocal(webpageText, filename='tempBrowseLocal.html'):
    '''Start your webbrowser on a local file containing the text
    with given filename.'''
    import webbrowser, os.path
    strToFile(webpageText, filename)
    webbrowser.open("file:///" + os.path.abspath(filename)) #elaborated for Mac

main()

This program encapsulates two basic operations into the last two functions that will be used over and over. The first, strToFile, has nothing new, it just puts specified text in a file with a specified name. The second, browseLocal, does more. It takes specified text (presumably a web page), puts it in a file, and directly displays the file in your default web browser. It uses the open function from the webbrowser module to start the new page in your web browser.

The open function here requires the name of a file or URL. Since the page is automatically generated by the program for one-time immediate viewing, it automatically uses the same throwaway filename, tempBrowseLocal.html specified as the default in the keyword parameter. If you really want another specific, name you could pass it as a parameter.

In this particular program the text that goes in the file is just copied from the literal string named contents in the program.

This is no advance over just opening the file in the browser directly! Still, it is a start towards the aim of creating web content dynamically.

An early example in this tutorial displayed the fixed Hello World!' to the screen. This was later modified in hello_you4.py to incorporate user input using the string format method of Dictionaries and String Formatting,

person = input('Enter your name: ')
greeting = 'Hello {person}!'.format(**locals())
print(greeting)

Similarly, I can turn the web page contents into a format string, and insert user data. Load and run the www example program helloWeb2.py.

The simple changes from helloWeb1.py are marked at the beginning of the file and shown below. I modified the web page text to contain ‘Hello, {person}!’ in place of ‘Hello, World!’, making the string into a format string, which I renamed to the more appropriate pageTemplate. The changed initial portion with the literal string and and the main program then becomes

pageTemplate = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=ISO-8859-1"
 http-equiv="content-type">
  <title>Hello</title>
</head>
<body>
Hello, {person}!
</body>
</html>''' # NEW note '{person}' two lines up

def main():    # NEW
    person = input("Enter a name: ")  
    contents = pageTemplate.format(**locals())   
    browseLocal(contents) 

Now the line

contents = pageTemplate.format(**locals())

incorporaties the person’s name into the contents for the web page before saving it to a file and displaying it.

In this case, I stored the literal format string inside the Python program, but consider a different approach:

Load and run the www example program helloWeb3.py. It behaves exactly like helloWeb2.py, but is slightly different internally - it does not directly contain the web page template string. Instead the web page template string is read from the file helloTemplate.html.

Below is the beginning of helloWeb3.py, showing the only new functions. The first, fileToStr, will be a standard function used in the future. It is the inverse of strToFile.

The main program obtains the input. In this simple example, the input is used directly, with little further processing. It is inserted into the web page, using the file helloTemplate.html as a format string.

def fileToStr(fileName): # NEW
    """Return a string containing the contents of the named file."""
    fin = open(fileName); 
    contents = fin.read();  
    fin.close() 
    return contents

def main():
    person = input('Enter a name: ')  
    contents = fileToStr('helloTemplate.html').format(**locals())   # NEW
    browseLocal(contents) 

Although helloTemplate.html is not intended to be viewed by the user (being a template), you should open it in a browser or web editor (Kompozer or ...) to look at it. It is legal to create a web page in a web page editor with expressions in braces embedded in it! If you look in the source view in Kompozer or in a web source editor, you will see something similar to the literal string in helloWeb2.py, except the lines are broken up differently. (This makes no difference in the formatted result, since in html, all white space is considered the same.)

Back in the Normal mode in Kompozer, or in source mode for any html editor, add an extra line of text right after the line “Hello, {person}!”. Then save the file again (under the same name). Run the program helloWeb3.py again, and see that you have been able to change the appearance of the output without changing the Python program itself. That is the aim of using the template html page, allowing the web output formatting to be managed mostly independently from the Python program.

A more complicated but much more common situation is where the input data is processed and transformed into results somehow, and these results, often along with some of the original input, are embedded in the output web page that is produced.

As a simple example, load and run the www example program additionWeb.py, which uses the template file additionTemplate.html.

The aim in the end of this chapter is to have user input come from a form on the web rather than the keyboard on a local machine, but in either case the input is still transformed into results and all embedded in a web page. To make parts easily reusable, I obtain the input in a distinct place from where the input is processed. In keeping with the later situation with web forms, all input is of string type (using keyboard input for now).

Look at the program. You will see only a few new lines! Because of the modular design, most of the program is composed of recent standard functions reused.

The only new code is at the beginning and is shown here:

def processInput(numStr1, numStr2):  # NEW
    '''Process input parameters and return the final page as a string.'''
    num1 = int(numStr1) # transform input to output data
    num2 = int(numStr2)
    total = num1+num2
    return fileToStr('additionTemplate.html').format(**locals())

def main(): # NEW
    numStr1 = input('Enter an integer: ')  # obtain input
    numStr2 = input('Enter another integer: ')  
    contents = processInput(numStr1, numStr2)   # process input into a page
    browseLocal(contents) # display page

The input is obtained (via input for now), and it is processed into a web page string, and as a separate step it is displayed in a local web page.

There are a few things to note:

  • All input is strings. Before the numerical calculations, the digit strings must be converted to integers.
  • I do calculate (a very simple!) result and use it in the output web page.
  • Although it is not in the Python code, an important part of the result comes from the web page format string in additionTemplate.html, which includes the needed variable names in braces, {num1}, {num2}, and {total}. View it in your browser or in a web editor.

When you write your own code, you might modify additionWeb.py, or you can start from a stripped down skeleton in the example www folder, skeletonForWeb.py, with comments about where to insert your special code.

We will examine the bottom part of the following diagram later. The top part outlines the flow of data from string input to web page in your browser for a regular Python program like what we have been describing, with the processing outlined in the middle line. The parts in the middle will be common to the later client/server program, that manges input and output with the bottom line, that we will discuss later.

image

Again, this last section was somewhat artificial. You are not in the end likely to find such programs practical as end products. However such programs are reasonable to write and test and they include almost all the code you will need for a more practical (but harder to debug) CGI program, coming next....

4.3.1.1. Quotient Web Exercise

* Save additionWeb.py or skeletonForWeb.py as quotientWeb.py. Modify it to display the results of a division problem in a web page. As in the exercises in Chapter 1, display a full sentence labeling the initial data and both the integer quotient and the remainder. You can take your calculations from Quotient String Return Exercise. You should only need to make Python changes to the processInput and main functions. You will also need the HTML for the output page displayed. Make a web page template file called quotientTemplate.html and read it into your program. Turn in both quotientWeb.py and quotientTemplate.html.