Following details changes made in various versions of Vampire which have been made available.
Warning
The current officially released version of Vampire is 1.8. Features described here for versions later than that are only available by checking out the development copy of the source code from the Vampire source code repository directly.
Special meaning of default section, ie., "DEFAULT", as implemented by original Python ConfigParser class wasn't being honoured by Vampire configuration system. This has now been fixed, and variables which need to be referenced through interpolation in multiple sections, can be placed in the "DEFAULT" section.
Added mechanism whereby Python import mechanism is partly overridden such that although the standard Python "import" statement is used, under the covers, the Vampire import mechanism is used to load a module.
Note though that this support only extends as far as file based modules and not to full blown packages. Python doesn't seem to be able to support packages which live totally outside of sys.modules.
The result of this feature is that file based module imports at global scope in a module containing a handler, instead of being written as:
import os
import vampire
directory = os.path.split(__file__)[0]
mymodule = vampire.importModule("mymodule",directory,__req__)
can be written in more traditional form of:
import mymodule
Because the Vampire import mechanism is actually being used, a change in the imported module will automatically result in the handler module being reimported as necessary when a request arrives, even though the handler module itself wasn't changed.
To enable this feature it is necessary to enable it within the Apache configuration by using the "PythonOption" directive to set the "VampireImportHooks" option to "On":
PythonOption VampireImportHooks On
By default, only the directory the handler module is contained within will be searched for the target module. If the target module is found, then "vampire.importModule()" will be used to load it. If the target module cannot be found, the standard Python import mechanism will be used which would result in a search within directories defined by "sys.path".
Additional directories to search before falling back to using the standard Python import mechanism, can be specified using the Vampire configuration file. This should be done by specifying a "path" setting within the "Modules" section:
[Modules] modules = %(__config_root__)s/modules templates = %(__config_root__)s/templates path = %(modules)s:%(templates)s
Multiple directories to search should be seperated by ":". The string interpolation feature of the configuration file can be used to refer to other value settings within the same section.
Note that the above mechanism will not currently come into play for imports which are done within an executing handler, only those defined at global scope. Explicit calls to "vampire.importModule()" would still need to be used within a executing handler.
The above mechanism should be used if using Cheetah templates and automatic reloading of top level templates is required when only the template they extend has been changed. This is because Cheetah explicitly compiles into code the use of "import" and it cannot be replaced with calls to "vampire.importModule()".
If this feature is used, it is highly recommended that "sys.path" be fixed so as not to include any directories within the document tree. This can be done by using:
PythonPath 'sys.path'
in the Apache configuration for the top level directory where the Python handlers for mod_python are setup.
Add "vampire.Instance" class. This can be used as a wrapper for an old or new style class such that each time the wrapper is accessed, a new instance of the wrapped class is created to handle the request. The class must be a callable object, ie., define "__call__()", as once the instance has been created, the object itself will be called.
import vampire
import time
class Old:
def __init__(self):
self.__time = time.time()
def __call__(self,req,arg1=None,arg2=None):
return self.__time,"Old.__call__()",arg1,arg2
old = vampire.Instance(Old)
class New(object):
def __init__(self):
self.__time = time.time()
def __call__(self,req,arg1=None,arg2=None):
return self.__time,"New.__call__()",arg1,arg2
new = vampire.Instance(New)
Either the "__init__()" method, if defined, or the "__call__()" method may accept form parameters or the "req" object as necessary.
This wrapper class should be usable within "vampire::publisher", "vampire.Publisher()" and "vampire.Handler()" or as a basic content handler.
In the same manner that "vampire.Instance()" can be used to trigger creation of an instance of a class to handle a request in the style of a handler, the "vampire.Publisher()" and "vampire.Handler()" classes have been enhanced to allow a class type to be wrapped, with an instance of the class being created for each request.
Made available "vampire.handlerArguments()" and "vampire.executeHandler()" functions. These provide means of determining set of arguments that a callable object accepts and a means of executing a callable object such that any form parameters supplied with the request are passed when appropriate via the arguments the callable object accepts.
When using "vampire.importModule()", if the path argument is explicitly set to "None", the directory in which the module will be searched for will be that which the calling module is located in. This means that instead of writing:
import os directory = os.path.dirname(__file__) xxx = vampire.importModule,"xxx",directory,__req__)
it is possible to use:
xxx = vampire.importModule,"xxx",None,__req__)
Added mechanism to turn off automatic reloading of modules by the Vampire module importing system. Ideally, turning off of module reloading should only be done from within a module which is imported using PythonImport. This will gaurantee that module reloading is off for the whole life of the interpreter in question. That is, once module reloading is turned off, it cannot be turned back on. To have it turned back on would entail disabling the code which turned it off and restarting Apache.
The code which should be used to turn off module loading from a module imported using PythonImport is:
import vampire vampire.ModuleCache().freezeCache()
This is done in this way because the alternative as used by mod_python itself, in the form of the option "PythonAutoReload" is unreliable because it can can be set to different values in different parts of the document tree. Further, explicit use of the "apache.import_module()" function doesn't consult this option. Instead, it is expected that the caller supply through a parameter whether module reloading is enabled or not.
Thus for the module loading system in mod_python, a request to load a module via a request through one part of the document tree may cause a automatic reload whereas through another part of the document tree it may not. This can cause inconsistencies as far as expectations as to what version of a module is loaded. For that reason, Vampire implements a mechanism whereby it is on everywhere, or not at all. If different parts of the document tree need to be on or off then distinct interpreters will need to be used to keep everything separate.
Similar to being able to disable reloading of modules by the module cache, option provided to disable reloading of page template files in the HTMLTemplate loading and caching mechanism. Again, this is preferably triggered from a module imported using PythonImport mechanism. The code which would be used is:
import vampire vampire.TemplateCache().freezeCache()
Fixed bug in vampire.Publisher(), vampire.Handler() and vampire.Service() which prevented them from being used from a handler not being dispatched by Vampire, ie., a standard mod_python handler. This previously worked but was probably broken in version 1.6.
The vampire.serviceRequest() now always return None and not apache.OK. Thus any code calling it may now have to return apache.OK explicitly itself from the handler the function is used in.
The "__baseurl_abs__" and "__baseurl_rel__" configuration defaults were wrongly set when used in handler which was supplied additional path information in the URL beyond that required to identify the handler. This would be the case when using wrapper class "vampire.Handler()", "vampire.Publisher()" and "vampire.Service()". Also would have been a problem when using "vampire::publisher".
Login handler hooks were not working properly with wrapper classes such as "vampire.Handler()" and "vampire.Publisher()". Problem was that a login handler defined at level outside of the wrapper class could be applied twice. If the login handler create a session object, this would result in handler deadlocking on itself when login handler was called a second time.
Fixed bug in module loading and caching system which could result in deadlock occuring under heavy thread load when imported modules did further subimports.
Fixed bug where getting Apache to perform Digest authentication would cause a bad request error to be returned by Vampire. This was caused by Vampire decoding Authorization header even when no authentication or access hooks defined. Now delay decoding Authorization header till point that it has been determined that it needs to actually be done. One can still only use __auth__ and __access__ with Basic authentication but at least now you can have Digest authentication performed by Apache and Vampire will not get in the way.
The special "__req__" variable which is explicitly defined in the global variables of a module for the life of performing the module import and then removed, is now the full "req" object pertaining to the request which has caused the module import. Previously it was a cut down version of the original request object. Problem is that although is was possible to call "__req__.server.register_cleanup()", it required the full original request object as argument and the cutdown version wasn't good enough. The "__req__" object should not be abused, ie., one shouldn't be doing anything with it during a module import that would affect the actual response being sent.
In the "Handlers" section of the Vampire configuration, is now possible to say "defaults" and refer to a Python code file in which Vampire should look for all handlers. A handler specified explicitly in the Vampire configuration takes precedence over one existing in the code file referenced by the "defaults" setting:
[Handlers] defaults = %(__config_root__)s/_defaults.py handler_html = %(__config_root__)s/_handler_html.py
If the "defaults" setting is specified and you want to apply HTTP basic authentication across a whole directory, the "__auth_realm__", "__auth__" and "__access__" hooks can be specified in the code file which has been referenced.
Added means of overriding the basic authentication mechanism and instead use an alternative login mechanism such as sessions and form based login. The handler which needs to be defined for this is "loginhandler()":
[Handlers] loginhandler = %(__config_root__)s/login.py
The "loginhandler()" may also appear in code file specified by "defaults".
The global handler may be overridden within a specific file, object or function scope by defining a "__login__()" method. This allows the login manager to be disabled completely for the login page itself or other resources where login or session management wouldn't be required.
Enhanced implementation dealing with lazy evaluation of form parameters. What this means is that if there is no need to actually decode form parameters then it will not be done. This avoids problems whereby a handler which wasn't interested in the form parameters wants to use a third party templating package that expects to have first go at parsing the form parameters. Ie., a package that doesn't look to see if "req.form" has already been set.
Note that in order to be compatible with mod_python.publisher, the "vampire::publisher" equivalent doesn't perform lazy evaluation of form parameters but always parses them even if not required. This ensures that code written for mod_python.publisher that expects "req.form" to be set will still work. If you you migrate to using "vampire.Publisher()" however, in that case lazy evaluation will be done.
If a handler doesn't accept form parameters as arguments, but internal to the handler you want to force form parsing and access the resultant form parameters, you can use:
args = vampire.processForm(req)
The result will be a dictionary containing the form parameters. A side effect of this will be that "req.form" will be set. Note that the Vampire extension for structured form parameter naming will be applied unless disabled.
Forgot to add "req" argument to "vampire.importModule()". Did exist on method as it appeared as member function of cache object.
When doing redirection to directory index URL, any form parameters were not being appended to the target URL for the redirection.
Bogus entry was left in module cache if module failed to load the first time. This could result in not found error or other issues on next access of module.
The parameter of a content handler which accepts the request object must now be called "req". The parameter can however be placed anywhere and does not have to be the first parameter. The naming conventions and treatment of form parameters is thus similar to publisher style methods.
If "None" is return explicitly or implicitly by a content handler it is taken to have the same meaning as returning "apache.OK".
Added equivalent of mod_python.publisher. This is enabled by setting PythonHandler to "vampire::publisher". This implementation of publisher style methods fixes a lot of mod_python.publisher bugs and allows traversal into new style classes where appropriate.
You should not use "import" or "apache.import_module()" to import application modules when using "vampire::publisher", instead use the "vampire.importModule()" method. Obviously you will then need to deal with the differences in the Vampire module loading and caching system.
Rather than use "vampire::publisher" exclusively, publisher style functions can be integrated into basic content handler mechanism. This is done using the "vampire.Publisher()" class. A single object such as a method or basic data type can be exported as:
page = "<html><body>...</body></html>" handler = vampire.Publisher(page)
If multiple objects and methods need to be exported, they will need to be wrapped in a class:
class Exported:
def __call__(self):
return "Exported.__call__()"
def method1(self):
return "Exported.method1()"
def method2(self):
return "Exported.method2()"
handler = vampire.Publisher(Exported())
There is an equivalent class called "vampire.Handler()" which can be used if rather than publisher style functions, normal content handler style functions are preferred. In the latter, basic Python types can not however be publisher like they can with publisher.
Note that both these classes can actually be used from outside of Vampire from a standard content handler.
Advanced form parameter processing, ie., conversion into structs and lists can be disabled by specifying:
PythonOption VampireStructuredForms Off
This should be used if converting existing publisher code to the vampire::publisher handler to avoid form parameters being interpreted strangely.
Added "vampire.PathInfo()". This can be used to wrap methods when using "vampire::publisher", "vampire.Publisher()" or "vampire.Handler()". The effect of wrapping the method is that any additional path beyond that that would have matched the method will be accumulated as additional path information and passed to the method in a single parameter in URL path form. Both global methods and methods of classes can be wrapped. By default the additional path information will be passed through the "path" parameter. Alternatively the name of the parameter to use can be specified when constructing the wrapper:
# For "/class1/method1/1/2/3", path will be set to "/1/2/3".
# For "/class1/method2/1/2/3", xpath will be set to "/1/2/3".
class Class:
def __init__(self):
self.method1 = vampire.PathInfo(self._method1)
self.method2 = vampire.PathInfo(self._method2,"xpath")
def _method1(self,path):
return "Class._method1",path
def _method2(self,req,xpath):
return "Class._method2",xpath
class1 = Class()
# For "/method1/1/2/3", path will be set to "/1/2/3".
def _method1(path):
return "method1()",path
method1 = vampire.PathInfo(_method1)
# For "/method2/1/2/3", path will be set to "/1/2/3".
def _method2(req,path):
return "_method2()",path
method2 = vampire.PathInfo(_method2)
Any wrapped method can still accept the "req" object or any other form parameters provided with the request. The path information when passed to the wrapped method will override any form parameter of the same name. Additional form parameters can still define default values.
Added "vampire.PathArgs()". This can be used to wrap methods when using "vampire::publisher", "vampire.Publisher()" or "vampire.Handler()". The effect of wrapping the method is that any additional path beyond that that would have matched the method will be accumulated as additional path information and passed to the method with each component passed as separate parameters. Both global methods and methods of classes can be wrapped:
# For "/method1/2005/12", day="2005", mon="12", day="0". # For "/method1/2005/12/25", day="2005", mon="12", day="25". def _method1(year,mon,day="0"): return "_method1",year,mon,day method1 = vampire.PathArgs(_method1) # For "/method2/2005/12", day="2005", mon="12", day="0". # For "/method2/2005/12/25", day="2005", mon="12", day="25". def _method2(req,year,mon,day="0"): return "_method2",year,mon,day method2 = vampire.PathArgs(_method2) # For "/method3/2005/12", day="2005", mon="12", day="0", va=(). # For "/method3/2005/12/25", day="2005", mon="12", day="25", va=(). # For "/method3/2005/12/25/0", day="2005", mon="12", day="25", va=(0,). def _method3(req,year,mon,day="0",*va): return "_method3",year,mon,day,va method3 = vampire.PathArgs(_method3)
Because each component of the additional path information is passed as a separate parameter, there can only be as much additional path information as the parameters allow. Note that any form parameters will be ignored, but if the first parameter is called "req" it will still be passed the request object. Default parameters can be specified as can a varargs parameter.
Added "vampire.Service()". This allows one to wrap an object and exports its methods and data as an XML-RPC service. Traversal works similar to publisher and "vampire.Publisher()". Note that data will be exported and accessible as if it were wrapped by an implicit function. In this case the number of arguments supplied is irrelevant. If you don't want data to be exposed, prefix the name of the variable with an underscore:
import vampire
class Object1:
def method1(self):
return "Object1.method1()"
class Object2:
def __init__(self):
self.object1 = Object1()
self.string1 = "Object2.string1"
def method1(self):
return "Object2.method1()"
def method2(self,req):
return "Object2.method2()",req.uri
def method3(self,data):
return "Object3.method3()",data
def method4(self,req,data):
return "Object4.method4()",req.uri,data
_object2 = Object2()
handler = vampire.Service(_object2)
If the first parameter of a function is "req", it will be passed the special mod_python request object with the XML-RPC parameters passed as the remaining parameters.
The authentication code used from mod_python.publisher has been replaced. The functionality is in general the same, but a number of bugs in the mod_python.publisher code have been fixed. Support for defining authentication and access constraints within a member of class has also been added. The other big difference is that an auth realm must be defined. In mod_python.publisher it would default to "unknown" if not defined. When using Apache, it will raise a 500 error if the auth realm isn't defined, so Vampire has been made to behave like Apache instead.
Configuration mechanism no longer squashes key names to lower case as is the default for ConfigParser module supplied with Python. Instead, case sensitivity is preserved.
Configuration mechanism will cache config in the request object. This avoids multiple searches if distinct bits of code executing within same context of one request use the same config. Also ensures that all code within that request will always use the same config files and not a slightly modified version which gets reloaded part way through a request.
Vampire can now be used to intercept handlers other than that for the PythonHandler directive and it will be directed to a specific module indicated within the handlers section of the Vampire config file. Thus, if in your ".htaccess" file you might have:
PythonAccessHandler vampire
In your ".vampire" config file you might then have:
[Handlers] accesshandler = %(__config_root__)s/modules/access-handler.py
The handler function in the specified file must be the default for the particular directive, it cannot be modified like it can using "::" in the ".htaccess" file.
Fixed bug which caused config search to fail when trying to obtain config in a handler phase prior to the phase corresponding to the PythonHandler directive. Would be triggered when request was against a directory.
Fixed bug whereby on Python 2.3 and latter where items() exists on the ConfigParser, that it wasn't providing defaults correctly when doing string interpolation, thus causing call to fail if any value in config referenced any of the default values using interpolation syntax.
If the __clone__() function of module fails or isn't callable, module will simply be thrown away and no attempt to try and preserve data from old module. An attempt will, as last ditch effort, be made to call __purge__() as indicator to module that it is being purged from the system.
The XmlRpcHandler class has been discarded completely. Instead, a much more low level request handler is provided whose job is only to parse the inbound request and then format the response. The actual execution of the request is passed off to a supplied callback. This callback must work out how to then map the request to a specific method.
This means that in the first instance the support for XML-RPC requests is not as useful and more work is required to use it, but it opens things up so that more powerful things can be done. For example, a means of optionally having the request object also passed through to methods could also be implemented. For example:
import vampire
import types
config = vampire.loadConfig(__req__,".vampire")
modules = config.get("Modules","common")
module = vampire.importModule("python-utils",modules)
def handler(req):
def _callback(req,name,params):
if name[0:1] != "_":
if module.__dict__.has_key(name):
method = module.__dict__[name]
if type(method) == types.FunctionType:
if len(method.func_code.co_varnames) != 0:
if method.func_code.co_varnames[0] == "req":
params = list(params)
params.insert(0,req)
return method(*params)
raise Exception("Method Unavailable")
return vampire.serviceRequest(req,_callback)
Special conventions can now be used in form arguments to denote data which should be translated to dictionaries and list. Specifically, keys (variable names) can have subkeys, with a . and can be numbered with -, like a.b-3=something means that the value a is a dictionary with a key b, and b is a list, the third(-ish) element with the value something. Numbers are used to sort, missing numbers are ignored.
The code to achieve this bit of magic courtesy of some code from the FormEncode/Validator package made available by Ian Bicking.
A cut down "req" object is now available when the Python code file for a handler is being imported. This will be stored in the module as "__req__". Once importing of the module has been completed it will be deleted out of the module. The cut down "req" object allows access to PythonOption variables using get_options(), other mod_python settings using get_config(), as well as attributes such as "interpreter" etc. The "uri" and "filename" are also supplied such that the cut down "req" object can be used on configuration file lookup during module importing. This is shown in practice in XML-RPC example above.
The Vampire module importing and caching mechanism will now log messages about module imports/reimports when PythonDebug option has been enabled. This will though only work for top level handler imports or where the "req" or "__req__" object is provided as appropriate argument to the "importModule()" method.
Support for the older predefined value names in configuration mechanism have been dropped. Make sure you use the newer names:
__root__ --> __config_root__ __file__ --> __config_file__ __mtime__ --> __config_mtime__ __base__ --> __baseurl_abs__
Include a workaround for a bug which is present in mod_python when running on Win32 platform which would have caused search algorithm for config file to fail. Problem with mod_python is that it errornously adds a trailing '' to the name of the directory where the original PythonHandler directive was declared, when it should have been '/' and should only have been added when a trailing '/' wasn't already present. The remainder of the path as supplied by Apache uses the POSIX path separator and this was being depended upon, but mod_python was breaking that. A separate patch is not provided because a workaround is possible. Bug was posted on the mod_python mailing list, although no one has acknowledged it is a valid problem.
The names of the special predefined variables defined within the configuration system and which are available for string interpolation in configuration have changed. New names are as follows:
__root__ --> __config_root__ __file__ --> __config_file__ __mtime__ --> __config_mtime__ __base__ --> __baseurl_abs__
Although these are officially the new names, some tricks are done to ensure the old names still work for now. The names are also no longer added to the DEFAULT section of the config object, but are made available for string interpolation in another way. They thus will not appear in any section of the config object if names defined in a section are iterated over.
The following extra special variables have also now been added:
__handler_root__ --> Where PythonHandler directive was defined. __baseurl_rel__ --> Base URL relative to the current request.
Although the names are no longer added into the DEFAULT section of the config file, they can still be accessed using the "defaults()" method of the config object returned by "loadConfig()".
Any variables defined in a .htaccess file or httpd.conf file using the PythonOption directive are now automatically loaded into the config in a way that they are also available for string interpolation without the need to explicitly specify them using the "vars" variable when getting the value of a configuration option.
Variables defined in the .htaccess file using the PythonOption directive will themselves have string interpolation performed upon them when used via the configuration mechanism. Thus, one could actually define:
PythonOption LayoutRoot %(__handler_root__)s
Then in the configuration file, one can use:
[Handlers] handler_html = %(LayoutRoot)s/layout/handler_html.py handler_pdf = %(LayoutRoot)s/layout/handler_pdf.py
In this examples, this would be equivalent to having used:
[Handlers] handler_html = %(__handler_root__)s/layout/handler_html.py handler_pdf = %(__handler_root__)s/layout/handler_pdf.py
Doing it this way though, means that the .htaccess file can be used to set explicit values, or reference back to the dynamic values generated when the search for the config file was performed.
When turning on default content handlers using the directive:
PythonOption VampireDefaultHandlers On
it is now possible to override the default name of the configuration file used to find the default handlers and what section in that configuration file is consulted. Thus:
PythonOption VampireHandlersConfig .vampire PythonOption VampireHandlersSection Handlers
The values ".vampire" and "Handlers" represent the inbuilt defaults, but by setting these, they can be overridden.
Vampire has been updated to ensure that it works correctly with any multithreaded MPM under Apache 2.0.
The module reloading mechanism has been changed such that when a module is reloaded, it isn't reloaded on top of the existing version of the module. This is necessary to avoid problems when using a multithreaded MPM under Apache 2.0.
If the fact that global data, not explicitly set when a module is first loaded, was being preserved across a reload was being relied upon, you will need to change your code. Specifically, you should add an additional special method to the code file called __clone__().
The version of this method in the existing module will be called just prior to the module being reloaded. The argument to the method will be an empty module. The method should copy into the new module, any data present in the existing one that needs to be preserved.
If necessary, the method should ensure it acquires any locks that pertain to the data being copied, while copying the data. The actual locks should also be copied across to the new module.
Note that data values which are being preserved, shouldn't be simple types, such as integers or strings as these will not be correctly shared between the two module instances while both still exist. Simple data should strictly be placed in dictionaries or inside a class object.
No longer wrap a form field which identifies an uploaded file in an instance of the class called File. This is no longer done in more recent versions of mod_python.publisher, so try and maintain some compatibility even if don't know what the consequences of doing this are. Maybe it should be done if using older mod_python but not newer.
When automatically decoding form values and passing as arguments to a content handler, no longer supply form values the content handler was not expecting. If content handler defines keyword argument list parameter, pass all form values.
It is now mandatory to have the extension type be a part of the handler method name. You only need to define __handle__ if you want to limit the exported list of handlers to a subset of those actually defined in the one file.
No longer provide the vampire.table module for processing of csv data. It is preferable that content handlers use Python "csv" module direct as it gives better control of input/output parameters.
Basic authentication mechanism now supported for content handlers. This is handled in the same way as authentication and access controls are handled when using the mod_python.publisher module. The authentication method from that module is actually invoked so it better work the same.
If a content handler accepts more than one argument, it is now assumed that it is designed to process a form request. As such, any GET/POST form parameters are decoded automatically using FieldStorage class from mod_python.util in the style of mod_python.publisher and where they match parameters of the content handler are passed as arguments.
Note that the first argument to the content handler will always be the request handler no matter what that argument is called. The method implementation should be the same as any other content handler. That is, there is no change in semantics of the returned value from the function as is the case with mod_python.publisher.
Provide vampire.xmlrpc module to assist in making a content handler service XML-RPC requests. This is derived from the SimpleXMLRPCServer class which is supplied with Python so follow its interface but start out with an vampire.XmlRpcHandler instance and call handle_request() with the "req" object.
Provide vampire.config module for implementing a search mechanism for configuration files. The specified config file will be searched for starting in the directory the content handler is located in, back up the directory hierarchy, stopping at the directory where the PythonHandler directive was applied.
The name of the config file should be specified at the point the search request is made. The format of the config object is that as defined by the Python ConfigParser module. Special variables called __root__, __file__, __base__ and __mtime__ are defined in the DEFAULT section and can be used in interpolation of values. These default variables indicate the root directory the config file is located in, the name of the config file, the path component of the URL which addresses the root directory and the modification time of the config file respectively.
This config mechanism can be used in conjunction with the PythonOption mechanism used to set variables in a .htaccess or httpd.conf file. Specifically, pass the result of req.get_options() as the "vars" parameter to the get() method when asking for the value for a specific setting. The options set by PythonOption mechanism will therefore be available for use in string interpolation when the value is being expanded.
Added a mechanism of defining default content handlers to be used for all requests of a specific file type, defined by extension, which weren't able to be handled by a dedicated content handler. This might be used for example to cascade requests onto the mod_python.psp or mod_python.servlet content handlers or process all HTML files through a templating mechanism to fill in fixed page layout information such as page indexes.
Added VampireDirectoryIndex option such that .htaccess or httpd.conf file can be used to define the virtual file to which a request should be redirected when a request is made against a directory. It seems that when using Apache 1.3 it will do this okay for the root directory where Vampire is being used, but not for subdirectories, when this option isn't used, but with Apache 2.0, it does work without this option in either situation.