[medusa] script_handler: possible optimization?

jam jam@n...
Fri, 3 Sep 1999 16:18:17 -0400


greetings,

I'm working on (the forth or fifth generation of) my website on quark, and
I'm using the latest-and-greatest medusa framework to do so, because I
noticed this great handler called persistent_script_handler that I *really*
wanted to use ;)

I have managed to get things up and running at least to the point that I
don't have any odd problems (like the server exiting immediately because I
had imported two different instances of the asyncore module). the next phase
of this is that I need to somehow scale the size of the site up by a factor
of several. I have two modules so far, and my primary design concern at the
moment is 'how do I hang other modules from different places in the
directory tree?'.. for example:

/gfd/index.html 
- handled by 'main.py' 
- also might handle things like '/gfd/about.html' or '/gfd/resume.html'
/gfd/links/index.html
- the 'match' routine in the 'links' module will need to know where it
hangs on the 'tree' in order to determine if it should handle the
request or not. this is where things get complicated, especially if
the site uses a lot of these modules. one thought I had was to
simplify things by *not* wrapping lots of different functions into
one module by splitting each 'page' into it's own module ('about.py'
handles '/gfd/about.html', 'resume.py' would handle '/gfd/resume.*',
etc.) but that *also* seems like it would get complicated fast.

I do have a small contribution to add to the cause, which anyone may use as
they see fit: I made a new version of the persistent_script_handler class
(called 'gfd_script_handler', below) that calls a method called 'match' in
each module to let *it* tell the framework if it can deal with the request
or not. I also changed the add_module routine to make it simpler to make the
'autoreload' functionality a possibility in the future (it's pretty easy
once you start using __import__ and the ihooks module).

comments, suggestions, strange looks, etc, accepted.

-- cut here --
#!/usr/bin/env python

#
# this code uses medusa, available from <http://www.nightmare.com/medusa/>
#

import sys
import asyncore

from cStringIO import StringIO

from medusa import status_handler, default_handler, filesys, http_server, counter
from medusa import script_handler, resolver, logger

import config

class gfd_script_handler:
def __init__(self):
self.exceptions = counter.counter()
self.modules = {}

def add_module(self, key):
moduleref = __import__(key)
self.modules[key] = moduleref

def del_module(self, key):
del self.modules[key]

def match(self, request):
for m in self.modules.values():
if hasattr(m, "match"):
if m.match(request) == 1:
print "found match", id(m)
request.module = m
return 1

return 0

def handle_request(self, request):
if request.command in ( "put", "post"):
cl = request.get_header("content-length")
length = int(cl)
if not cl:
request.error(411)
else:
collector(self, length, request)
else:
self.continue_request(request, StringIO())

def continue_request(self, request, input_data):
temp_files = input_data, StringIO(), StringIO()
old_files = sys.stdin, sys.stdout, sys.stderr

try:
sys.stdin, sys.stdout, sys.stderr = temp_files
request["content-type"] = "text/html"
try:
request.module.main(request)
request.reply_code = 200
except:
request.reply_code = 500
self.exceptions.increment()
finally:
sys.stdin, sys.stdout, sys.stderr = old_files

i, o, e = temp_files

if request.reply_code != 200:
s = e.getvalue()
else:
s = o.getvalue()

request["content-length"] = len(s)
request.push(s)
request.done()

if __name__ == "__main__":

sys.path.append("/home/jam/projects/thpwact/modules/")

rs = resolver.resolver("neutron")

lg = logger.file_logger(sys.stdout)

hs = http_server.http_server("toast", 8080, rs, lg)

# 
# the 'default_handler' tries to cover requests that aren't
# handled by the script_handler (like /images)
#
fs = filesys.os_filesystem(config.SOFTROOT)
dh = default_handler.default_handler(fs)
hs.install_handler(dh)
gsh = gfd_script_handler()
gsh.add_module("about")
gsh.add_module("main")
hs.install_handler(gsh)

sh = status_handler.status_extension( (hs, lg, gsh) )
hs.install_handler(sh)
asyncore.loop()
-- cut here --

then, as an example, here's my 'about.py' module, which uses
DocumentTemplate and a few things from the 'config' module (which I'm not
including lest I get shot for such a huge post ;)):

-- cut here --
_RCSID = "$Id: $"

import os

import common
import config

def match(request):
path, params, query, fragment = request.split_uri()
parts = os.path.split(path)
##	print "about: ", parts
if len(parts) > 1 and parts[1] == "about.html":
return 1
else:
return 0

def main(request):
print common.standard_header(title="about this site")
print common.HTMLFile(config.DTMLROOT + "about.dtml")()
print common.standard_footer()

if __name__ == "__main__":
# for testing
main(None)
-- cut here --

regards,
J
-- 
|| visit gfd <http://quark.newimage.com/> 
|| psa member #293 <http://www.python.org/> 
|| New Image Systems & Services, Inc. <http://www.newimage.com/>