|
- #!/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://<server[:port]>/lorem/ipsum/dolor/sit
-
- handler: environ['router.args']:
-
- any handler matching in handlers - capture groups -
- <cont_dir>/lorem/ipsum/dolor/sit ()
- <cont_dir>/lorem/ipsum/dolor ('sit')
- <cont_dir>/lorem/ipsum ('dolor','sit')
- <cont_dir>/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'] ]
|