Support for `__before__` and `__after__` method calls for controllers

For convenience, a `Controller` class now exists from which all classes wanting to implement these methods should descend. This will allow sane cooperative multiple inheritance.
master
Dustin C. Hatch 2011-03-27 14:54:42 -05:00
parent 02bc0ea404
commit 9b30000a36
2 changed files with 87 additions and 14 deletions

View File

@ -7,10 +7,12 @@ Please give me a docstring!
:Updated: $Date$
:Updater: $Author$
'''
import milla.dispatch.traversal
from webob.exc import HTTPNotFound, WSGIHTTPException
import milla.dispatch
import webob
__all__ = ['Application']
class Application(object):
'''Represents a Milla web application
@ -22,33 +24,81 @@ class Application(object):
automatically created if a root is given
:param dispatcher: An object implementing the dispatcher protocol
``Application`` instances are WSGI applications
``Application`` instances are WSGI applications.
.. py:attribute:: config
A mapping of configuration settings. For each request, the
configuration is copied and assigned to ``request.config``.
'''
def __init__(self, root=None, dispatcher=None):
if not dispatcher:
if root:
self.dispatcher = milla.dispatch.traversal.Traverser(root)
else:
raise ValueError('Must specify either a root object or a '
'dispatcher')
else:
self.dispatcher = dispatcher
def __init__(self, dispatcher):
self.dispatcher = dispatcher
self.config = {}
def __call__(self, environ, start_response):
path_info = environ['PATH_INFO']
try:
func = self.dispatcher.resolve(environ['PATH_INFO'])
func = self.dispatcher.resolve(path_info)
except milla.dispatch.UnresolvedPath:
return HTTPNotFound()(environ, start_response)
request = webob.Request(environ)
request.config = self.config.copy()
start_response_wrapper = StartResponseWrapper(start_response)
request.start_response = start_response_wrapper
try:
# If the callable has an __before__ attribute, call it
if hasattr(func, '__before__'):
func.__before__(request)
# If the callable is an instance method and its class has
# a __before__ method, call that
elif hasattr(func, 'im_self') and \
hasattr(func.im_self, '__before__'):
func.im_self.__before__(request)
# The callable might be a partial, so check the inner func
elif hasattr(func, 'func'):
if hasattr(func.func, '__before__'):
func.func.__before__(request)
elif hasattr(func.func, 'im_self') and \
hasattr(func.func.im_self, '__before__'):
func.func.im_self.__before__(request)
response = func(request)
except WSGIHTTPException as e:
return e(environ, start_response)
finally:
# If the callable has an __after__ method, call it
if hasattr(func, '__after__'):
func.__after__(request)
# If the callable is an instance method and its class has
# an __after__ method, call that
elif hasattr(func, 'im_self') and \
hasattr(func.im_self, '__after__'):
func.im_self.__after__(request)
# The callable might be a partial, so check the inner func
elif hasattr(func, 'func'):
if hasattr(func.func, '__after__'):
func.func.__after__(request)
elif hasattr(func.func, 'im_self') and \
hasattr(func.func.im_self, '__after__'):
func.func.im_self.__after__(request)
if isinstance(response, basestring):
# The callable might have returned just a string, which is OK,
# but we need to wrap it in a WebOb response
if isinstance(response, basestring) or not response:
response = webob.Response(response)
start_response(response.status, response.headerlist)
if not start_response_wrapper.called:
start_response(response.status, response.headerlist)
return response.app_iter
class StartResponseWrapper():
def __init__(self, start_response):
self.start_response = start_response
self.called = False
def __call__(self, *args, **kwargs):
self.called = True
return self.start_response(*args, **kwargs)

23
src/milla/controllers.py Normal file
View File

@ -0,0 +1,23 @@
'''Stub controller classes
These classes can be used as base classes for controllers. While any
callable can technically be a controller, using a class that inherits
from one or more of these classes can make things significantly easier.
:Created: Mar 27, 2011
:Author: dustin
:Updated: $Date$
:Updater: $Author$
'''
class Controller(object):
'''The base controller class
This class simply provides empty ``__before__`` and ``__after__``
methods to facilitate cooperative multiple inheritance.
'''
def __before__(self, request):
pass
def __after__(self, request):
pass