VAMPIRE

Quick Overview of Vampire

Author: Graham Dumpleton
Contact: grahamd@dscpl.com.au
Updated:10/05/2005

This article provides an introduction to Vampire, giving an overview of its major features.

Vampire is Not a Framework

Vampire builds on top of mod_python, an Apache module that embeds the Python interpreter within the Apache server. Using mod_python it is possible to intercept any phase of Apache's handling of a HTTP request and have Python code executed in order to generate the desired result for that phase of processing. The standard mod_python handler mechanism only allows one content handler to be associated with a whole directory when handling the content delivery phase of servicing a HTTP request. Vampire implements a mechanism for selecting between multiple content handlers associated with distinct resources within a directory.

Vampire can be seen as an extension of the "AddHandler" and "SetHandler" directives which are normally used to dictate which content handler is used to service a request. Rather than such a decision being made by Apache, it is brought into the scope of Python code executing under mod_python. Doing this results in a greater ability to control exactly which content handler is used to generate the content returned in response to a request.

Although all requests falling within a designated directory are initially passed to Vampire, only requests of interest are processed by it. Specifically, only requests for a resource where an associated Python code file exists containing a subordinate content handler will actually be processed within the context of Vampire. All other requests are passed back to Apache to be handled.

What this means is that you can still have a mixture of resources located within a directory. For example, plain text or HTML files and image files which physically exist and where no or partial processing is required, along with virtual resources that are generated on demand. Except for the case where special Python code needs to be executed, the basic handling mechanisms of Apache are used in returning the content of the file, thus ensuring there is no significant loss in performance for normal files.

In addition to expanding on the basic content handler mechanism, Vampire also provides a drop in replacement for mod_python.publisher. Some obscure corners of this replacement for mod_python.publisher are a little different to the original. These differences derive however from the fact that the Vampire version fixes many bugs in the original and also addresses some of its shortcomings and limitations. In order to do this, it wasn't practical to maintain 100 percent compatibility as that would have mean't preserving what could be seen as incorrect or buggy behaviour.

Most important of all to note is that Vampire is not a framework in itself. Its prime purpose is to make the basic mechanisms available through using mod_python more flexible. Vampire does not try to enforce on you a particular approach to implementing an application nor does it try and dictate a particular solution for performing page templating. In fact, it is relatively straight forward to integrate the use of frameworks for creating actual HTML output such as PSP, mpservlets, HTMLTemplate and Cheetah. Use of HTMLTemplate does however get a head start on the others and is more easy to use by virtue of Vampire integrating a template loading and caching system for that system.

Major Features of Vampire

In addition to providing an extended content handler selection mechanism for mod_python, Vampire also supplies a number of other useful features which can help to ease the construction of mod_python based web applications and web services. The full list of the major features provided by Vampire are:

  • A extended content handler selection mechanism which allows unique content handlers to be associated with distinct resources in a directory. The standard mod_python handler mechanism only allows one content handler to be associated with a whole directory.
  • A mechanism for having default content handlers triggered for different types of resources when no dedicated content handler exists. This enables a handler to be defined once for the processing of special files such as a ".psp" file, thereby enabling easy integration and mixing of third party templating systems even within the same directory.
  • Automatic unmarshalling and passing of form data to a basic content handler method when a content handler defines extra arguments in addition to that of the request object. Previously this was only available to users of the mod_python.publisher module.
  • An extension to the form parameter handling mechanism which through use of appropriate naming conventions, enables form parameters to be converted into structured types such as lists and dictionaries where appropriate.
  • A user authentication mechanism for basic content handlers. Previously this was only available to users of the mod_python.publisher module.
  • A drop in replacement to the mod_python.publisher module implementation which is distributed with mod_python. This implementation addresses many bugs which are present within the standard version as well as providing extra enhancements such as the ability to traverse into instances of new style classes. This alternative implementation also provides the same functionality when either mod_python 2.7.X or 3.1.X is used where as that supplied with mod_python behaves differently in each version.
  • A version of the publisher mechanism implemented as a wrapper class which can be selectively applied within the context of a basic content handler. This allows for the mixing of the basic content handler mechanism and the publisher mechanism within the one directory.
  • A special wrapper which can be applied to methods exported by the publisher mechanism, which allows additional path information to be supplied, with the path being passed in as a single parameter.
  • A special wrapper which can be applied to methods exported by the publisher mechanism, which allows additional path information to be supplied, with the path being split into components and passed as separate parameters.
  • An improved module importing and caching system which can track parent/child relationships between modules and will automatically reimport a parent module when the child module has changed. This avoids the constant problem in the standard mod_python module loader whereby Apache has to be restarted after any changes are made, or all files have to be touched to force reloading.
  • An application configuration mechanism incorporating a dynamic search capability for finding an appropriate configuration file within a parent directory without the need to hard code relative paths into code.
  • A special handler for servicing XML-RPC requests and mapping them into code associated with your application. This makes it possible to also write external scripts which can interact with your application and turn it into a web service.
  • A template loading and caching system for the third party web templating engine called HTMLTemplate.

