Here we introduce you to the components of the WebPython toolkit, the concept of our object-relational database interface, and the theory behind the toolkit.
Bored yet? Check out the "Hello World" application.
Now that you have WebPython installed, it is time to show off what it can do. First, setup our project (assuming you have installed Apache in /home/user/apache; change accordingly):
webpython new_project -name HelloWorld\ -httpd_path /home/user/apache/conf/ -docroot /home/user/public_html/
This command will create a directory named HelloWord in your DocumentRoot, and add four subdirectories to this directory. The four subdirectories are a code directory, an images directory, a modules directory, and a templates directory. Only the code directory can contain the Python files to be served.
So... Create a file called index.py from the following code listing and save it in $DocumentRoot/HelloWorld/code (replace $DocumentRoot with your actual DocumentRoot. Duh!).
import time from WebPython import template, date def index(req): AppRoot = '/home/user/public_html/HelloWorld/' current_date = date.ConvertDateString( time.strftime('%Y-%m-%d'), 'dd mon yyyy' ) current_time = time.strftime('%H:%M') template_vars = { 'current_date':current_date, 'current_time':current_time, 'message':'Hello World!' } template.TemplateInit(req, 'error.log') return template.LoadTemplate(AppRoot + 'templates/index.tpl', template_vars)
And then create a file called index.tpl from the following (X)HTML and save it in $DocumentRoot/HelloWorld/templates.
<html> <head> <title>Hello from WebPython</title> </head> <body> <h1>{$message}</h1> <hr> <p>Today is {$current_date}. The time is {$current_time}.</p> </body> </html>
So how does it work? Let's break it down. First we need to import the modules we need from WebPython (we need to import the time module, too, but that is self-explanatory). We do this with the following line, which imports the templating engine and date utilities.
from WebPython import template, date
Next, we define a function called index, which takes a mod_python request object as an argument. Then comes the declaration of an AppRoot variable, which holds a string representing the working directory of our application (don't forget to set this to your actual application directory). After that, we get the current date and time, using the standard Python time utilites. However, we convert the date from 'yyyy-mm-dd' format to 'dd mon yyyy' using date.ConvertDateString() from WebPython. This could, of course, be easily done using time.strftime(), but this way, you get to see the mod_python date routines in action.
The ConvertDateString() function takes two arguments, the string representation of a date to convert, and a format string. Valid formats are:
The next thing we do in the code is to create a dictionary of template variables (as keys) and their values. You will notice that the template (index.tpl) has pieces of text of the form {$variable}. These are the variables that are replaced by the corresponding values defined in the application code. In this example, it is very simple: we have an <h1> tag that holds our message, and then a paragraph displaying the date and time.
We then initialize the templating engine by passing it the request object and the path to the file where we want any error messages logged. And finally, we call our template, passing the template.LoadTemplate() function the path to the template and the dictionary of variables and their values. We then display the resulting HTML.
The whole thing is quite simple, and we easily preserve separation of logic and presentation throughout. But after all, it is just a toy application. How well does WebPython scale to something more representative of the Real World? To answer that question, I later present a (relatively) real-world application for our guided tutorial.
First, though, some background information and theory.
The WebPython toolkit is, as it is called in the Python world, a package. That is, it is a collection of Python modules which, while useable independently, reside under a shared namespace. This is useful, as it prevents (at least theoretically) global namespace pollution.
WebPython consists of the following sub-modules:
But wait, what is the "database sub-package?" This is exactly what it sounds like: a package of modules residing within another package. In this case, WebPython has so many different database routines that it made sense to divide them into modules and put them in a package within WebPython. Lumped together, the resulting module would be unmanageably huge, split into separate modules under the base package, the layout would be needlessly complicated.
The contents of this package are as follows:
Note: This section contains some possibly confusing theory. Please read it all anyway.
In the preceding section, we looked at the components of the WebPython toolkit. By far, the most complex (and most critical) is the database layer. This layer of software allows us to interface to a given database (hosted by either MySQL or PostgreSQL) and retrieve or change either the data contained within it or its structure. And the WebPython database layer allows us to achieve this without using SQL at all.
It does this by means of a concept called "object-relational mapping," which happens to be somewhat nebulous, with many different styles of implementation. In WebPython, the appropriate database module, when the LoadDatabase function is called, queries the database server and builds a set of information about the requested database. This consists of descriptive information (the database name, the tables contained within it, the structure of those tables, etc.) as well as the actual information stored in the database's tables. It then proceeds to build an object containing this data, along with methods reflecting common actions performed by or on databases.
Within this database object are contained object representations of the tables. Each table object is named after the corresponding table in the database, possesses members representing its structural information (fields, mostly), and methods representing actions (fetching the contents, searching for a given value, inserting rows, deleting rows, etc.).
As one can see, the version of object-relational mapping used by WebPython does not attempt to build a representation of the data stored in a given database, but rather attempts to build a set of objects through which the database can be transparently manipulated. The goal is to keep the application server(s) free to serve the application, rather than requiring them to extensively manipulate the data. Actions are offloaded to the database engine as much as possible (and as is practical).
Nearly on par with the database layer in terms of importence is the templating engine. However, it is much less complex. Basically, it takes a user-supplied template with variables embedded within it and replaces the variables with application-supplied data. Here, variables are enclosed by braces ("{" and "}") and preceeded by a dollar sign ("$"). So, a variable representing the contents of a paragraph would look like this (in context):
<p>{$paragraph_text}</p>
Any alphanumeric (and extra) charater can be used in a variable identifier, with the exception of braces and dollar signs.
On a more complex level, the WebPython templating engine contains simple switch and loop statements. The "check:" statement (essentially a switch) checks a to see whether a supplied variable has a Boolean value of True. If it does, "check:" tells the templating engine to display the contents within its scope, substituting out any variables. An example would probably help:
{check: $display_hello} <h1>Hello, World!</h1> {end check}
Here, if $display_hello has been set to True (or any Boolean equivalent) in the application, then the HTML will be displayed. If not, nothing will be displayed.
The scope of the "check:" statement spans from the initialization ("{check: $display_hello}") to the end ("{end check}").
The "loop:" statement acts much like the Python for loop. It loops over a list of key-homogeneous dictionaries supplied in the application, substitues the keys for their values, displays the result, and repeats until all dictionaries have been used. An example here would very definitely help:
{loop: $numbers} <p>Name: {$numbers.name}. Numeral: {$numbers.numeral}.</p> {end loop}
Given that the list of dictionaries registered as "numbers" looked like this:
[{'name':'One', 'numeral':'1'}, {'name':'Two', 'numeral':'2'}, {'name':'Three', 'numeral':'3'}]
The output of this loop would be thus:
<p>Name: One. Numeral: 1.</p> <p>Name: Two. Numeral: 2.</p> <p>Name: Three. Numeral: 3.</p>
Simple! But not necessarily fool-proof.
So that is everything one needs to know about the current templating system. It is somewhat primitive, but I have found it to work well for almost any presentation needs that one might encounter with a web application. And it is reasonably fast, though it lacks caching. I plan to add caching capabilities of at least a few types (caching of "empty" templates and caching of generated documents) in upcoming releases.
The final central technology of WebPython is PyRPC, the Python Remote Procedure Call mechanism. As mentioned in the overview, PyRPC differs from most RPC systems by using Python dictionaries as the data transport, as opposed to XML. This (at least theoretically) is a faster approach, as no "external" parsing of data is necessary. As a side note, because JavaScript has an identical associative array syntax, this means that PyRPC is easily useable for client-side scripting of web applications.
PyRPC works by having the programmer create a class holding methods corresponding to the RPCs they wish to make, and then passing this class to the PyRPCServer class when a request is received. Usually, this passing occurs in a wrapper function which takes the client-supplied request (in the form of a URL) and, if appropriate, executes the requested action, returning any resulting data.
In other words, PyRPC is a simple RPC system with integrated safety checks for data integrity and execution scope.