Adding an on_route hook
This commit is contained in:
parent
5db48d30ce
commit
15892e5360
|
@ -9,17 +9,20 @@ There is nothing wrong with WSGI Middleware, and actually, it is really easy to
|
|||
use middleware with Pecan, but it is rather hard (sometimes impossible) to have
|
||||
access to Pecan's internals.
|
||||
|
||||
Hooks offer three ways of dealing with a request: *before*, *after* and
|
||||
*on_error* . Obviously,
|
||||
an *after* hook will be triggered once the request has been processed, a *before* hook will
|
||||
be dealt before anything and a *on_error* will be called when the request
|
||||
generated an error.
|
||||
Hooks offer four ways of dealing with a request:
|
||||
|
||||
* ``on_route``: called before Pecan attempts to route a request
|
||||
|
||||
* ``before``: called after routing but before processing the request
|
||||
|
||||
* ``after``: called after a request has been processed
|
||||
|
||||
* ``on_error``: called when a request generates an error
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
They are very easy to plug into Pecan. In the below example we will go through
|
||||
a simple hooks that will gather some information about the request and then it
|
||||
a simple hook that will gather some information about the request and then it
|
||||
will print it out to ``stdout``.
|
||||
|
||||
Your hook needs to import ``PecanHook`` which will be used to inherit from.
|
||||
|
|
|
@ -34,6 +34,9 @@ class HookController(object):
|
|||
class PecanHook(object):
|
||||
priority = 100
|
||||
|
||||
def on_route(self ,state):
|
||||
return
|
||||
|
||||
def before(self, state):
|
||||
return
|
||||
|
||||
|
@ -57,7 +60,7 @@ class TransactionHook(PecanHook):
|
|||
return True
|
||||
return False
|
||||
|
||||
def before(self, state):
|
||||
def on_route(self, state):
|
||||
state.request.error = False
|
||||
if self.is_transactional(state):
|
||||
state.request.transactional = True
|
||||
|
|
|
@ -94,16 +94,19 @@ class Pecan(MonitorableProcess):
|
|||
if not controller.pecan['check_permissions']():
|
||||
raise exc.HTTPUnauthorized
|
||||
|
||||
def determine_hooks(self, controller):
|
||||
def determine_hooks(self, controller=None):
|
||||
controller_hooks = []
|
||||
if controller:
|
||||
controller_hooks = controller.pecan.get('hooks', [])
|
||||
return list(
|
||||
sorted(
|
||||
chain(controller.pecan.get('hooks', []), self.hooks),
|
||||
chain(controller_hooks, self.hooks),
|
||||
lambda x,y: cmp(x.priority, y.priority)
|
||||
)
|
||||
)
|
||||
|
||||
def handle_hooks(self, hook_type, *args):
|
||||
if hook_type == 'before':
|
||||
if hook_type in ['before', 'on_route']:
|
||||
hooks = state.hooks
|
||||
else:
|
||||
hooks = reversed(state.hooks)
|
||||
|
@ -146,6 +149,13 @@ class Pecan(MonitorableProcess):
|
|||
return schema.to_python(to_validate)
|
||||
|
||||
def handle_request(self):
|
||||
|
||||
# get a sorted list of hooks, by priority (no controller hooks yet)
|
||||
state.hooks = self.determine_hooks()
|
||||
|
||||
# handle "on_route" hooks
|
||||
self.handle_hooks('on_route', state)
|
||||
|
||||
# lookup the controller, respecting content-type as requested
|
||||
# by the file extension on the URI
|
||||
path = state.request.path
|
||||
|
|
|
@ -17,6 +17,9 @@ class TestHooks(object):
|
|||
return 'Hello, World!'
|
||||
|
||||
class SimpleHook(PecanHook):
|
||||
def on_route(self, state):
|
||||
run_hook.append('on_route')
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('before')
|
||||
|
||||
|
@ -31,10 +34,11 @@ class TestHooks(object):
|
|||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
assert len(run_hook) == 3
|
||||
assert run_hook[0] == 'before'
|
||||
assert run_hook[1] == 'inside'
|
||||
assert run_hook[2] == 'after'
|
||||
assert len(run_hook) == 4
|
||||
assert run_hook[0] == 'on_route'
|
||||
assert run_hook[1] == 'before'
|
||||
assert run_hook[2] == 'inside'
|
||||
assert run_hook[3] == 'after'
|
||||
|
||||
def test_basic_multi_hook(self):
|
||||
run_hook = []
|
||||
|
@ -49,6 +53,9 @@ class TestHooks(object):
|
|||
def __init__(self, id):
|
||||
self.id = str(id)
|
||||
|
||||
def on_route(self, state):
|
||||
run_hook.append('on_route'+self.id)
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('before'+self.id)
|
||||
|
||||
|
@ -65,14 +72,17 @@ class TestHooks(object):
|
|||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
assert len(run_hook) == 7
|
||||
assert run_hook[0] == 'before1'
|
||||
assert run_hook[1] == 'before2'
|
||||
assert run_hook[2] == 'before3'
|
||||
assert run_hook[3] == 'inside'
|
||||
assert run_hook[4] == 'after3'
|
||||
assert run_hook[5] == 'after2'
|
||||
assert run_hook[6] == 'after1'
|
||||
assert len(run_hook) == 10
|
||||
assert run_hook[0] == 'on_route1'
|
||||
assert run_hook[1] == 'on_route2'
|
||||
assert run_hook[2] == 'on_route3'
|
||||
assert run_hook[3] == 'before1'
|
||||
assert run_hook[4] == 'before2'
|
||||
assert run_hook[5] == 'before3'
|
||||
assert run_hook[6] == 'inside'
|
||||
assert run_hook[7] == 'after3'
|
||||
assert run_hook[8] == 'after2'
|
||||
assert run_hook[9] == 'after1'
|
||||
|
||||
def test_prioritized_hooks(self):
|
||||
run_hook = []
|
||||
|
@ -87,6 +97,9 @@ class TestHooks(object):
|
|||
def __init__(self, id):
|
||||
self.id = str(id)
|
||||
|
||||
def on_route(self, state):
|
||||
run_hook.append('on_route'+self.id)
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('before'+self.id)
|
||||
|
||||
|
@ -104,16 +117,19 @@ class TestHooks(object):
|
|||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
assert len(run_hook) == 7
|
||||
assert run_hook[0] == 'before1'
|
||||
assert run_hook[1] == 'before2'
|
||||
assert run_hook[2] == 'before3'
|
||||
assert run_hook[3] == 'inside'
|
||||
assert run_hook[4] == 'after3'
|
||||
assert run_hook[5] == 'after2'
|
||||
assert run_hook[6] == 'after1'
|
||||
assert len(run_hook) == 10
|
||||
assert run_hook[0] == 'on_route1'
|
||||
assert run_hook[1] == 'on_route2'
|
||||
assert run_hook[2] == 'on_route3'
|
||||
assert run_hook[3] == 'before1'
|
||||
assert run_hook[4] == 'before2'
|
||||
assert run_hook[5] == 'before3'
|
||||
assert run_hook[6] == 'inside'
|
||||
assert run_hook[7] == 'after3'
|
||||
assert run_hook[8] == 'after2'
|
||||
assert run_hook[9] == 'after1'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
run_hook = []
|
||||
|
||||
state.app.hooks[0].priority = 3
|
||||
state.app.hooks[1].priority = 2
|
||||
|
@ -123,14 +139,17 @@ class TestHooks(object):
|
|||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
assert len(run_hook) == 7
|
||||
assert run_hook[0] == 'before3'
|
||||
assert run_hook[1] == 'before2'
|
||||
assert run_hook[2] == 'before1'
|
||||
assert run_hook[3] == 'inside'
|
||||
assert run_hook[4] == 'after1'
|
||||
assert run_hook[5] == 'after2'
|
||||
assert run_hook[6] == 'after3'
|
||||
assert len(run_hook) == 10
|
||||
assert run_hook[0] == 'on_route3'
|
||||
assert run_hook[1] == 'on_route2'
|
||||
assert run_hook[2] == 'on_route1'
|
||||
assert run_hook[3] == 'before3'
|
||||
assert run_hook[4] == 'before2'
|
||||
assert run_hook[5] == 'before1'
|
||||
assert run_hook[6] == 'inside'
|
||||
assert run_hook[7] == 'after1'
|
||||
assert run_hook[8] == 'after2'
|
||||
assert run_hook[9] == 'after3'
|
||||
|
||||
def test_transaction_hook(self):
|
||||
run_hook = []
|
||||
|
@ -145,19 +164,6 @@ class TestHooks(object):
|
|||
def error(self):
|
||||
return [][1]
|
||||
|
||||
class SimpleHook(PecanHook):
|
||||
def __init__(self, id):
|
||||
self.id = str(id)
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('before'+self.id)
|
||||
|
||||
def after(self, state):
|
||||
run_hook.append('after'+self.id)
|
||||
|
||||
def on_error(self, state, e):
|
||||
run_hook.append('error'+self.id)
|
||||
|
||||
def gen(event):
|
||||
return lambda: run_hook.append(event)
|
||||
|
||||
|
@ -180,7 +186,7 @@ class TestHooks(object):
|
|||
assert run_hook[1] == 'inside'
|
||||
assert run_hook[2] == 'clear'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
run_hook = []
|
||||
|
||||
response = app.post('/')
|
||||
assert response.status_int == 200
|
||||
|
@ -192,7 +198,7 @@ class TestHooks(object):
|
|||
assert run_hook[2] == 'commit'
|
||||
assert run_hook[3] == 'clear'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
run_hook = []
|
||||
try:
|
||||
response = app.post('/error')
|
||||
except IndexError:
|
||||
|
@ -207,6 +213,9 @@ class TestHooks(object):
|
|||
run_hook = []
|
||||
|
||||
class SimpleHook(PecanHook):
|
||||
def on_route(self, state):
|
||||
run_hook.append('on_route')
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('before')
|
||||
|
||||
|
@ -240,7 +249,7 @@ class TestHooks(object):
|
|||
assert len(run_hook) == 1
|
||||
assert run_hook[0] == 'inside'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
run_hook = []
|
||||
|
||||
response = app.get('/sub')
|
||||
assert response.status_int == 200
|
||||
|
@ -258,6 +267,9 @@ class TestHooks(object):
|
|||
def __init__(self, id):
|
||||
self.id = str(id)
|
||||
|
||||
def on_route(self, state):
|
||||
run_hook.append('on_route'+self.id)
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('before'+self.id)
|
||||
|
||||
|
@ -288,23 +300,25 @@ class TestHooks(object):
|
|||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
assert len(run_hook) == 3
|
||||
assert run_hook[0] == 'before1'
|
||||
assert run_hook[1] == 'inside'
|
||||
assert run_hook[2] == 'after1'
|
||||
assert len(run_hook) == 4
|
||||
assert run_hook[0] == 'on_route1'
|
||||
assert run_hook[1] == 'before1'
|
||||
assert run_hook[2] == 'inside'
|
||||
assert run_hook[3] == 'after1'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
run_hook = []
|
||||
|
||||
response = app.get('/sub')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Inside here!'
|
||||
|
||||
assert len(run_hook) == 5
|
||||
assert run_hook[0] == 'before2'
|
||||
assert run_hook[1] == 'before1'
|
||||
assert run_hook[2] == 'inside_sub'
|
||||
assert run_hook[3] == 'after1'
|
||||
assert run_hook[4] == 'after2'
|
||||
assert len(run_hook) == 6
|
||||
assert run_hook[0] == 'on_route1'
|
||||
assert run_hook[1] == 'before2'
|
||||
assert run_hook[2] == 'before1'
|
||||
assert run_hook[3] == 'inside_sub'
|
||||
assert run_hook[4] == 'after1'
|
||||
assert run_hook[5] == 'after2'
|
||||
|
||||
def test_hooks_with_validation(self):
|
||||
run_hook = []
|
||||
|
@ -322,6 +336,9 @@ class TestHooks(object):
|
|||
]
|
||||
|
||||
class SimpleHook(PecanHook):
|
||||
def on_route(self, state):
|
||||
run_hook.append('on_route')
|
||||
|
||||
def before(self, state):
|
||||
run_hook.append('before')
|
||||
|
||||
|
@ -372,11 +389,12 @@ class TestHooks(object):
|
|||
))
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'False'
|
||||
assert len(run_hook) == 3
|
||||
assert run_hook[0] == 'before'
|
||||
assert run_hook[1] == 'inside'
|
||||
assert run_hook[2] == 'after'
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
assert len(run_hook) == 4
|
||||
assert run_hook[0] == 'on_route'
|
||||
assert run_hook[1] == 'before'
|
||||
assert run_hook[2] == 'inside'
|
||||
assert run_hook[3] == 'after'
|
||||
run_hook = []
|
||||
|
||||
# test that the hooks get properly run with validation errors
|
||||
app = TestApp(make_app(RootController(), hooks=[SimpleHook()]))
|
||||
|
@ -391,11 +409,12 @@ class TestHooks(object):
|
|||
))
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'True'
|
||||
assert len(run_hook) == 3
|
||||
assert run_hook[0] == 'before'
|
||||
assert run_hook[1] == 'inside'
|
||||
assert run_hook[2] == 'after'
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
assert len(run_hook) == 4
|
||||
assert run_hook[0] == 'on_route'
|
||||
assert run_hook[1] == 'before'
|
||||
assert run_hook[2] == 'inside'
|
||||
assert run_hook[3] == 'after'
|
||||
run_hook = []
|
||||
|
||||
# test that the hooks get properly run with validation errors
|
||||
# and an error handler
|
||||
|
@ -411,10 +430,11 @@ class TestHooks(object):
|
|||
))
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'errors'
|
||||
assert len(run_hook) == 5
|
||||
assert run_hook[0] == 'before'
|
||||
assert run_hook[1] == 'after'
|
||||
assert run_hook[2] == 'before'
|
||||
assert run_hook[3] == 'inside'
|
||||
assert run_hook[4] == 'after'
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
assert len(run_hook) == 7
|
||||
assert run_hook[0] == 'on_route'
|
||||
assert run_hook[1] == 'before'
|
||||
assert run_hook[2] == 'after'
|
||||
assert run_hook[3] == 'on_route'
|
||||
assert run_hook[4] == 'before'
|
||||
assert run_hook[5] == 'inside'
|
||||
assert run_hook[6] == 'after'
|
||||
|
|
Loading…
Reference in New Issue