Philosophy Behind Vampire

When using Apache and wishing to define a content handler, there are two possible ways to cause mod_python to be triggered. You can use the "AddHandler" directive to indicate that a specified content handler be used for all requests where a specific file extension is used, or you can use the "SetHandler" directive to have all requests falling within a directory be processed by a specified content handler. The best two examples where these mechanisms are generally used are "mod_python.psp" in the first instance and "mod_python.publisher" in the latter.

When using the "AddHandler" directive, it is possible to specify multiple content handlers, however the means of distinguishing which is to be used is based upon the extension of the resource specified in the URL for the request. This results in the use of arbitrary extensions which are bound to the specific implementation mechanism being used to generate the content returned.

For example, although HTML markup may be returned and a ".html" extension would conceptually be most appropriate, when using mod_python.psp your request has to use ".psp" as the extension. If you also wanted to mix in the use of the mod_python.servlet module for generating content, for those cases you must use the extension ".mps". This distinction is required otherwise Apache will not know to hand the request off to a different content handler for processing.

Although you have an arbitrary extension being used which is not directly related to the type of content being returned, both of these systems do however ultimately derive the result from a file specific to the resource being requested. In the case of mod_python.psp, the file which is the target of the request is a HTML template incorporating embedded code. In the case of mod_python.servlet the file contains Python code implementing a servlet instance which would generate the HTML programmatically.

However, because each system uses a different extension to identify it, if it so happened that at one point you wanted to change the mechanism used to generate the response for a request of a specific resource from using mod_python.psp to mod_python.servlet, the URL under which that resource is accessed will now need to be changed, along with all the links to it. If the resource had been bookmarked by users or indexed by a search engine, all such references would be made invalid.

If the "SetHandler" directive and "mod_python.publisher" is used then extensions aren't typically used at all. In fact the "." character has special significance when it comes to the lookup mechanism used to determine the target method for handling of a request.

Now it can be argued that since a response includes a definition of the content type anyway, that the extension appearing on a request is irrelevant and this is one of the basic ideas underlying the REST architecture. If an extension is used however, it is believed that it is still conceptually preferable that it relate to the type of content being returned and not the implementation mechanism used to create that content.

An important philosophy thus underlying Vampire, is that URLs should not expose the means by which a web site is implemented. If a resource is presented as HTML markup and an extension is going to be used, then the URL for that resource should use a ".html" extension regardless of what implementation mechanism is used to generate the page. If the mechanism used to generate the content is changed, the URL should be able to remain as it was.

Further, the content handler which is used to service a request should primarily be determined based on the name of the resource being requested and not the extension on the request. As such, individual resources should each have their own content handler stored in a file specific to the resource. Only where a resource can be represented in multiple formats, would the file associated with that resource contain more than one content handler, one for each data format in which the resource is available.

How Vampire Actually Works

As does mod_python.publisher, Vampire utilises the "SetHandler" directive and it is possible to have multiple Python code files in a directory which can be individually addressed as a resource. Where it differs though is that each of the handler methods in those Python code files follows the same form as a standard mod_python content handler as illustrated below.

from mod_python import apache

def handler(req):
  req.content_type = "text/html"
  req.send_http_header()
  req.write("<html><body>Give Blood!</body></html>")
  return apache.OK

Although there might be multiple content handlers defined in one Python code file, this is only to facilitate delivering up that specific resource in different formats. Which of the content handlers within a specific Python code file will be executed will be determined by looking at the file extension appearing in the URL supplied with the request.

