bloat code with even more layers of indirection, split off the filesystem-based stuff into a separate lib.filesystem package (next, move it to sites/www.qmsk.net)
--- a/index.cgi Sat Feb 07 06:54:52 2009 +0200
+++ b/index.cgi Sat Feb 07 16:33:27 2009 +0200
@@ -27,19 +27,16 @@
"""
try :
- from lib import page_tree
-
- # load page list
- page_tree.load()
-
- # our WSGI app
- from lib import wsgi
+ from lib import wsgi, site
# create handler
cgi_handler = wsgiref.handlers.CGIHandler()
+
+ # create app
+ app = wsgi.Application(site.SiteModuleCollection('sites'))
# run once
- cgi_handler.run(wsgi.app)
+ cgi_handler.run(app)
except :
cgi_error()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/filesystem/map.py Sat Feb 07 16:33:27 2009 +0200
@@ -0,0 +1,131 @@
+
+import os, os.path
+
+from lib import http, template, map
+
+import page, page_tree, menu
+
+class FilesystemMapper (map.Mapper) :
+ """
+ Translates requests to handlers based on a filesystem directory containing various kinds of files
+ """
+
+ # list of page handlers, by type
+ PAGE_TYPES = [
+ ('html', page.HTMLPage ),
+ (template.TEMPLATE_EXT, page.TemplatePage ),
+ ]
+
+ def __init__ (self, path) :
+ """
+ Create, path is where the pages are stored. The list of pages is loaded from $path/list
+ """
+
+ # store
+ self.path = path
+
+ # load the page tree
+ self.tree = page_tree.PageTree(path + '/list')
+
+ def _lookup_page_type (self, url, path, filename, basename, extension, tail) :
+ """
+ We found the file that we looked for, now get the correct type
+ """
+
+ # find appropriate handler
+ for handler_ext, type in self.PAGE_TYPES :
+ # match against file extension?
+ if handler_ext == extension :
+ # found handler, return instance
+ return type(self, url, path, basename, tail)
+
+ # no handler found
+ raise PageError("No handler found for page %r of type %r" % (url, extension))
+
+ def _lookup_page (self, name) :
+ """
+ Look up and return a Page object for the given page, or raise an error
+ """
+
+ # inital path
+ path = self.path
+ url_segments = []
+
+ # name segments
+ segments = name.split('/')
+
+ # iterate through the parts of the page segments
+ while True :
+ segment = None
+
+ # pop segment
+ if segments :
+ segment = segments.pop(0)
+
+ url_segments.append(segment)
+
+ # translate empty -> index
+ if not segment :
+ segment = 'index'
+
+ # look for it in the dir
+ for filename in os.listdir(path) :
+ # build full file path
+ file_path = os.path.join(path, filename)
+
+ # stat, recurse into subdirectory?
+ if os.path.isdir(file_path) and filename == segment :
+ # use new dir
+ path = file_path
+
+ # break for-loop to look at next segment
+ break
+
+ # split into basename + extension
+ basename, extension = os.path.splitext(filename)
+
+ # ...remove that dot
+ extension = extension.lstrip('.')
+
+ # match against requested page name?
+ if basename == segment :
+ # found the file we wanted
+ return self._lookup_page_type('/'.join(url_segments), file_path, filename, basename, extension, '/'.join(segments))
+
+ else :
+ # inspect next file in dir
+ continue
+
+ else :
+ # did not find any dir or file, break out of while loop
+ break
+
+ # did not find the filename we were looking for in os.listdir
+ raise PageError("Page not found: %s" % name, status='404 Not Found')
+
+ def handle_request (self, request) :
+ """
+ Looks up the appropriate Page, and then renders it
+ """
+
+ # determine the page name
+ page_name = request.get_page_name()
+
+ # get the page handler
+ p = self._lookup_page(page_name)
+
+ # bind to request
+ p.bind_request(request)
+
+ # render the template
+ response_data = template.render("layout",
+ site_root_url = request.get_script_dir(),
+ site_page_url = request.get_page_prefix(),
+ page = p,
+ menu = menu.Menu(self, p),
+ )
+
+ # return the response
+ return http.Response(response_data)
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/filesystem/menu.py Sat Feb 07 16:33:27 2009 +0200
@@ -0,0 +1,23 @@
+"""
+ Handling the list of available pages
+"""
+
+class Menu (object) :
+ """
+ Contains info needed to render the menu
+ """
+
+ def __init__ (self, fs, page) :
+ """
+ Gather the menu information for the given page, as part of the given FilesystemMapper
+ """
+
+ # the selected page
+ self.page = fs.tree.get_page(page.url)
+
+ # the selected pagen's inheritance
+ self.ancestry = self.page.get_ancestry() if self.page else []
+
+ # list of menu items == root children, since we always show the full menu...
+ self.items = fs.tree.root.children
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/filesystem/page.py Sat Feb 07 16:33:27 2009 +0200
@@ -0,0 +1,132 @@
+
+"""
+ Handling page requests
+"""
+
+# for filesystem ops
+import os, os.path
+import time
+
+from lib import http, template, config
+
+class PageError (http.ResponseError) :
+ """
+ Error looking up/handling a page
+ """
+
+ pass
+
+# XXX: should inherit from PageInfo
+class Page (object) :
+ """
+ This object represents the information about our attempt to render some specific page
+ """
+
+ def __init__ (self, fs, url, path, basename, url_tail, charset='utf8') :
+ """
+ Initialize the page at the given location
+
+ @param fs the FilesysteMapper
+ @param url the URL leading to this page
+ @param path the filesystem path to this page's file
+ @param basename the filesystem name of this page's file, without the file extension
+ @param url_trail trailing URL for this page
+ @param charset file charset
+ """
+
+ # store
+ self.fs = fs
+ self.url = url
+ self.path = path
+ self.basename = basename
+ self.url_tail = url_tail
+ self.charset = charset
+
+ # unbound
+ self.request = None
+
+ # sub-init
+ self._init()
+
+ def _init (self) :
+ """
+ Do initial data loading, etc
+ """
+
+ pass
+
+ def bind_request (self, request) :
+ """
+ Bind this page-render to the given request
+ """
+
+ self.request = request
+
+ @property
+ def title (self) :
+ """
+ Return the page's title
+
+ Defaults to the retreiving the page title from page_list, or basename in Titlecase.
+ """
+
+ # lookup in PageTree
+ page_info = self.fs.tree.get_page(self.url)
+
+ # fallback to titlecase
+ if page_info :
+ title = page_info.title
+
+ else :
+ title = self.basename.title()
+
+ return title
+
+ @property
+ def content (self) :
+ """
+ Return the page content as a string
+ """
+
+ abstract
+
+ @property
+ def modified (self) :
+ """
+ Returns the page modification timestamp
+ """
+
+ # stat
+ timestamp = os.stat(self.path).st_mtime
+
+ return time.strftime(config.DATETIME_FMT, time.gmtime(timestamp))
+
+class HTMLPage (Page) :
+ """
+ A simple .html page that's just passed through directly
+ """
+
+ @property
+ def content (self) :
+ """
+ Opens the .html file, reads and returns contents
+ """
+
+ return open(self.path, 'rb').read().decode(self.charset)
+
+class TemplatePage (Page) :
+ """
+ A template that's rendered using our template library
+ """
+
+ @property
+ def content (self) :
+ """
+ Loads the .tmpl file, and renders it
+ """
+
+ return template.render_file(self.path,
+ request = self.request,
+ page_tree = self.fs.tree
+ )
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/filesystem/page_tree.py Sat Feb 07 16:33:27 2009 +0200
@@ -0,0 +1,218 @@
+"""
+ Implements the tree containing pages and their metadata
+"""
+
+from lib import tree_parse
+
+class PageTreeError (Exception) :
+ """
+ Error parsing/loading the page tree
+ """
+
+ pass
+
+class PageInfo (object) :
+ """
+ Contains metainformation about a page
+ """
+
+ def __init__ (self, parent, name, title, children=None) :
+ """
+ Initialize, children defaults to empty list
+ """
+
+ # store
+ self.parent = parent
+ self.name = name
+ self.title = title
+ self.children = children if children else []
+
+ # no url get
+ self._url = None
+
+ def set_parent (self, parent) :
+ """
+ Set a parent where non was set before
+ """
+
+ assert self.parent is None
+
+ self.parent = parent
+
+ def add_child (self, child) :
+ """
+ Add a PageInfo child
+ """
+
+ self.children.append(child)
+
+ def get_child (self, name) :
+ """
+ Look up a child by name, returning None if not found
+ """
+
+ return dict((c.name, c) for c in self.children).get(name)
+
+ def get_ancestry (self) :
+ """
+ Returns a list of this page's parents and the page itself, but not root
+ """
+
+ # collect in reverse order
+ ancestry = []
+
+ # starting from self
+ item = self
+
+ # add all items, but not root
+ while item and item.parent :
+ ancestry.append(item)
+
+ item = item.parent
+
+ # reverse
+ ancestry.reverse()
+
+ # done
+ return ancestry
+
+ @property
+ def url (self) :
+ """
+ Build this page's URL
+ """
+
+ # cached?
+ if self._url :
+ return self._url
+
+ segments = [item.name for item in self.get_ancestry()]
+
+ # add empty segment if dir
+ if self.children :
+ segments.append('')
+
+ # join
+ url = '/'.join(segments)
+
+ # cache
+ self._url = url
+
+ # done
+ return url
+
+class PageTree (object) :
+ """
+ The tree of pages, rooted at .root.
+
+ Use load_page_tree to initialize the global page_tree instance, and then use that
+ """
+
+ def __init__ (self, path) :
+ """
+ Loads the PageTree root from the given file
+ """
+
+ # store
+ self.path = path
+
+ # load
+ self._load(path)
+
+ def _load (self, path) :
+ """
+ Processes the lines in the given file
+ """
+
+ # parse tree
+ tree = tree_parse.parse(path, ':')
+
+ if not tree :
+ raise PageTreeError("No root node found")
+
+ def _create_node (parent, item) :
+ """
+ Creates and returns a PageInfo from the given parent node and (line_number, line, children) tuple item
+ """
+
+ # unpack
+ line_number, line, children = item
+
+ # parse line
+ url = title = None
+
+ try :
+ url, title = line.split(':')
+
+ except :
+ raise PageTreeError("Invalid line: %s:%d: %r" % (path, line_number, line))
+
+ # remove whitespace
+ url = url.strip()
+ title = title.strip()
+
+ # create PageInfo
+ node = PageInfo(parent, url, title)
+
+ # set node children
+ node.children = [
+ _create_node(node, child_item) for child_item in children
+ ]
+
+ # return
+ return node
+
+ # translate
+ self.root = _create_node(None, tree)
+
+ # *evil cackle*
+ self.root.children.insert(0, self.root)
+
+ def get_page (self, url) :
+ """
+ Lookup the given page URL, and return the matching PageInfo object, or None, if not found
+ """
+
+ # start from root
+ node = self.root
+
+ # traverse the object tree
+ for segment in url.split('/') :
+ if segment :
+ node = node.get_child(segment)
+
+ if not node :
+ return None
+
+ # return
+ return node
+
+ def get_siblings (self, url) :
+ """
+ Get the list of siblings for the given url, including the given page itself
+ """
+
+ # look up the page itself
+ page = self.get_page(url)
+
+ # specialcase root/unknown node
+ if page and page.parent :
+ return page.parent.children
+
+ else :
+ return self.root.children
+
+ def dump (self) :
+ """
+ Returns a string representation of the tree
+ """
+
+ def _print_node (indent, node) :
+ return '\n'.join('%s%s' % (' '*indent, line) for line in [
+ "%-15s : %s" % (node.name, node.title)
+ ] + [
+ _print_node(indent + 4, child) for child in node.children if child != node
+ ])
+
+ return _print_node(0, self.root)
+
--- a/lib/handler.py Sat Feb 07 06:54:52 2009 +0200
+++ b/lib/handler.py Sat Feb 07 16:33:27 2009 +0200
@@ -2,7 +2,7 @@
The actual application behaviour, i.e. generating a Response from a Request :)
"""
-class Handler (object) :
+class RequestHandler (object) :
"""
A handler handles a Request, returning a Response
"""
@@ -15,35 +15,10 @@
def handle_request (self, request) :
"""
Handle the request, returning a Response object
+
+ XXX: rename to __call__ kplzthx
"""
return self.func(request, *self.args, **self.kwargs)
-# fs handler
-import http, page, menu, template
-
-def handle_request (request) :
- """
- Take the Request, and return a Response
- """
-
- # determine the page name
- page_name = request.get_page_name()
-
- # get the page handler
- p = page.lookup(page_name)
- # bind to request
- p.bind_request(request)
-
- # render the template
- response_data = template.render("layout",
- site_root_url = request.get_script_dir(),
- site_page_url = request.get_page_prefix(),
- page = p,
- menu = menu.Menu(p),
- )
-
- # return the response
- return http.Response(response_data)
-
--- a/lib/map.py Sat Feb 07 06:54:52 2009 +0200
+++ b/lib/map.py Sat Feb 07 16:33:27 2009 +0200
@@ -13,14 +13,14 @@
def __init__ (self, url) :
super(MappingError, self).__init__("URL not found: %s" % (url, ), status='404 Not Found')
-class Mapper (object) :
+class Mapper (handler.RequestHandler) :
"""
- Translates requests to handlers
+ Looks up the handler to use based on the URL
"""
- def map_request (self, request) :
+ def handle_request (self, request) :
"""
- Map the given request, returning a Handler
+ Map the given request
"""
abstract
@@ -73,40 +73,24 @@
Returns the appropriate handler
"""
+ # find handler to use
+ handler = None
+
# just test each mapping in turn
for mapping in self.mappings :
handler = mapping.test(request)
if handler :
- return handler
-
- # fail, not found
- raise MappingError(request.get_page_name())
+ break
-class FilesystemMapper (Mapper) :
- """
- Translates requests to handlers based on a filesystem directory containing various kinds of files
- """
-
- def __init__ (self, path) :
- """
- Create, path is where the pages are stored
- """
+ if not handler :
+ # fail, not found
+ raise MappingError(request.get_page_name())
- # store
- self.path = path
-
- def map_request (self, request) :
- """
- Looks up the appropriate Page, and then returns a generic Handler
- """
-
- # XXX: harcoded
- return handler.Handler(handler.handle_request)
+ # passthrough
+ return handler.handle_request(request)
# "friendly" names
-fstree = FilesystemMapper
-
map = SimpleMapping
mapre = RegexpMapping
--- a/lib/menu.py Sat Feb 07 06:54:52 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-"""
- Handling the list of available pages
-"""
-
-# for page_list
-from page_tree import page_tree
-
-class Menu (object) :
- """
- Contains info needed to render the menu
- """
-
- def __init__ (self, page) :
- """
- Gather the menu information for the given page
- """
-
- # the selected page
- self.page = page_tree.get_page(page.url)
-
- # the selected pagen's inheritance
- self.ancestry = self.page.get_ancestry() if self.page else []
-
- # list of menu items == root children, since we always show the full menu...
- self.items = page_tree.root.children
-
--- a/lib/page.py Sat Feb 07 06:54:52 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,222 +0,0 @@
-
-"""
- Handling page requests
-"""
-
-# for filesystem ops
-import os, os.path
-import time
-
-# for ResponseError
-import http
-
-# for TemplatePage
-import template
-
-from page_tree import page_tree
-import config
-
-# path to directory containing the page heirarcy
-PAGE_DIR = "pages"
-
-class PageError (http.ResponseError) :
- """
- Error looking up/handling a page
- """
-
- pass
-
-# XXX: should inherit from PageInfo
-class Page (object) :
- """
- This object represents the information about our attempt to render some specific page
- """
-
- def __init__ (self, url, path, basename, url_tail, charset='utf8') :
- """
- Initialize the page at the given location
-
- @param url the URL leading to this page
- @param path the filesystem path to this page's file
- @param basename the filesystem name of this page's file, without the file extension
- @param url_trail trailing URL for this page
- @param charset file charset
- """
-
- # store
- self.url = url
- self.path = path
- self.basename = basename
- self.url_tail = url_tail
- self.charset = charset
-
- # unbound
- self.request = None
-
- # sub-init
- self._init()
-
- def _init (self) :
- """
- Do initial data loading, etc
- """
-
- pass
-
- def bind_request (self, request) :
- """
- Bind this page-render to the given request
- """
-
- self.request = request
-
- @property
- def title (self) :
- """
- Return the page's title
-
- Defaults to the retreiving the page title from page_list, or basename in Titlecase.
- """
-
- # lookup in page_list
- page_info = page_tree.get_page(self.url)
-
- # fallback to titlecase
- if page_info :
- title = page_info.title
-
- else :
- title = self.basename.title()
-
- return title
-
- @property
- def content (self) :
- """
- Return the page content as a string
- """
-
- abstract
-
- @property
- def modified (self) :
- """
- Returns the page modification timestamp
- """
-
- # stat
- timestamp = os.stat(self.path).st_mtime
-
- return time.strftime(config.DATETIME_FMT, time.gmtime(timestamp))
-
-class HTMLPage (Page) :
- """
- A simple .html page that's just passed through directly
- """
-
- @property
- def content (self) :
- """
- Opens the .html file, reads and returns contents
- """
-
- return open(self.path, 'rb').read().decode(self.charset)
-
-class TemplatePage (Page) :
- """
- A template that's rendered using our template library
- """
-
- @property
- def content (self) :
- """
- Loads the .tmpl file, and renders it
- """
-
- return template.render_file(self.path,
- request = self.request,
- page_tree = page_tree
- )
-
-# list of page handlers, by type
-TYPE_HANDLERS = [
- ('html', HTMLPage ),
- (template.TEMPLATE_EXT, TemplatePage ),
-]
-
-def _lookup_handler (url, path, filename, basename, extension, tail) :
- """
- We found the file that we looked for, now get its handler
- """
-
- # find appropriate handler
- for handler_ext, handler in TYPE_HANDLERS :
- # match against file extension?
- if handler_ext == extension :
- # found handler, return instance
- return handler(url, path, basename, tail)
-
- # no handler found
- raise PageError("No handler found for page %r of type %r" % (url, extension))
-
-def lookup (name) :
- """
- Look up and return a Page object for the given page, or raise an error
- """
-
- # inital path
- path = PAGE_DIR
- url_segments = []
-
- # name segments
- segments = name.split('/')
-
- # iterate through the parts of the page segments
- while True :
- segment = None
-
- # pop segment
- if segments :
- segment = segments.pop(0)
-
- url_segments.append(segment)
-
- # translate empty -> index
- if not segment :
- segment = 'index'
-
- # look for it in the dir
- for filename in os.listdir(path) :
- # build full file path
- file_path = os.path.join(path, filename)
-
- # stat, recurse into subdirectory?
- if os.path.isdir(file_path) and filename == segment :
- # use new dir
- path = file_path
-
- # break for-loop to look at next segment
- break
-
- # split into basename + extension
- basename, extension = os.path.splitext(filename)
-
- # ...remove that dot
- extension = extension.lstrip('.')
-
- # match against requested page name?
- if basename == segment :
- # found the file we wanted
- return _lookup_handler('/'.join(url_segments), file_path, filename, basename, extension, '/'.join(segments))
-
- else :
- # inspect next file in dir
- continue
-
- else :
- # did not find any dir or file, break out of while loop
- break
-
- # did not find the filename we were looking for in os.listdir
- raise PageError("Page not found: %s" % name, status='404 Not Found')
-
--- a/lib/page_tree.py Sat Feb 07 06:54:52 2009 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-"""
- Implements the tree containing pages and their metadata
-"""
-
-import tree_parse
-
-# path to file containing the page metadata tree
-PAGE_TREE_FILE = "pages/list"
-
-class PageTreeError (Exception) :
- """
- Error parsing/loading the page tree
- """
-
- pass
-
-class PageInfo (object) :
- """
- Contains metainformation about a page
- """
-
- def __init__ (self, parent, name, title, children=None) :
- """
- Initialize, children defaults to empty list
- """
-
- # store
- self.parent = parent
- self.name = name
- self.title = title
- self.children = children if children else []
-
- # no url get
- self._url = None
-
- def set_parent (self, parent) :
- """
- Set a parent where non was set before
- """
-
- assert self.parent is None
-
- self.parent = parent
-
- def add_child (self, child) :
- """
- Add a PageInfo child
- """
-
- self.children.append(child)
-
- def get_child (self, name) :
- """
- Look up a child by name, returning None if not found
- """
-
- return dict((c.name, c) for c in self.children).get(name)
-
- def get_ancestry (self) :
- """
- Returns a list of this page's parents and the page itself, but not root
- """
-
- # collect in reverse order
- ancestry = []
-
- # starting from self
- item = self
-
- # add all items, but not root
- while item and item.parent :
- ancestry.append(item)
-
- item = item.parent
-
- # reverse
- ancestry.reverse()
-
- # done
- return ancestry
-
- @property
- def url (self) :
- """
- Build this page's URL
- """
-
- # cached?
- if self._url :
- return self._url
-
- segments = [item.name for item in self.get_ancestry()]
-
- # add empty segment if dir
- if self.children :
- segments.append('')
-
- # join
- url = '/'.join(segments)
-
- # cache
- self._url = url
-
- # done
- return url
-
-class PageTree (object) :
- """
- The tree of pages, rooted at .root.
-
- Use load_page_tree to initialize the global page_tree instance, and then use that
- """
-
- def __init__ (self) :
- """
- Empty PageList, must call load_page_list to initialize, once
- """
-
- def _load (self, path) :
- """
- Processes the lines in the given file
- """
-
- # parse tree
- tree = tree_parse.parse(path, ':')
-
- if not tree :
- raise PageTreeError("No root node found")
-
- def _create_node (parent, item) :
- """
- Creates and returns a PageInfo from the given parent node and (line_number, line, children) tuple item
- """
-
- # unpack
- line_number, line, children = item
-
- # parse line
- url = title = None
-
- try :
- url, title = line.split(':')
-
- except :
- raise PageTreeError("Invalid line: %s:%d: %r" % (path, line_number, line))
-
- # remove whitespace
- url = url.strip()
- title = title.strip()
-
- # create PageInfo
- node = PageInfo(parent, url, title)
-
- # set node children
- node.children = [
- _create_node(node, child_item) for child_item in children
- ]
-
- # return
- return node
-
- # translate
- self.root = _create_node(None, tree)
-
- # *evil cackle*
- self.root.children.insert(0, self.root)
-
- def get_page (self, url) :
- """
- Lookup the given page URL, and return the matching PageInfo object, or None, if not found
- """
-
- # start from root
- node = self.root
-
- # traverse the object tree
- for segment in url.split('/') :
- if segment :
- node = node.get_child(segment)
-
- if not node :
- return None
-
- # return
- return node
-
- def get_siblings (self, url) :
- """
- Get the list of siblings for the given url, including the given page itself
- """
-
- # look up the page itself
- page = self.get_page(url)
-
- # specialcase root/unknown node
- if page and page.parent :
- return page.parent.children
-
- else :
- return self.root.children
-
- def dump (self) :
- """
- Returns a string representation of the tree
- """
-
- def _print_node (indent, node) :
- return '\n'.join('%s%s' % (' '*indent, line) for line in [
- "%-15s : %s" % (node.name, node.title)
- ] + [
- _print_node(indent + 4, child) for child in node.children if child != node
- ])
-
- return _print_node(0, self.root)
-
-# global singleton PageList instance
-page_tree = PageTree()
-
-def load () :
- """
- Load the global singleton PageInfo instance
- """
-
- page_tree._load(PAGE_TREE_FILE)
-
--- a/lib/site.py Sat Feb 07 06:54:52 2009 +0200
+++ b/lib/site.py Sat Feb 07 16:33:27 2009 +0200
@@ -4,53 +4,82 @@
import imp
-SITE_DIR = "sites"
+import handler
-class Site (object) :
+class Site (handler.RequestHandler) :
"""
A site is a website and its configuration
+
+ XXX: need to somehow communicate the site name to our downstream handler
"""
- def __init__ (self, name) :
+ def __init__ (self, name, handler) :
"""
The given name must be like a valid hostname, e.g. 'www.qmsk.net'
"""
# store
self.name = name
+ self.handler = handler
- # load the site
- self._load()
-
- def _load (self) :
+ def handle_request (self, request) :
"""
- Loads this site, as a python module (i.e. dir with __init__.py)
+ Map the request through our handler...
"""
- # first, we need to find it
- file, pathname, description = imp.find_module(self.name, [SITE_DIR])
+ return self.handler.handle_request(request)
- # then, we can load it
- self.module = imp.load_module(self.name, file, pathname, description)
-
- # create our mapper
- self.mapper = self.module.build_mapper()
+class SiteModule (Site) :
+ """
+ A site, represented as python module/package, with the following module attributes:
- def get_mapper (self) :
+ handler - the RequestHandler to use
+ """
+
+ def __init__ (self, name, module) :
"""
- Return the Mapper for this site
+ Create the Site based on the given module
"""
- return self.mapper
+ super(SiteModule, self).__init__(name,
+ module.handler
+ )
-def lookup (request) :
+class SiteModuleCollection (handler.RequestHandler) :
"""
- Lookup and return a Site object for the given request
+ A collection of SiteModules, looking up the correct site to use based on the request hostname
"""
- # request hostnmae
- hostname = request.env.get('HTTP_POST')
+ def __init__ (self, path) :
+ """
+ Initialize to load site modules from the given path
+ """
- # XXX: hardcoded for now
- return Site("www.qmsk.net")
+ self.path = path
+ self.site_cache = dict()
+ def handle_request (self, request) :
+ """
+ Lookup and return a Site object for the given request
+ """
+
+ # request hostnmae
+ name = request.env.get('HTTP_HOST')
+
+ # already loaded?
+ if name in self.site_cache :
+ site = self.site_cache[name]
+
+ else :
+ # first, we need to find it
+ file, pathname, description = imp.find_module(name, [self.path])
+
+ # then, we can load the module
+ module = imp.load_module(name, file, pathname, description)
+
+ # then build+cache the SiteModule
+ site = self.site_cache[name] = SiteModule(name, module)
+
+ # then execute the site's request handler
+ return site.handle_request(request)
+
--- a/lib/wsgi.py Sat Feb 07 06:54:52 2009 +0200
+++ b/lib/wsgi.py Sat Feb 07 16:33:27 2009 +0200
@@ -9,63 +9,60 @@
# for Request/Response
import http
-# to lookup the Site
-from site import lookup as site_lookup
-
-# for the request -> response bit :)
-import handler
-
-def request_handler (env, start_response) :
+class Application (object) :
"""
- The actual request handling code
+ Our WSGI application, implements the wsgi __call__ interface
"""
- # build Request object
- request = http.Request(env)
-
- # lookup site
- site = site_lookup(request)
-
- # mapper...
- mapper = site.get_mapper()
+ def __init__ (self, handler) :
+ """
+ Initialize to use the given handler for requests
+ """
- # lookup handler
- handler = mapper.map_request(request)
-
- try :
- # request -> response
- response = handler.handle_request(request)
-
- except http.ResponseError, err :
- # just use the generated response
- response = err.get_response()
-
- # send response
- assert response, "No response"
+ self.handler = handler
- # send response status/headers
- start_response(response.get_status(), response.get_headers())
-
- # send respones data
- yield response.get_data()
-
-def app (env, start_response) :
- """
- Wraps request_handler to trap errors
- """
+ def handle_request (self, env, start_response) :
+ """
+ The actual request handling code
+ """
- try :
- # passthrough request_handler
- for chunk in request_handler(env, start_response) :
- yield chunk
+ # build Request object
+ request = http.Request(env)
- except :
- # execption info
- info = sys.exc_info()
+ try :
+ # request -> response using our handler
+ response = self.handler.handle_request(request)
- # try and send 500 ISE to browser, if no headers yet...
- start_response("500 Internal Server Error", [('Content-type', "text/plain; charset=utf8")], info)
+ except http.ResponseError, err :
+ # just use the generated response
+ response = err.get_response()
- # send traceback
- yield traceback.format_exc()
+ # send response
+ assert response, "No response"
+
+ # send response status/headers
+ start_response(response.get_status(), response.get_headers())
+ # send respones data
+ yield response.get_data()
+
+ def __call__ (self, env, start_response) :
+ """
+ Wraps handle_request to trap errors
+ """
+
+ try :
+ # passthrough request_handler
+ for chunk in self.handle_request(env, start_response) :
+ yield chunk
+
+ except :
+ # execption info
+ info = sys.exc_info()
+
+ # try and send 500 ISE to browser, if no headers yet...
+ start_response("500 Internal Server Error", [('Content-type', "text/plain; charset=utf8")], info)
+
+ # send traceback
+ yield traceback.format_exc()
+
--- a/sites/www.qmsk.net/__init__.py Sat Feb 07 06:54:52 2009 +0200
+++ b/sites/www.qmsk.net/__init__.py Sat Feb 07 16:33:27 2009 +0200
@@ -2,7 +2,8 @@
The www.qmsk.net site is just a simple site with a filesystem-based URL mapping
"""
-from lib.map import fstree
+from lib.filesystem.map import FilesystemMapper as _fstree
-build_mapper = lambda: fstree("site/www.qmsk.net")
+# global mapper attribute
+handler = _fstree("pages")