diff options
author | Marcel Hellkamp <marc@gsites.de> | 2010-07-08 22:02:13 +0200 |
---|---|---|
committer | Marcel Hellkamp <marc@gsites.de> | 2010-07-08 22:15:03 +0200 |
commit | e9047a78c37748da20c94dfbf6ab2fd5a03ccdc1 (patch) | |
tree | 9f2b70617874fe10536521849600d04277bf4093 | |
parent | a2d3cb2e8305a3583d9f9f4770a4e95779fa3307 (diff) | |
download | bottle-e9047a78c37748da20c94dfbf6ab2fd5a03ccdc1.tar.gz |
The new hook-API allows a plugin to register functions that are then called at
specific stages during the request circle. This closes the gap between
WSGI middleware (app-wide but no access to request/response objects) and
decorators (not app-wide).
-rw-r--r-- | apidoc/changelog.rst | 10 | ||||
-rwxr-xr-x | apidoc/index.rst | 1 | ||||
-rwxr-xr-x | bottle.py | 32 |
3 files changed, 35 insertions, 8 deletions
diff --git a/apidoc/changelog.rst b/apidoc/changelog.rst index e6b3201..d9b7974 100644 --- a/apidoc/changelog.rst +++ b/apidoc/changelog.rst @@ -5,6 +5,16 @@ Release Notes and Changelog =========================== +Release 0.9 +=========== + +This changes are not released yet and are only part of the development documentation. + +.. rubric:: New Features + +* A new hook-API to inject code immediately before or after the execution of handler callbacks. + + Release 0.7 =========== diff --git a/apidoc/index.rst b/apidoc/index.rst index ae62f69..7c4a524 100755 --- a/apidoc/index.rst +++ b/apidoc/index.rst @@ -62,6 +62,7 @@ The documentation is a work in progress. If you have questions not answered here stpl changelog development + plugindev Tutorials and Resources ======================= @@ -81,6 +81,7 @@ import thread import threading import time import tokenize +import types from Cookie import SimpleCookie from tempfile import TemporaryFile @@ -372,6 +373,7 @@ class Bottle(object): self.castfilter = [] if autojson and json_dumps: self.add_filter(dict, dict2json) + self.hooks = {'before_request': [], 'after_request': []} def mount(self, app, script_path): ''' Mount a Bottle application to a specific URL prefix ''' @@ -470,12 +472,21 @@ class Bottle(object): return self.route(path, method, **kargs) def error(self, code=500): - """ Decorator: Registrer an output handler for a HTTP error code""" + """ Decorator: Register an output handler for a HTTP error code""" def wrapper(handler): self.error_handler[int(code)] = handler return handler return wrapper + def hook(self, hook): + """ Decorator: Register a hook.""" + def wrapper(handler): + if name not in self.hooks: + raise ValueError("Unknown hook name %s" % name) + self.hooks[name] = handler + return handler + return wrapper + def handle(self, url, method): """ Execute the handler bound to the specified url and method and return its output. If catchall is true, exceptions are catched and returned as @@ -560,16 +571,19 @@ class Bottle(object): return self._cast(HTTPError(500, 'Unsupported response type: %s'\ % type(first)), request, response) - def __call__(self, environ, start_response): + def wsgi(self, environ, start_response): """ The bottle WSGI-interface. """ try: environ['bottle.app'] = self request.bind(environ) - response.bind() - out = self.handle(request.path, request.method) - out = self._cast(out, request, response) + response.bind(start_response) + for func in self.hooks['before_request']: func() + response.output = self.handle(request.path, request.method) + for func in self.hooks['after_request']: func() + out = self._cast(response.output, request, response) # rfc2616 section 4.3 if response.status in (100, 101, 204, 304) or request.method == 'HEAD': + if hasattr(out, 'close'): out.close() out = [] status = '%d %s' % (response.status, HTTP_CODES[response.status]) start_response(status, response.headerlist) @@ -577,8 +591,7 @@ class Bottle(object): except (KeyboardInterrupt, SystemExit, MemoryError): raise except Exception, e: - if not self.catchall: - raise + if not self.catchall: raise err = '<h1>Critical error while processing request: %s</h1>' \ % environ.get('PATH_INFO', '/') if DEBUG: @@ -587,6 +600,9 @@ class Bottle(object): environ['wsgi.errors'].write(err) #TODO: wsgi.error should not get html start_response('500 INTERNAL SERVER ERROR', [('Content-Type', 'text/html')]) return [tob(err)] + + def __call__(self, environ, start_response): + return self.wsgi(environ, start_response) class Request(threading.local, DictMixin): @@ -615,7 +631,7 @@ class Request(threading.local, DictMixin): def copy(self): ''' Returns a copy of self ''' return Request(self.environ.copy()) - + def path_shift(self, shift=1): ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa. |