You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
4.1 KiB

  1. #!/usr/bin/python
  2. # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
  3. import re
  4. import imp
  5. import os
  6. class WSGIRouter(object):
  7. """ parse http uri and call the proper handler (controller in MVC terms)
  8. """
  9. def __init__( self, handlers=(), fallback=None ):
  10. """ handlers = list of tuples in the form ( regex, handler )
  11. groups in regex are passed to handlers within environ['router.args']
  12. fallback = if defined, is the default handler to be called if no
  13. regex in 'handlers' matches
  14. """
  15. self.handlers = handlers
  16. self.fallback = fallback
  17. def __call__( self, environ, start_response ):
  18. #
  19. # search form matching handler
  20. #
  21. for regex, handler in self.handlers:
  22. match = re.match( '^' + regex + '$', environ['SCRIPT_URL'])
  23. if match:
  24. environ['router.args'] = match.groups()
  25. return handler( environ, start_response )
  26. #
  27. # use fallback ( if defined )
  28. #
  29. if self.fallback:
  30. environ['router.args'] = [
  31. token for token in environ['SCRIPT_URL'].split('/') if token ]
  32. return self.fallback( environ, start_response )
  33. #
  34. # last resort
  35. #
  36. start_response( '202 Accepted', [('Content-Type', 'text/plain')] )
  37. return [ "WSGIRouter: I have no route for '%s'" % environ['SCRIPT_URL'] ]
  38. class WSGISmartRouter(object):
  39. """ like WSGIRouter, but before calling fallback handler,
  40. searches in 'cont_dir' folder for a file matching the url.
  41. uri = http://<server[:port]>/lorem/ipsum/dolor/sit
  42. handler: environ['router.args']:
  43. any handler matching in handlers - capture groups -
  44. <cont_dir>/lorem/ipsum/dolor/sit ()
  45. <cont_dir>/lorem/ipsum/dolor ('sit')
  46. <cont_dir>/lorem/ipsum ('dolor','sit')
  47. <cont_dir>/lorem ('ipsum','dolor','sit')
  48. fallback ('lorem','ipsum','dolor','sit')
  49. """
  50. def __init__( self, handlers=(), fallback=None, cont_dir='.' ):
  51. self.handlers = handlers
  52. self.fallback = fallback
  53. self.cont_dir = os.path.normpath( os.path.join( os.path.split(__file__)[0], cont_dir ) ) + '/'
  54. def __call__( self, environ, start_response ):
  55. #
  56. # search form matching handler
  57. #
  58. for regex, handler in self.handlers:
  59. match = re.match( '^' + regex + '$', environ['SCRIPT_URL'])
  60. if match:
  61. environ['router.args'] = match.groups()
  62. return handler( environ, start_response )
  63. #
  64. # search in filesystem for matching file
  65. #
  66. tokens = [ token for token in environ['SCRIPT_URL'].split('/') if token ]
  67. #for idx in range( 1, len( tokens ) + 1 ): # from shortest path to longest one
  68. for idx in range( len( tokens ) , 0, -1 ): # from longest path to shortest one
  69. module = os.path.normpath( self.cont_dir + '/'.join( tokens[:idx] ) + '.py' )
  70. args = tokens[idx:]
  71. try:
  72. handler = imp.load_source( 'application', module )
  73. environ['router.args'] = args
  74. if environ['REQUEST_METHOD'] == 'GET' and hasattr( handler, 'get' ):
  75. return handler.get( environ, start_response )
  76. elif environ['REQUEST_METHOD'] == 'POST' and hasattr( handler, 'post' ):
  77. return handler.post( environ, start_response )
  78. elif hasattr( handler, 'application' ):
  79. return handler.application( environ, start_response )
  80. except IOError:
  81. # not found! Better luck next time...
  82. pass
  83. #
  84. # use fallback ( if defined )
  85. #
  86. if self.fallback:
  87. environ['router.args'] = tokens
  88. return self.fallback( environ, start_response )
  89. #
  90. # last resort
  91. #
  92. start_response( '202 Accepted', [('Content-Type', 'text/plain')] )
  93. return [ "WSGISmartRouter: I have no route for '%s'" % environ['SCRIPT_URL'] ]