As example, consider that you have a resource called "donors". The Python code file for mediating access to this resource must be called "donors.py". If you wish to adhere to the REST architecture ideal of no file extensions, the code appearing above would be used. When a request is received the "handler()" method will be executed to generate the response.

If instead you still wish to use extensions within the URL, the extension used should be that which is normally equated with the type of content to be returned for that request. If the content type is "text/html", you would therefore use an extension of ".html". In this case, the code shown above would need to be changed such that instead of "handler()" the method is instead called "handler_html()".

from mod_python import apache

def handler_html(req):
  req.content_type = "text/html"
  req.send_http_header()
  req.write("<html><body>Give Blood!</body></html>")
  return apache.OK

Where a resource can be presented in multiple formats, multiple content handler methods can be defined within the one code file, with each method having the extension incorporated into the name of the handler method as appropriate.

from mod_python import apache

# Handler for ".html" request.

def handler_html(req):
  req.content_type = "text/html"
  req.send_http_header()
  req.write("<html><body>Give Blood!</body></html>")
  return apache.OK

# Handler for ".txt" request.

def handler_txt(req):
  req.content_type = "text/plain"
  req.send_http_header()
  req.write("Give Blood!\n")
  return apache.OK

A request against this resource may now use "donors.html" or "donors.txt" in the request URL. For a URL containing "donors.html", the "handler_html()" method will be executed to generate the response, with HTML markup being returned. For a URL containing "donors.txt" the "handler_txt()" method will be executed to generate the response, with plain text being returned.

If a request arrives which is matched to a resource and no appropriate handler method is found corresponding to the extension used, control is passed back to Apache. If Apache could not then find an actual physical file corresponding to the request, it would result in a "Not Found" error being returned, otherwise Apache would return the content corresponding to the physical resource. This means that for a single resource, it may exist in both physical form or virtual form accessible under different extension types for that resource.

If any attempt is made to access the actual Python code file by supplying the ".py" extension, it will result in a "Not Found" error being returned by Vampire. This ensures that the Python code is never accessible to users of a web site and that the fact that Python is used is also not evident.

Apache Configuration Files

Apache can be configured to use Vampire from within a ".htaccess" file or the main Apache configuration file. You should first use the "SetHandler" directive to enable use of mod_python and then the "PythonHandler" directive to specify use of Vampire. The configuration settings should be included within a "Directory" directive if being placed in the main Apache configuration file.

SetHandler python-program
PythonHandler vampire
PythonPath 'sys.path'

Setting "PythonPath" as shown is highly recommended but is optional. Setting "PythonPath" in this way ensures that the document tree directory does not get added into the Python module search path. This is beneficial because there is then no risk that arbitrary ".py" files in your document tree will errornously get picked up as being modules. It does mean though that the Vampire module loading system needs to be used to explicitly load modules which are stored in the document tree.

It is also recommended that both "MultiViews" and "Indexes" be disabled. This is necessary because extensions such as mod_negotiation and mod_index can get applied to requests falling beneath the directory which has been set up to be managed by Vampire. If "MultiViews" is enabled, Apache can rewrite request URLs that it shouldn't and confuse Vampire. If "Indexes" are enabled a remote person would be able to view what source code may exist in a directory.

Options -Indexes -MultiViews

Processing Form Parameters

When defining a content handler for mod_python, the handler method would nearly always accept at least a single argument corresponding to the request object. The name of this argument must be called "req" and will be needed in order to access details in respect of a request and also provides the means to send back a response to the request. Similar to how mod_python.publisher works, Vampire enhances the content handler mechanism such that if additional arguments are defined, any form parameters will be automatically decoded and passed to the corresponding argument.

from mod_python import apache

# Handler for ".txt" request.

def handler_txt(req,message=""):
  if not message: message = "Give Blood!"
  req.content_type = "text/plain"
  req.send_http_header()
  req.write(message)
  return apache.OK

Vampire does however take the automatic decoding of form parameters a step beyond what is provided by mod_python.publisher. Namely, provided that certain naming conventions for form parameters are adhered to, form parameters can also be automatically converted into structured types such as lists and dictionaries.

The naming convention used here is that if a set of form parameters should be converted into a list, they should be named as "name-1", "name-2", etc. For this case, the value associated with each form parameter with "name" prefix will be placed into a list and will passed to the function parameter called "name".

