Merge "Improve argspec detection and leniency for wrapped controllers."

This commit is contained in:
Jenkins
2014-09-25 19:13:19 +00:00
committed by Gerrit Code Review
6 changed files with 111 additions and 7 deletions

View File

@@ -19,7 +19,7 @@ from .compat import urlparse, unquote_plus, izip
from .secure import handle_security
from .templating import RendererFactory
from .routing import lookup_controller, NonCanonicalPath
from .util import _cfg, encode_if_needed
from .util import _cfg, encode_if_needed, getargspec
from .middleware.recursive import ForwardRequestException
@@ -527,6 +527,15 @@ class PecanBase(object):
resp = state.response
pecan_state = req.pecan
# If a keyword is supplied via HTTP GET or POST arguments, but the
# function signature does not allow it, just drop it (rather than
# generating a TypeError).
argspec = getargspec(controller)
keys = kwargs.keys()
for key in keys:
if key not in argspec.args and not argspec.keywords:
kwargs.pop(key)
# get the result from the controller
result = controller(*args, **kwargs)

View File

@@ -1,8 +1,8 @@
from inspect import getargspec, getmembers, isclass, ismethod, isfunction
from inspect import getmembers, isclass, ismethod, isfunction
import six
from .util import _cfg
from .util import _cfg, getargspec
__all__ = [
'expose', 'transactional', 'accept_noncanonical', 'after_commit',

View File

@@ -1,4 +1,4 @@
from inspect import getargspec, ismethod
from inspect import ismethod
import warnings
from webob import exc
@@ -7,7 +7,7 @@ import six
from .core import abort
from .decorators import expose
from .routing import lookup_controller, handle_lookup_traversal
from .util import iscontroller
from .util import iscontroller, getargspec
class RestController(object):

View File

@@ -1,10 +1,9 @@
import warnings
from inspect import getargspec
from webob import exc
from .secure import handle_security, cross_boundary
from .util import iscontroller
from .util import iscontroller, getargspec
__all__ = ['lookup_controller', 'find_object']

68
pecan/tests/test_util.py Normal file
View File

@@ -0,0 +1,68 @@
import functools
import inspect
import unittest
from pecan import expose
from pecan import util
class TestArgSpec(unittest.TestCase):
@property
def controller(self):
class RootController(object):
@expose()
def index(self, a, b, c=1, *args, **kwargs):
return 'Hello, World!'
return RootController()
def test_no_decorator(self):
expected = inspect.getargspec(self.controller.index.__func__)
actual = util.getargspec(self.controller.index.__func__)
assert expected == actual
def test_simple_decorator(self):
def dec(f):
return f
expected = inspect.getargspec(self.controller.index.__func__)
actual = util.getargspec(dec(self.controller.index.__func__))
assert expected == actual
def test_simple_wrapper(self):
def dec(f):
@functools.wraps(f)
def wrapped(*a, **kw):
return f(*a, **kw)
return wrapped
expected = inspect.getargspec(self.controller.index.__func__)
actual = util.getargspec(dec(self.controller.index.__func__))
assert expected == actual
def test_multiple_decorators(self):
def dec(f):
@functools.wraps(f)
def wrapped(*a, **kw):
return f(*a, **kw)
return wrapped
expected = inspect.getargspec(self.controller.index.__func__)
actual = util.getargspec(dec(dec(dec(self.controller.index.__func__))))
assert expected == actual
def test_decorator_with_args(self):
def dec(flag):
def inner(f):
@functools.wraps(f)
def wrapped(*a, **kw):
return f(*a, **kw)
return wrapped
return inner
expected = inspect.getargspec(self.controller.index.__func__)
actual = util.getargspec(dec(True)(self.controller.index.__func__))
assert expected == actual

View File

@@ -1,10 +1,38 @@
import inspect
import sys
import six
def iscontroller(obj):
return getattr(obj, 'exposed', False)
def getargspec(method):
"""
Drill through layers of decorators attempting to locate the actual argspec
for a method.
"""
argspec = inspect.getargspec(method)
args = argspec[0]
if args and args[0] == 'self':
return argspec
if hasattr(method, '__func__'):
method = method.__func__
func_closure = six.get_function_closure(method)
closure = next(
(
c for c in func_closure if six.callable(c.cell_contents)
),
None
)
method = closure.cell_contents
return getargspec(method)
def _cfg(f):
if not hasattr(f, '_pecan'):
f._pecan = {}