Some reorganization, more tests, improved handling of errors, and
support for prioritized hooks.
This commit is contained in:
@@ -2,7 +2,7 @@ Metadata-Version: 1.0
|
||||
Name: pecan
|
||||
Version: 0.1dev
|
||||
Summary: A WSGI object-dispatching web framework, in the spirit of TurboGears, only much much smaller, with many fewer dependancies.
|
||||
Home-page: http://code.google.com/p/pecan
|
||||
Home-page: http://sf.net/p/pecan
|
||||
Author: Jonathan LaCour
|
||||
Author-email: jonathan@cleverdevil.org
|
||||
License: BSD
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
README
|
||||
setup.cfg
|
||||
setup.py
|
||||
pecan/__init__.py
|
||||
pecan/decorators.py
|
||||
pecan/hooks.py
|
||||
pecan/jsonify.py
|
||||
pecan/pecan.py
|
||||
pecan/routing.py
|
||||
pecan/secure.py
|
||||
pecan/templating.py
|
||||
pecan.egg-info/PKG-INFO
|
||||
pecan.egg-info/SOURCES.txt
|
||||
@@ -11,4 +15,5 @@ pecan.egg-info/dependency_links.txt
|
||||
pecan.egg-info/entry_points.txt
|
||||
pecan.egg-info/requires.txt
|
||||
pecan.egg-info/top_level.txt
|
||||
pecan.egg-info/zip-safe
|
||||
pecan.egg-info/zip-safe
|
||||
tests/templates/__init__.py
|
||||
@@ -1,6 +1,8 @@
|
||||
WebOb >= 0.9.8
|
||||
simplejson >= 2.0.9
|
||||
simplegeneric >= 0.7
|
||||
Genshi >= 0.6
|
||||
Kajiki >= 0.2.2
|
||||
Mako >= 0.3
|
||||
Mako >= 0.3
|
||||
py >= 1.3.4
|
||||
WebTest >= 1.2.2
|
||||
Paste >= 1.7.5.1
|
||||
@@ -1 +1,2 @@
|
||||
tests
|
||||
pecan
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
from paste.urlparser import StaticURLParser
|
||||
from paste.cascade import Cascade
|
||||
|
||||
from pecan import Pecan, request, override_template
|
||||
from pecan import Pecan, request, response, override_template
|
||||
from decorators import expose
|
||||
|
||||
__all__ = [
|
||||
'make_app', 'Pecan', 'request', 'response', 'override_template', 'expose'
|
||||
]
|
||||
|
||||
def make_app(root, renderers = None,
|
||||
default_renderer = None,
|
||||
template_path = None,
|
||||
hooks = None,
|
||||
static_root = None):
|
||||
|
||||
kw = {}
|
||||
if renderers is not None: kw['renderers'] = renderers
|
||||
if default_renderer is not None: kw['default_renderer'] = default_renderer
|
||||
if template_path is not None: kw['template_path'] = template_path
|
||||
if hooks is not None: kw['hooks'] = hooks
|
||||
|
||||
|
||||
def make_app(root, static_root=None, **kw):
|
||||
app = Pecan(root, **kw)
|
||||
|
||||
if static_root:
|
||||
app = Cascade([StaticURLParser(static_root), app])
|
||||
|
||||
return app
|
||||
@@ -1,4 +1,39 @@
|
||||
from inspect import getmembers, ismethod
|
||||
from routing import iscontroller
|
||||
|
||||
|
||||
__all__ = ['PecanHook', 'TransactionHook', 'HookController']
|
||||
|
||||
|
||||
def walk_controller(root_class, controller, hooks):
|
||||
if hasattr(controller, '_lookup'):
|
||||
# TODO: what about this?
|
||||
pass
|
||||
|
||||
if not isinstance(controller, (int, dict)):
|
||||
for name, value in controller.__dict__.iteritems():
|
||||
if name == 'controller': continue
|
||||
if name.startswith('__') and name.endswith('__'): continue
|
||||
|
||||
if iscontroller(value):
|
||||
for hook in hooks:
|
||||
value.pecan.setdefault('hooks', []).append(hook)
|
||||
elif hasattr(value, '__class__'):
|
||||
if name.startswith('__') and name.endswith('__'): continue
|
||||
walk_controller(root_class, value, hooks)
|
||||
|
||||
|
||||
class HookController(object):
|
||||
__hooks__ = []
|
||||
|
||||
class __metaclass__(type):
|
||||
def __init__(cls, name, bases, dict_):
|
||||
walk_controller(cls, cls, dict_['__hooks__'])
|
||||
|
||||
|
||||
class PecanHook(object):
|
||||
priority = 100
|
||||
|
||||
def before(self, state):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
try:
|
||||
from json import JSONEncoder, dumps
|
||||
except ImportError:
|
||||
from simplejson import JSONEncoder, dumps
|
||||
from simplejson import JSONEncoder, dumps
|
||||
|
||||
from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
|
||||
158
pecan/pecan.py
158
pecan/pecan.py
@@ -1,22 +1,27 @@
|
||||
from templating import renderers
|
||||
from webob import Request, Response, exc
|
||||
from threading import local
|
||||
from routing import lookup_controller
|
||||
|
||||
import string
|
||||
from webob import Request, Response, exc
|
||||
from threading import local
|
||||
from itertools import chain
|
||||
|
||||
|
||||
state = local()
|
||||
|
||||
|
||||
class RequestWrapper(object):
|
||||
def __getattr__(self, attr):
|
||||
return getattr(state.request, attr)
|
||||
def __setattr__(self, attr, value):
|
||||
return setattr(state.request, attr, value)
|
||||
def proxy(key):
|
||||
class ObjectProxy(object):
|
||||
def __getattr__(self, attr):
|
||||
obj = getattr(state, key)
|
||||
return getattr(obj, attr)
|
||||
def __setattr__(self, attr, value):
|
||||
obj = getattr(state, key)
|
||||
return setattr(obj, attr, value)
|
||||
return ObjectProxy()
|
||||
|
||||
|
||||
request = RequestWrapper()
|
||||
request = proxy('request')
|
||||
response = proxy('response')
|
||||
|
||||
|
||||
def override_template(template):
|
||||
@@ -35,10 +40,6 @@ class Pecan(object):
|
||||
self.default_renderer = default_renderer
|
||||
self.hooks = hooks
|
||||
self.template_path = template_path
|
||||
self.translate = string.maketrans(
|
||||
string.punctuation,
|
||||
'_' * len(string.punctuation)
|
||||
)
|
||||
|
||||
def get_content_type(self, format):
|
||||
return {
|
||||
@@ -51,71 +52,100 @@ class Pecan(object):
|
||||
path = path.split('/')[1:]
|
||||
node, remainder = lookup_controller(node, path)
|
||||
return node
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# create the request object
|
||||
state.request = Request(environ)
|
||||
|
||||
# lookup the controller
|
||||
|
||||
def handle_security(self, controller):
|
||||
if controller.pecan.get('secured', False):
|
||||
if not controller.pecan['check_permissions']():
|
||||
raise exc.HTTPUnauthorized
|
||||
|
||||
def determine_hooks(self, controller):
|
||||
return list(
|
||||
sorted(
|
||||
chain(controller.pecan.get('hooks', []), self.hooks),
|
||||
lambda x,y: cmp(x.priority, y.priority)
|
||||
)
|
||||
)
|
||||
|
||||
def handle_hooks(self, hook_type, *args):
|
||||
for hook in state.hooks:
|
||||
getattr(hook, hook_type)(*args)
|
||||
|
||||
def handle_request(self):
|
||||
# lookup the controller, respecting content-type as requested
|
||||
# by the file extension on the URI
|
||||
path = state.request.path
|
||||
content_type = None
|
||||
if '.' in path.split('/')[-1]:
|
||||
path, format = path.split('.')
|
||||
override_content_type = True
|
||||
content_type = self.get_content_type(format)
|
||||
controller = self.route(self.root, path)
|
||||
|
||||
# if we didn't find a controller, issue a 404
|
||||
if controller is None:
|
||||
response = Response()
|
||||
response.status = 404
|
||||
return response(environ, start_response)
|
||||
|
||||
# handle security
|
||||
if controller.pecan.get('secured', False):
|
||||
if not controller.pecan['check_permissions']():
|
||||
raise exc.HTTPUnauthorized
|
||||
|
||||
|
||||
# determine content type
|
||||
if content_type is None:
|
||||
content_type = controller.pecan.get('content_type', 'text/html')
|
||||
|
||||
# handle security
|
||||
self.handle_security(controller)
|
||||
|
||||
# get a sorted list of hooks, by priority
|
||||
state.hooks = self.determine_hooks(controller)
|
||||
|
||||
# handle "before" hooks
|
||||
for hook in self.hooks:
|
||||
hook.before(state)
|
||||
|
||||
self.handle_hooks('before', state)
|
||||
|
||||
# get the result from the controller, properly handling wrap hooks
|
||||
try:
|
||||
result = controller(**dict(state.request.str_params))
|
||||
|
||||
# pull the template out based upon content type
|
||||
template = controller.pecan.get('content_types', {}).get(content_type)
|
||||
result = controller(**dict(state.request.str_params))
|
||||
|
||||
# pull the template out based upon content type and handle overrides
|
||||
template = controller.pecan.get('content_types', {}).get(content_type)
|
||||
template = getattr(request, 'override_template', template)
|
||||
|
||||
# handle template overrides
|
||||
template = getattr(request, 'override_template', template)
|
||||
# if there is a template, render it
|
||||
if template:
|
||||
renderer = self.renderers.get(self.default_renderer, self.template_path)
|
||||
if template == 'json':
|
||||
renderer = self.renderers.get('json', self.template_path)
|
||||
elif ':' in template:
|
||||
renderer = self.renderers.get(template.split(':')[0], self.template_path)
|
||||
template = template.split(':')[1]
|
||||
result = renderer.render(template, result)
|
||||
content_type = renderer.content_type
|
||||
|
||||
if template:
|
||||
renderer = self.renderers.get(self.default_renderer, self.template_path)
|
||||
if template == 'json':
|
||||
renderer = self.renderers.get('json', self.template_path)
|
||||
elif ':' in template:
|
||||
renderer = self.renderers.get(template.split(':')[0], self.template_path)
|
||||
template = template.split(':')[1]
|
||||
result = renderer.render(template, result)
|
||||
content_type = renderer.content_type
|
||||
|
||||
response = Response(result)
|
||||
if content_type:
|
||||
response.content_type = content_type
|
||||
except Exception, e:
|
||||
# handle "error" hooks
|
||||
for hook in self.hooks:
|
||||
hook.on_error(state, e)
|
||||
raise
|
||||
# set the body content
|
||||
if isinstance(result, unicode):
|
||||
state.response.unicode_body = result
|
||||
else:
|
||||
return response(environ, start_response)
|
||||
state.response.body = result
|
||||
|
||||
# set the content type
|
||||
if content_type:
|
||||
state.response.content_type = content_type
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# create the request and response object
|
||||
state.request = Request(environ)
|
||||
state.response = Response()
|
||||
state.hooks = []
|
||||
|
||||
# handle the request
|
||||
try:
|
||||
self.handle_request()
|
||||
except Exception, e:
|
||||
# if this is an HTTP Exception, set it as the response
|
||||
if isinstance(e, exc.HTTPException):
|
||||
state.response = e
|
||||
|
||||
# handle "error" hooks
|
||||
self.handle_hooks('on_error', state, e)
|
||||
finally:
|
||||
# handle "after" hooks
|
||||
for hook in self.hooks:
|
||||
hook.after(state)
|
||||
del state.request
|
||||
self.handle_hooks('after', state)
|
||||
|
||||
# get the response
|
||||
try:
|
||||
return state.response(environ, start_response)
|
||||
finally:
|
||||
# clean up state
|
||||
del state.request
|
||||
del state.response
|
||||
del state.hooks
|
||||
@@ -1,4 +1,3 @@
|
||||
from webob.exc import HTTPUnauthorized
|
||||
from inspect import getmembers, ismethod
|
||||
|
||||
from routing import iscontroller
|
||||
|
||||
17
setup.py
17
setup.py
@@ -1,8 +1,18 @@
|
||||
from setuptools import setup, find_packages
|
||||
import sys, os
|
||||
from setuptools import setup, Command, find_packages
|
||||
import sys, os, py
|
||||
|
||||
version = '0.1'
|
||||
|
||||
class PyTest(Command):
|
||||
user_options = []
|
||||
def initialize_options(self):
|
||||
pass
|
||||
def finalize_options(self):
|
||||
pass
|
||||
def run(self):
|
||||
import py
|
||||
py.cmdline.pytest(py.std.sys.argv[2:])
|
||||
|
||||
setup(
|
||||
name = 'pecan',
|
||||
version = version,
|
||||
@@ -17,6 +27,7 @@ setup(
|
||||
packages = find_packages(exclude=['ez_setup', 'examples', 'tests']),
|
||||
include_package_data = True,
|
||||
zip_safe = True,
|
||||
cmdclass = {'test': PyTest},
|
||||
install_requires=[
|
||||
"WebOb >= 0.9.8",
|
||||
"simplegeneric >= 0.7",
|
||||
@@ -30,4 +41,4 @@ setup(
|
||||
entry_points = """
|
||||
# -*- Entry points: -*-
|
||||
""",
|
||||
)
|
||||
)
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
@@ -1,4 +1,4 @@
|
||||
from pecan import Pecan, expose
|
||||
from pecan import Pecan, expose, request, response
|
||||
from webtest import TestApp
|
||||
|
||||
class TestBase(object):
|
||||
@@ -10,17 +10,17 @@ class TestBase(object):
|
||||
return 'Hello, World!'
|
||||
|
||||
app = TestApp(Pecan(RootController()))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
r = app.get('/')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'Hello, World!'
|
||||
|
||||
response = app.get('/index')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
r = app.get('/index')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'Hello, World!'
|
||||
|
||||
response = app.get('/index.html')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
r = app.get('/index.html')
|
||||
assert r.status_int == 200
|
||||
assert r.body == 'Hello, World!'
|
||||
|
||||
def test_object_dispatch(self):
|
||||
class SubSubController(object):
|
||||
@@ -56,9 +56,9 @@ class TestBase(object):
|
||||
|
||||
app = TestApp(Pecan(RootController()))
|
||||
for path in ('/', '/deeper', '/sub', '/sub/deeper', '/sub/sub', '/sub/sub/deeper'):
|
||||
response = app.get(path)
|
||||
assert response.status_int == 200
|
||||
assert response.body == path
|
||||
r = app.get(path)
|
||||
assert r.status_int == 200
|
||||
assert r.body == path
|
||||
|
||||
def test_lookup(self):
|
||||
class LookupController(object):
|
||||
@@ -83,17 +83,17 @@ class TestBase(object):
|
||||
return LookupController(someID), remainder
|
||||
|
||||
app = TestApp(Pecan(RootController()))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert response.body == '/'
|
||||
r = app.get('/')
|
||||
assert r.status_int == 200
|
||||
assert r.body == '/'
|
||||
|
||||
response = app.get('/100')
|
||||
assert response.status_int == 200
|
||||
assert response.body == '/100'
|
||||
r = app.get('/100')
|
||||
assert r.status_int == 200
|
||||
assert r.body == '/100'
|
||||
|
||||
response = app.get('/100/name')
|
||||
assert response.status_int == 200
|
||||
assert response.body == '/100/name'
|
||||
r = app.get('/100/name')
|
||||
assert r.status_int == 200
|
||||
assert r.body == '/100/name'
|
||||
|
||||
|
||||
class TestEngines(object):
|
||||
@@ -105,14 +105,14 @@ class TestEngines(object):
|
||||
return dict(name=name)
|
||||
|
||||
app = TestApp(Pecan(RootController(), template_path='tests/templates'))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert "<h1>Hello, Jonathan!</h1>" in response.body
|
||||
r = app.get('/')
|
||||
assert r.status_int == 200
|
||||
assert "<h1>Hello, Jonathan!</h1>" in r.body
|
||||
|
||||
app = TestApp(Pecan(RootController(), template_path='tests/templates'))
|
||||
response = app.get('/index.html?name=World')
|
||||
assert response.status_int == 200
|
||||
assert "<h1>Hello, World!</h1>" in response.body
|
||||
r = app.get('/index.html?name=World')
|
||||
assert r.status_int == 200
|
||||
assert "<h1>Hello, World!</h1>" in r.body
|
||||
|
||||
def test_kajiki(self):
|
||||
class RootController(object):
|
||||
@@ -121,14 +121,14 @@ class TestEngines(object):
|
||||
return dict(name=name)
|
||||
|
||||
app = TestApp(Pecan(RootController(), template_path='tests/templates'))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert "<h1>Hello, Jonathan!</h1>" in response.body
|
||||
r = app.get('/')
|
||||
assert r.status_int == 200
|
||||
assert "<h1>Hello, Jonathan!</h1>" in r.body
|
||||
|
||||
app = TestApp(Pecan(RootController(), template_path='tests/templates'))
|
||||
response = app.get('/index.html?name=World')
|
||||
assert response.status_int == 200
|
||||
assert "<h1>Hello, World!</h1>" in response.body
|
||||
r = app.get('/index.html?name=World')
|
||||
assert r.status_int == 200
|
||||
assert "<h1>Hello, World!</h1>" in r.body
|
||||
|
||||
def test_mako(self):
|
||||
class RootController(object):
|
||||
@@ -137,14 +137,14 @@ class TestEngines(object):
|
||||
return dict(name=name)
|
||||
|
||||
app = TestApp(Pecan(RootController(), template_path='tests/templates'))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert "<h1>Hello, Jonathan!</h1>" in response.body
|
||||
r = app.get('/')
|
||||
assert r.status_int == 200
|
||||
assert "<h1>Hello, Jonathan!</h1>" in r.body
|
||||
|
||||
app = TestApp(Pecan(RootController(), template_path='tests/templates'))
|
||||
response = app.get('/index.html?name=World')
|
||||
assert response.status_int == 200
|
||||
assert "<h1>Hello, World!</h1>" in response.body
|
||||
r = app.get('/index.html?name=World')
|
||||
assert r.status_int == 200
|
||||
assert "<h1>Hello, World!</h1>" in r.body
|
||||
|
||||
def test_json(self):
|
||||
from simplejson import loads
|
||||
@@ -157,7 +157,7 @@ class TestEngines(object):
|
||||
return expected_result
|
||||
|
||||
app = TestApp(Pecan(RootController()))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
result = dict(loads(response.body))
|
||||
r = app.get('/')
|
||||
assert r.status_int == 200
|
||||
result = dict(loads(r.body))
|
||||
assert result == expected_result
|
||||
@@ -1,5 +1,5 @@
|
||||
from pecan import Pecan, expose
|
||||
from pecan.hooks import PecanHook, TransactionHook
|
||||
from pecan.hooks import PecanHook, TransactionHook, HookController
|
||||
from webtest import TestApp
|
||||
|
||||
|
||||
@@ -72,6 +72,64 @@ class TestHooks(object):
|
||||
assert run_hook[5] == 'after2'
|
||||
assert run_hook[6] == 'after3'
|
||||
|
||||
def test_prioritized_hooks(self):
|
||||
run_hook = []
|
||||
|
||||
class RootController(object):
|
||||
@expose()
|
||||
def index(self):
|
||||
run_hook.append('inside')
|
||||
return 'Hello, World!'
|
||||
|
||||
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)
|
||||
|
||||
papp = Pecan(RootController(), hooks=[
|
||||
SimpleHook(1), SimpleHook(2), SimpleHook(3)
|
||||
])
|
||||
app = TestApp(papp)
|
||||
response = app.get('/')
|
||||
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] == 'after1'
|
||||
assert run_hook[5] == 'after2'
|
||||
assert run_hook[6] == 'after3'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
|
||||
papp.hooks[0].priority = 3
|
||||
papp.hooks[1].priority = 2
|
||||
papp.hooks[2].priority = 1
|
||||
|
||||
response = app.get('/')
|
||||
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] == 'after3'
|
||||
assert run_hook[5] == 'after2'
|
||||
assert run_hook[6] == 'after1'
|
||||
|
||||
def test_transaction_hook(self):
|
||||
run_hook = []
|
||||
|
||||
@@ -142,4 +200,106 @@ class TestHooks(object):
|
||||
assert run_hook[0] == 'start'
|
||||
assert run_hook[1] == 'rollback'
|
||||
assert run_hook[2] == 'clear'
|
||||
|
||||
|
||||
def test_basic_isolated_hook(self):
|
||||
run_hook = []
|
||||
|
||||
class SimpleHook(PecanHook):
|
||||
def before(self, state):
|
||||
run_hook.append('before')
|
||||
|
||||
def after(self, state):
|
||||
run_hook.append('after')
|
||||
|
||||
def on_error(self, state, e):
|
||||
run_hook.append('error')
|
||||
|
||||
class SubController(HookController):
|
||||
__hooks__ = [SimpleHook()]
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
run_hook.append('inside_sub')
|
||||
return 'Inside here!'
|
||||
|
||||
class RootController(object):
|
||||
@expose()
|
||||
def index(self):
|
||||
run_hook.append('inside')
|
||||
return 'Hello, World!'
|
||||
|
||||
sub = SubController()
|
||||
|
||||
app = TestApp(Pecan(RootController()))
|
||||
response = app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Hello, World!'
|
||||
|
||||
assert len(run_hook) == 1
|
||||
assert run_hook[0] == 'inside'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
|
||||
response = app.get('/sub')
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Inside here!'
|
||||
|
||||
assert len(run_hook) == 3
|
||||
assert run_hook[0] == 'before'
|
||||
assert run_hook[1] == 'inside_sub'
|
||||
assert run_hook[2] == 'after'
|
||||
|
||||
def test_isolated_hook_with_global_hook(self):
|
||||
run_hook = []
|
||||
|
||||
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)
|
||||
|
||||
class SubController(HookController):
|
||||
__hooks__ = [SimpleHook(2)]
|
||||
|
||||
@expose()
|
||||
def index(self):
|
||||
run_hook.append('inside_sub')
|
||||
return 'Inside here!'
|
||||
|
||||
class RootController(object):
|
||||
@expose()
|
||||
def index(self):
|
||||
run_hook.append('inside')
|
||||
return 'Hello, World!'
|
||||
|
||||
sub = SubController()
|
||||
|
||||
app = TestApp(Pecan(RootController(), hooks=[SimpleHook(1)]))
|
||||
response = app.get('/')
|
||||
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'
|
||||
|
||||
for i in range(len(run_hook)): run_hook.pop()
|
||||
|
||||
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] == 'after2'
|
||||
assert run_hook[4] == 'after1'
|
||||
@@ -1,7 +1,7 @@
|
||||
from pecan import expose, make_app
|
||||
from pecan.secure import secure, unlocked, SecureController
|
||||
from webob import exc
|
||||
from webtest import TestApp
|
||||
from webtest import TestApp, AppError
|
||||
from py.test import raises
|
||||
|
||||
class TestSecure(object):
|
||||
@@ -50,11 +50,11 @@ class TestSecure(object):
|
||||
assert response.status_int == 200
|
||||
assert response.body == 'Sure thing'
|
||||
|
||||
with raises(exc.HTTPUnauthorized):
|
||||
response = app.get('/locked')
|
||||
response = app.get('/locked', expect_errors=True)
|
||||
assert response.status_int == 401
|
||||
|
||||
with raises(exc.HTTPUnauthorized):
|
||||
response = app.get('/secret')
|
||||
response = app.get('/secret', expect_errors=True)
|
||||
assert response.status_int == 401
|
||||
|
||||
response = app.get('/secret/allowed')
|
||||
assert response.status_int == 200
|
||||
|
||||
Reference in New Issue
Block a user