Dictionaries will be constructed if a "." is used within the name of a form parameter, thereby creating a naming hierarchy. For example, a form parameter with name "parent.child" will result in a dictionary being passed to the function parameter called "parent", where the dictionary holds the value of the form parameter indexed by "child".

Multiple elements may be placed into a dictionary by sharing the same parent node name. Dictionaries can also be made to hold lists of values and lists of values can also be made to hold dictionaries through appropriate use of the "." and "-" designations in the names of form parameters.

Using Templating Systems

Even when using Vampire, it is still possible to use mod_python.psp by defining an appropriate "AddHandler" and "PythonHandler" directive for the ".psp" extension along side the configuration directives for Vampire. This will result in any requests using the ".psp" extension still being processed by mod_python.psp instead of Vampire.

Alternatively, and especially if you wanted to eliminate the use of the ".psp" extension and use ".html" instead, you could invoke mod_python.psp from inside the content handler executed by Vampire.

from mod_python import apache, psp

import os

# Handler for ".html" request.

def handler_html(req):
  path = os.path.splitext(req.filename)[0] + ".psp"
  if os.path.exists(path):
    req.content_type = "text/html"
    settings = { "form": req.form }
    template = psp.PSP(req,filename=path,vars=settings)
    template.run()
    return apache.OK
  return apache.DECLINED

# Handler for ".psp" request.

def handler_psp(req):
  if os.path.exists(req.filename):
    return apache.HTTP_NOT_FOUND
  return apache.DECLINED

The intent here is that the PSP markup would still be placed into a file with ".psp" extension, however, direct access to the raw content by using the ".psp" extension would be blocked. Instead, when a request arrives for the resource using the ".html" extension, the ".psp" file will be used as input to the PSP template system in order to generate the required content. This ensures that the mechanism used to generate the response isn't implied by the type of extension as is the case when mod_python.psp is normally used.

Although this could be put into the Python code file sitting along side the ".psp" file, doing this for every ".psp" file would be labourius. Instead, Vampire provides a mechanism through its configuration system for defining a default set of content handlers to be applied for different extensions when no specific content handler could be found. Thus a content handler could be defined once and it would be applied in all cases where a ".html" request is made and a corresponding ".psp" file exists.

Even if a default content handler for an extension is defined, a specific content handler defined in a Python code file corresponding to the resource still takes precendence. You might therefore still override the processing for a specific resource in order to take more control over how the environment is setup before triggering the execution of the specific templating system.

Similar or slightly different techniques to that described aboved can be employed to integrate any other third party templating system which you may wish to use. Such a task should be relatively straight forward, especially if the package has already been setup to be used with mod_python in some way previously.

User Authentication Hooks

Vampire uses the same style of interface as mod_python.publisher for performing user authentication. This can be performed at file scope and thus applies to all content handlers connected to a resource, or may also be implemented independently for each different handler if necessary. Implementing authentication at file scope does however make more sense when using Vampire since a file is notionally connected with only one resource whereas in mod_python.publiser one file can contain handlers for multiple distinct resources.

from mod_python import apache, psp

import os

__auth_realm__ = "Blood Bank"
__auth__ = { "donor": "blood" }

# Handler for ".html" request.

def handler_html(req):
  path = os.path.splitext(req.filename)[0] + ".psp"
  if os.path.exists(path):
    req.content_type = "text/html"
    settings = { "form": req.form }
    template = psp.PSP(req,filename=path,vars=settings)
    template.run()
    return apache.OK
  return apache.DECLINED

If user authentication should be applied across a whole directory, an alternate approach would be to define an authentication handler. This can be done by defining the "PythonAuthenHandler" directive, or by using the Vampire configuration mechanism to define the authentication handler.

When an authentication handler is used in this way, it does not disable the mod_python.publisher style authentication mechanism by default. To avoid possible conflict, Vampire also provides a way of disabling this mechanism. This same mechanism however, instead of being used to disable the default mechanism, can also be used to specify an alternate authentication mechanism to control login and access to a site. This may for example make use of HTML form based login and sessions.

Configuration Mechanism

