#!/usr/bin/python # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- import re import imp import os class WSGIRouter(object): """ parse http uri and call the proper handler (controller in MVC terms) """ def __init__( self, handlers=(), fallback=None ): """ handlers = list of tuples in the form ( regex, handler ) groups in regex are passed to handlers within environ['router.args'] fallback = if defined, is the default handler to be called if no regex in 'handlers' matches """ self.handlers = handlers self.fallback = fallback def __call__( self, environ, start_response ): # # search form matching handler # for regex, handler in self.handlers: match = re.match( '^' + regex + '$', environ['SCRIPT_URL']) if match: environ['router.args'] = match.groups() return handler( environ, start_response ) # # use fallback ( if defined ) # if self.fallback: environ['router.args'] = [ token for token in environ['SCRIPT_URL'].split('/') if token ] return self.fallback( environ, start_response ) # # last resort # start_response( '202 Accepted', [('Content-Type', 'text/plain')] ) return [ "WSGIRouter: I have no route for '%s'" % environ['SCRIPT_URL'] ] class WSGISmartRouter(object): """ like WSGIRouter, but before calling fallback handler, searches in 'cont_dir' folder for a file matching the url. uri = http:///lorem/ipsum/dolor/sit handler: environ['router.args']: any handler matching in handlers - capture groups - /lorem/ipsum/dolor/sit () /lorem/ipsum/dolor ('sit') /lorem/ipsum ('dolor','sit') /lorem ('ipsum','dolor','sit') fallback ('lorem','ipsum','dolor','sit') """ def __init__( self, handlers=(), fallback=None, cont_dir='.' ): self.handlers = handlers self.fallback = fallback self.cont_dir = os.path.normpath( os.path.join( os.path.split(__file__)[0], cont_dir ) ) + '/' def __call__( self, environ, start_response ): # # search form matching handler # for regex, handler in self.handlers: match = re.match( '^' + regex + '$', environ['SCRIPT_URL']) if match: environ['router.args'] = match.groups() return handler( environ, start_response ) # # search in filesystem for matching file # tokens = [ token for token in environ['SCRIPT_URL'].split('/') if token ] #for idx in range( 1, len( tokens ) + 1 ): # from shortest path to longest one # from longest path to shortest one for idx in range( len( tokens ) , 0, -1 ): module = os.path.normpath( self.cont_dir + '/'.join( tokens[:idx] ) + '.py' ) args = tokens[idx:] try: handler = imp.load_source( 'application', module ) environ['router.args'] = args if environ['REQUEST_METHOD'] == 'GET' and hasattr( handler, 'get' ): # FIXME: forse รจ meglio eseguire QUI la start_response # e compilare il template (solo per GET e POST) return handler.get( environ, start_response ) elif environ['REQUEST_METHOD'] == 'POST' and hasattr( handler, 'post' ): return handler.post( environ, start_response ) elif hasattr( handler, 'application' ): return handler.application( environ, start_response ) except IOError: # not found! Better luck next time... pass # # use fallback ( if defined ) # if self.fallback: environ['router.args'] = tokens return self.fallback( environ, start_response ) # # last resort # start_response( '202 Accepted', [('Content-Type', 'text/plain')] ) return [ "WSGISmartRouter: I have no route for '%s'" % environ['SCRIPT_URL'] ]