Provide pecan.state.arguments for inspecting controller call arguments

Change-Id: Ibbd8b2f075a875b109c7309bc42e0d1f1d5ae610
This commit is contained in:
Ryan Petrello
2014-07-21 23:43:26 -04:00
parent 236301f6d7
commit 01c9a110fc
3 changed files with 388 additions and 13 deletions

View File

@@ -71,6 +71,10 @@ response objects, and which controller was selected by Pecan's routing::
# and used to generate the response body
#
assert state.controller.__func__ is RootController.index.__func__
assert isinstance(state.arguments, inspect.Arguments)
print state.arguments.args
print state.arguments.varargs
print state.arguments.keywords
assert isinstance(state.request, webob.Request)
assert isinstance(state.response, webob.Response)
assert isinstance(state.hooks, list)

View File

@@ -2,6 +2,7 @@ try:
from simplejson import dumps, loads
except ImportError: # pragma: no cover
from json import dumps, loads # noqa
from inspect import Arguments
from itertools import chain, tee
from mimetypes import guess_type, add_type
from os.path import splitext
@@ -31,12 +32,14 @@ logger = logging.getLogger(__name__)
class RoutingState(object):
def __init__(self, request, response, app, hooks=[], controller=None):
def __init__(self, request, response, app, hooks=[], controller=None,
arguments=None):
self.request = request
self.response = response
self.app = app
self.hooks = hooks
self.controller = controller
self.arguments = arguments
class Request(WebObRequest):
@@ -326,6 +329,7 @@ class PecanBase(object):
passed the argument specification for the controller.
'''
args = []
varargs = []
kwargs = dict()
valid_args = argspec.args[1:] # pop off `self`
pecan_state = state.request.pecan
@@ -354,7 +358,7 @@ class PecanBase(object):
if [i for i in remainder if i]:
if not argspec[1]:
abort(404)
args.extend(remainder)
varargs.extend(remainder)
# get the default positional arguments
if argspec[3]:
@@ -377,7 +381,7 @@ class PecanBase(object):
if name not in argspec[0]:
kwargs[encode_if_needed(name)] = value
return args, kwargs
return args, varargs, kwargs
def render(self, template, namespace):
renderer = self.renderers.get(
@@ -492,9 +496,6 @@ class PecanBase(object):
)
raise exc.HTTPNotFound
# handle "before" hooks
self.handle_hooks(self.determine_hooks(controller), 'before', state)
# fetch any parameters
if req.method == 'GET':
params = dict(req.GET)
@@ -502,15 +503,19 @@ class PecanBase(object):
params = dict(req.params)
# fetch the arguments for the controller
args, kwargs = self.get_args(
args, varargs, kwargs = self.get_args(
state,
params,
remainder,
cfg['argspec'],
im_self
)
state.arguments = Arguments(args, varargs, kwargs)
return controller, args, kwargs
# handle "before" hooks
self.handle_hooks(self.determine_hooks(controller), 'before', state)
return controller, args+varargs, kwargs
def invoke_controller(self, controller, args, kwargs, state):
'''
@@ -691,11 +696,11 @@ class ExplicitPecan(PecanBase):
except IndexError:
raise signature_error
args, kwargs = super(ExplicitPecan, self).get_args(
args, varargs, kwargs = super(ExplicitPecan, self).get_args(
state, all_params, remainder, argspec, im_self
)
args = [state.request, state.response] + args
return args, kwargs
return args, varargs, kwargs
class Pecan(PecanBase):
@@ -747,12 +752,14 @@ class Pecan(PecanBase):
state.hooks = []
state.app = self
state.controller = None
state.arguments = None
return super(Pecan, self).__call__(environ, start_response)
finally:
del state.hooks
del state.request
del state.response
del state.controller
del state.arguments
del state.app
def init_context_local(self, local_factory):
@@ -766,6 +773,7 @@ class Pecan(PecanBase):
state.response = _state.response
controller, args, kw = super(Pecan, self).find_controller(_state)
state.controller = controller
state.arguments = _state.arguments
return controller, args, kw
def handle_hooks(self, hooks, *args, **kw):

View File

@@ -1,11 +1,13 @@
import inspect
import operator
from webtest import TestApp
from six import PY3
from six import b as b_
from six import u as u_
from six.moves import cStringIO as StringIO
from webob import Response
from pecan import make_app, expose, redirect, abort
from pecan import make_app, expose, redirect, abort, rest, Request, Response
from pecan.hooks import (
PecanHook, TransactionHook, HookController, RequestViewerHook
)
@@ -13,6 +15,9 @@ from pecan.configuration import Config
from pecan.decorators import transactional, after_commit, after_rollback
from pecan.tests import PecanTestCase
# The `inspect.Arguments` namedtuple is different between PY2/3
kwargs = operator.attrgetter('varkw' if PY3 else 'keywords')
class TestHooks(PecanTestCase):
@@ -412,6 +417,364 @@ class TestHooks(PecanTestCase):
assert run_hook[3] == 'last - before hook', run_hook[3]
class TestStateAccess(PecanTestCase):
def setUp(self):
super(TestStateAccess, self).setUp()
self.args = None
class RootController(object):
@expose()
def index(self):
return 'Hello, World!'
@expose()
def greet(self, name):
return 'Hello, %s!' % name
@expose()
def greetmore(self, *args):
return 'Hello, %s!' % args[0]
@expose()
def kwargs(self, **kw):
return 'Hello, %s!' % kw['name']
@expose()
def mixed(self, first, second, *args):
return 'Mixed'
class SimpleHook(PecanHook):
def before(inself, state):
self.args = (state.controller, state.arguments)
self.root = RootController()
self.app = TestApp(make_app(self.root, hooks=[SimpleHook()]))
def test_no_args(self):
self.app.get('/')
assert self.args[0] == self.root.index
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_single_arg(self):
self.app.get('/greet/joe')
assert self.args[0] == self.root.greet
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['joe']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_single_vararg(self):
self.app.get('/greetmore/joe')
assert self.args[0] == self.root.greetmore
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == ['joe']
assert kwargs(self.args[1]) == {}
def test_single_kw(self):
self.app.get('/kwargs/?name=joe')
assert self.args[0] == self.root.kwargs
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'name': 'joe'}
def test_single_kw_post(self):
self.app.post('/kwargs/', params={'name': 'joe'})
assert self.args[0] == self.root.kwargs
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'name': 'joe'}
def test_mixed_args(self):
self.app.get('/mixed/foo/bar/spam/eggs')
assert self.args[0] == self.root.mixed
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['foo', 'bar']
assert self.args[1].varargs == ['spam', 'eggs']
class TestStateAccessWithoutThreadLocals(PecanTestCase):
def setUp(self):
super(TestStateAccessWithoutThreadLocals, self).setUp()
self.args = None
class RootController(object):
@expose()
def index(self, req, resp):
return 'Hello, World!'
@expose()
def greet(self, req, resp, name):
return 'Hello, %s!' % name
@expose()
def greetmore(self, req, resp, *args):
return 'Hello, %s!' % args[0]
@expose()
def kwargs(self, req, resp, **kw):
return 'Hello, %s!' % kw['name']
@expose()
def mixed(self, req, resp, first, second, *args):
return 'Mixed'
class SimpleHook(PecanHook):
def before(inself, state):
self.args = (state.controller, state.arguments)
self.root = RootController()
self.app = TestApp(make_app(
self.root,
hooks=[SimpleHook()],
use_context_locals=False
))
def test_no_args(self):
self.app.get('/')
assert self.args[0] == self.root.index
assert isinstance(self.args[1], inspect.Arguments)
assert len(self.args[1].args) == 2
assert isinstance(self.args[1].args[0], Request)
assert isinstance(self.args[1].args[1], Response)
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_single_arg(self):
self.app.get('/greet/joe')
assert self.args[0] == self.root.greet
assert isinstance(self.args[1], inspect.Arguments)
assert len(self.args[1].args) == 3
assert isinstance(self.args[1].args[0], Request)
assert isinstance(self.args[1].args[1], Response)
assert self.args[1].args[2] == 'joe'
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_single_vararg(self):
self.app.get('/greetmore/joe')
assert self.args[0] == self.root.greetmore
assert isinstance(self.args[1], inspect.Arguments)
assert len(self.args[1].args) == 2
assert isinstance(self.args[1].args[0], Request)
assert isinstance(self.args[1].args[1], Response)
assert self.args[1].varargs == ['joe']
assert kwargs(self.args[1]) == {}
def test_single_kw(self):
self.app.get('/kwargs/?name=joe')
assert self.args[0] == self.root.kwargs
assert isinstance(self.args[1], inspect.Arguments)
assert len(self.args[1].args) == 2
assert isinstance(self.args[1].args[0], Request)
assert isinstance(self.args[1].args[1], Response)
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'name': 'joe'}
def test_single_kw_post(self):
self.app.post('/kwargs/', params={'name': 'joe'})
assert self.args[0] == self.root.kwargs
assert isinstance(self.args[1], inspect.Arguments)
assert len(self.args[1].args) == 2
assert isinstance(self.args[1].args[0], Request)
assert isinstance(self.args[1].args[1], Response)
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'name': 'joe'}
def test_mixed_args(self):
self.app.get('/mixed/foo/bar/spam/eggs')
assert self.args[0] == self.root.mixed
assert isinstance(self.args[1], inspect.Arguments)
assert len(self.args[1].args) == 4
assert isinstance(self.args[1].args[0], Request)
assert isinstance(self.args[1].args[1], Response)
assert self.args[1].args[2:] == ['foo', 'bar']
assert self.args[1].varargs == ['spam', 'eggs']
class TestRestControllerStateAccess(PecanTestCase):
def setUp(self):
super(TestRestControllerStateAccess, self).setUp()
self.args = None
class RootController(rest.RestController):
@expose()
def _default(self, _id, *args, **kw):
return 'Default'
@expose()
def get_all(self, **kw):
return 'All'
@expose()
def get_one(self, _id, *args, **kw):
return 'One'
@expose()
def post(self, *args, **kw):
return 'POST'
@expose()
def put(self, _id, *args, **kw):
return 'PUT'
@expose()
def delete(self, _id, *args, **kw):
return 'DELETE'
class SimpleHook(PecanHook):
def before(inself, state):
self.args = (state.controller, state.arguments)
self.root = RootController()
self.app = TestApp(make_app(self.root, hooks=[SimpleHook()]))
def test_get_all(self):
self.app.get('/')
assert self.args[0] == self.root.get_all
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_get_all_with_kwargs(self):
self.app.get('/?foo=bar')
assert self.args[0] == self.root.get_all
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'foo': 'bar'}
def test_get_one(self):
self.app.get('/1')
assert self.args[0] == self.root.get_one
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_get_one_with_varargs(self):
self.app.get('/1/2/3')
assert self.args[0] == self.root.get_one
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == ['2', '3']
assert kwargs(self.args[1]) == {}
def test_get_one_with_kwargs(self):
self.app.get('/1?foo=bar')
assert self.args[0] == self.root.get_one
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'foo': 'bar'}
def test_post(self):
self.app.post('/')
assert self.args[0] == self.root.post
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_post_with_varargs(self):
self.app.post('/foo/bar')
assert self.args[0] == self.root.post
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == ['foo', 'bar']
assert kwargs(self.args[1]) == {}
def test_post_with_kwargs(self):
self.app.post('/', params={'foo': 'bar'})
assert self.args[0] == self.root.post
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == []
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'foo': 'bar'}
def test_put(self):
self.app.put('/1')
assert self.args[0] == self.root.put
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_put_with_method_argument(self):
self.app.post('/1?_method=put')
assert self.args[0] == self.root.put
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'_method': 'put'}
def test_put_with_varargs(self):
self.app.put('/1/2/3')
assert self.args[0] == self.root.put
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == ['2', '3']
assert kwargs(self.args[1]) == {}
def test_put_with_kwargs(self):
self.app.put('/1?foo=bar')
assert self.args[0] == self.root.put
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'foo': 'bar'}
def test_delete(self):
self.app.delete('/1')
assert self.args[0] == self.root.delete
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {}
def test_delete_with_method_argument(self):
self.app.post('/1?_method=delete')
assert self.args[0] == self.root.delete
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'_method': 'delete'}
def test_delete_with_varargs(self):
self.app.delete('/1/2/3')
assert self.args[0] == self.root.delete
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == ['2', '3']
assert kwargs(self.args[1]) == {}
def test_delete_with_kwargs(self):
self.app.delete('/1?foo=bar')
assert self.args[0] == self.root.delete
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'foo': 'bar'}
def test_post_with_invalid_method_kwarg(self):
self.app.post('/1?_method=invalid')
assert self.args[0] == self.root._default
assert isinstance(self.args[1], inspect.Arguments)
assert self.args[1].args == ['1']
assert self.args[1].varargs == []
assert kwargs(self.args[1]) == {'_method': 'invalid'}
class TestTransactionHook(PecanTestCase):
def test_transaction_hook(self):
run_hook = []