A standard Python installation provides the "ConfigParser" module for implementing of configuration files. Vampire uses this to allow some aspects of its internal configuration and behaviour to be overridden. This configuration file may however also be used for application data.

Normally when using "ConfigParser" it is necessary to explicitly define the location of the configuration file to be read. In Vampire however, a dynamic search mechanism is used to find the appropriate configuration file which pertains to code being executed to handle a particular request.

import vampire

def handler(req):
  config = vampire.loadConfig(req,".vampire")
  templates = config.get("Settings","templates")
  ...

What will occur is that a search will be performed starting at the physical directory which the URL mapped to, back up the directory hierarchy until the root directory where the original "PythonHandler" directive was defined so as to use Vampire. Along the way, if a configuration file with the specified name is found in one of the directories, that configuration file will be the one which is used.

This search mechanism means that a single configuration file can be specified in the root directory of an application and it will be automatically found by any request handlers falling anywhere under that root directory. By the configuration file being dynamically located in this way, it avoids having to encode relative or absolute path names into code and makes it easy to move code between different directories.

One means of using the configuration mechanism is to make available application settings stored within the configuration file within the environment of available variables in a templating system.

from mod_python import apache, psp

import os

# Handler for ".html" request.

def handler_html(req):
  path = os.path.splitext(req.filename)[0] + ".psp"
  if os.path.exists(path):
    req.content_type = "text/html"
    config = vampire.loadConfig(req,".vampire")
    settings = {}
    for key,value in config.items("Settings"):
      settings[key] = value
    settings["form"] = req.form
    template = psp.PSP(req,filename=path,vars=settings)
    template.run()
    return apache.OK
  return apache.DECLINED

Vampire makes this even more useful by predefining some special variables within the configuration which could then be used within template code. These special variables are:

__handler_root__
Absolute path name of the directory where the PythonHandler directive was defined which pertains to the request being handled.
__config_root__
Absolute path name of the directory containing the configuration file being used.
__baseurl_abs__
Absolute URL which maps to the directory in which the configuration file being used was located.
__baseurl_rel__
Relative URL in respect of the request URL which maps to the directory in which the configuration file being used was located.

These may be used direct by a template, or could be used in the configuration file itself as part of the definition of a variable, with it being expanded automatically when accessed. The inbuilt variable "__baseurl_rel__" is particularly useful in constructing relative URLs to special files such as images and stylesheets.

[Settings]

templates = %(__config_root__)s/templates
images = %(__baseurl_rel__)s/images
styles = %(__baseurl_rel__)s/styles

Module Importing System

When changes are made to content handlers, Vampire will automatically reload the code the next time the resource is accessed. Although mod_python provides an import system for content handlers, Vampire does not use this. This is because the system which is provided with mod_python doesn't handle very well the situation where you might have multiple Python code files of the same name located in different directories, nor does it handle very well the situation where the name of a file corresponds to that of a standard Python module.

The main problem with the module importing system supplied with mod_python is that it has to reload a module when it detects that it has previously loaded a module of the same name but from a different location. If requests cycle between the resources corresponding to the modules in the different locations, a reload is done for every request. As well as being inefficient, problems can arise if any data is held at global scope within the module, with the modules possibly interfering with the operation of another.

This is a big issue with Vampire because it enforces the requirement that the Python code module have the same basename as the resource it relates to. If your web application was spread across multiple directories and all those directories supplied an "index.py" file for delivering up an "index.html" for that directory, you will end up with exactly this scenario. The module importing system in mod_python also has a few bugs which further complicate this, whereby a resource in a root directory may become inaccessible once a resource of the same name in a subdirectory is acecssed.

In order to avoid the problems described above, Vampire implements it own module importing system which keeps separated modules of the same name located in different directories. Further, automatic reloading will be done based on any change to the modification time of the Python code module. When using the module importing system supplied with mod_python, a Python code module will only be reimported if its modification date is newer than before. This can be a problem where files are restored from a backup and the modification date is then older than before.

Where required, the Vampire module importing system can be used explicitly. A major difference though when compared to the standard mod_python module importing system is that the directory where the module is to be loaded from must always be stated explicitly. That is, the Python search path is not used to find a module. This ensures that one is always sure exactly which module will be loaded and that changes to the Python search path do not causes problems.

import vampire

