'''Contains the main Router class that functions as the main WSGI app, and an
Application object that represents a WSGI app
'''
import logging
[docs]class Application:
'''This class represents a WSGI application. It holds the WSGI handler
function of the app, the url prefix that the app is mounted at, and some
other configuration options. A ``Router`` object holds an array of
``Application`` objects, along with the default WSGI application.
:param func: The WSGI handler function (the one with the ``environ`` and the
``start_request`` arguments)
:param url: The url to mount the application at. It must have a beginning
forward slash, but must not have one at the end
(e.g. ``/path/to/app``).
:param modify_urls: Whether or not to parse the app's output and correct
urls in it (e.g. in an ``<a>`` tag) to go to urls within
the one the app is mounted at. For example, an
application that is mounted at ``/oof`` will have
``<a href="/no">`` replaced to ``<a href="/oof/no">``. It
currently has not been implemented yet. When this
parameter is set to ``True`` (the default), the app's
output will be minified regardless of the ``minify``
attribute.
'''
def __init__(self, func, url, modify_urls=True, minify=False):
self.func = func
self.url = url
self.modify_urls = modify_urls
self.minify = minify
logging.info('Initialized Application with url "{}" and handler {}'.format(url, func))
def __call__(self, *args):
'''Calls the application's ``func`` attribute'''
self.func(*args)
[docs]class Router:
__slots__ = ('default', 'apps')
def __init__(self, default_app=None, apps=[]):
if default_app:
self.default = Application(func=default_app, url='/')
self.apps=apps
logging.info('Initialized styrofoam.Router')
[docs] def add_app(self, *args):
'''Adds a WSGI application to the router. The attributes passed to this
method are passed to ``Application.__init__``, so these two are the same:
::
my_router.apps.append(Application(func=f, url='/hi'))
::
my_router.add_app(func=f, url='/hi')
'''
self.apps.append(Application(*args))
def __call__(self, environ, start_response):
logging.info('Router has been called')
logging.debug('Values in environ dictionary:')
for key, value in environ.items():
logging.debug(' {} = "{}"'.format(key, value))
selected_app = None
for app in self.apps:
logging.debug('Checking if "{}" starts with "{}"'.format(environ['SCRIPT_NAME'], app.url))
if environ['PATH_INFO'].startswith(app.url):
logging.debug('App {} has been selected'.format(app))
selected_app = app
break
else:
logging.debug('Default app has been selected')
if selected_app is not None:
return selected_app.func(environ, start_response)
else:
return self.default.func(environ, start_response)