config = vampire.loadConfig(__req__,".vampire")
directory = config.get("Modules","common")
handlers = vampire.importModule("default-handlers",directory,__req__)

def handler_html(req):
  return handlers.handler_html(req)

The Python "import" directive could in many circumstances also have been used to achieve the same effect, but the problem with using "import" is that if the imported module is changed, the content handler will never be reloaded and it is generally necessary to restart Apache to pick up the change. When using the Vampire module importing system, it will detect that changes have been made in the child modules imported by a content handler using Vampire and will automatically force a reload of the content handler and the child module.

Through Vampire being able to do this, any restarting of Apache is avoided in most circumstances, nor is there any need to be explicitly touching all files to update their modification times as is done by some users to trick mod_python into reloading handlers when only modules they have imported have changed.

Publisher Implementation

When using handler functions, each resource identified by a URL is typically linked directly to a distinct file stored within the filesystem. If it is desired that a component of the URL identifies a virtual resource such as a Python data structure or data stored in a database, it becomes necessary to manually interpret any additional path info detailed in the URL.

The mod_python.publisher handler provides one automated means of interpreting this additional path information by mapping requests to either Python data objects, methods of those data objects or functions. Due to the manner in which the mod_python module importing mechanism is implemented and the way that mod_python.publisher makes use of it, certain constraints are however placed on the use of mod_python.publisher. This is currently exacerbated by a number of bugs which are also present in the current version of mod_python.publisher and which are documented on the mod_python bug list.

As a solution to these problems, Vampire provides its own implementation of the publisher handler which should be able to be used in most cases as a drop in replacement for mod_python.publisher. To make use of this handler the "PythonHandler" directive should be set to reference the Vampire implementation rather than the mod_python.publisher version.

PythonHandler vampire::publisher

Because Vampire provides an extended mechanism for interpreting form parameters, it may be necessary to modify the names of form parameters where a conflict occurs. Alternatively, this extended mechanism can be disabled. This can be done by setting the "VampireStructuredForms" option to "Off".

PythonOption VampireStructuredForms Off

To make best use of Vampire, any direct use of the Python "import" statement or the "apache.import_module()" function to import modules which are a part of the web application, should be changed to use the Vampire import mechanism instead. This will provide a more reliable way of having modules reloaded when changes are made and lift the various constraints imposed by using the standard Python module import system.

Mixing Publisher and Handler

By using mod_python.publisher or the Vampire equivalent, one gains the ability to have URLs map to distinct data or functions within a single Python code file. This can be beneficial in itself, but at the same time one has to sacrifice other abilities deriving from using basic content handlers to get it.

Vampire avoids any loss of functionality by allowing you to selectively apply publisher style URL mapping and response generation within the context of a basic content handler. This is achieved by creating special handler objects which apply publisher style semantics to selected data and functions.

import vampire

class _Object:

  def __init__(self):
    self.value1 = "value1"

  def method1(self):
    return "method1()"

  def method2(self,req):
    req.content_type = "text/plain"
    req.send_http_header()
    req.write("method2()")

  def __call__(self):
    return "__call__()"

_object = _Object()

handler = vampire.Publisher(_object)

def handler_txt(req):
  req.content_type = "text/plain"
  req.send_http_header()
  req.write("handler_txt()")

When this handler is accessed with no extension specified, the "__call__()" method will be executed. If a ".txt" extension is used then "handler_txt()" is called. Finally, if no extension is specified but additional path information is supplied, this will be mapped against the object hierarchy as appropriate to select "value1", "method1()" or "method2()" if possible.

Note that in Vampire, if a basic content handler doesn't explicitly return a value, it is interpreted as if "apache.OK" had been returned. If a method mapped using publisher neither writes an explicit response to the request object, nor returns a value, it is interpreted as an empty response. This behaviour is different on both counts when compared to basic content handlers in mod_python and published methods in mod_python.publisher, both of which respond with a HTTP internal server error.

Processing Extra Path Info

When using a basic content handler, any part of the URL which extends beyond that which matches the file containing the handler is available for use by the handler as part of a mechanism to identify a non file based resource. It is this information which is used by mod_python.publisher to identify a function to invoke.

Because this information is used in this way, it is not possible when using mod_python.publisher for a function itself to make use of such additional path information. Vampire removes this limitation through use of a special class which can be used to wrap any function which you would like to be able to receive and process additional path information beyond that which is used to identify the actual function.

def _method1(path):
  return "method1()",path

method1 = vampire.PathInfo(_method1)

def _method2(xpath):
  return "method2()",xpath

method2 = vampire.PathInfo(_method2,path="xpath")

def _method3(req,path,name):
  req.content_type = "text/plain"
  req.send_http_header()
  req.write(str("method3()",path,name))

method3 = vampire.PathInfo(_method3)

By default any additional path information will be passed to the function through the "path" parameter. This may be overridden when constructing the wrapper if required. The function may still accept form parameters, however any supplied form parameter of the same name as the function parameter through which the path information will be passed will be ignored.

The additional path information can contain any number of virtual directory nodes. It is still up to the function to split the path on "/" and raise an error if an incorrect number is supplied corresponding to the operation being performed. If the number of nodes is fixed or within a certain range and each has a set meaning, one can instead have each mapped to separate parameters of the function using a different class wrapper.

def _method1(year,month,day="0"):
  return "_method1",year,month,day

method1 = vampire.PathArgs(_method1)

def _method2(req,year,mon,day="0",*varargs):
  req.content_type = "text/plain"
  req.send_http_header()
  req.write(str("_method2",year,mon,day,varargs))

method2 = vampire.PathArgs(_method2)

Because each component of the path is mapped to a distinct function parameter, it is not possible for the function to also accept form parameters. The function may still accept the request object, but the parameter must be the first parameter of the function.

Using Objects As Handlers

The handler being used to service a request is not restricted to being a function. Instead, an instance of a class may also be used. For this to work however, the class must have defined the "__call__()" method. As with any handler, the method can be defined to accept the "req" object and any other form parameters as appropriate.

class Handler:

  def __call__(self,req,message=""):
    if not message: message = "Give Blood!"
    req.content_type = "text/plain"
    req.send_http_header()
    req.write(message)
    return apache.OK

handler_txt = Handler()

The benefit of embedding the handler into a class is that one could encapsulate common code used by different handlers into a base class. One could for example create a servlet like system similar to the mpservlets extension for mod_python.

As written however, the same instance of the class would be used for every request. If the instance used to handle a request cannot be reused, or if a multithreaded MPM was being used and multiple requests at the same time cannot be handled, a single instance would not be sufficient. In this case a mechanism is needed such that a distinct instance of the class is created to handle each request.

One solution to this problem is to code an actual handler which creates an instance of the class on demand.

def handler_txt(req,message=""):
  return Handler()(req,message)

Using this approach however, the handler would need to mirror the set of arguments and default values and explicitly pass them through when making the call against the object instance. To avoid this sort of duplication, Vampire provides a wrapper class which will perform the creation of an instance of the class and automatically supply the "req" object and any form parameters for you as appropriate.

handler_txt = vampire.Instance(Handler)

In addition to supplying these parameters when applying the call to the object instance, if the constructor specifies the "req" argument or any form parameters, they will also be supplied at the point of creation of the instance as well.

class Handler:

  def __init__(self,req):
    self.req = req

  def __call__(self,message=""):
    if not message: message = "Give Blood!"
    self.req.content_type = "text/plain"
    self.req.send_http_header()
    self.req.write(message)
    return apache.OK

handler_txt = vampire.Instance(Handler)

Especially in the case of the request object, this would allow any base classes to be initialised with data specific to the request.

Note that this wrapper class may be used with published methods as well as with basic content handlers.

Implementing Web Services

Interacting with an application through web pages alone may be possible, but a more rich style of interaction is often required. One means of achieving this is by implementing a web service using the XML-RPC protocol. This permits the exporting of functions by an application such that they are callable using a remote procedure call over the HTTP protocol. These functions could be the same functions which are used in populating data in the web pages.

class _Object:

  def method1(self):
    return "method1"

  def method2(self,req):
    return "method2"

  def method3(self,req,arg):
    return "method3"

_object = _Object()

handler = vampire.Service(_object)

The XML-RPC service wrapper makes use of the "xmlrpclib" module. As such, any types supported by the "xmlrpclib" module can be used. Faults can also be raised using the "xmlrpclib.Fault" class. Where the first parameter of a function is the "req" parameter, it will be passed the mod_python request object.