Merge "Improve argspec detection and leniency for wrapped controllers."
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
68
pecan/tests/test_util.py
Normal 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
|
||||
@@ -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 = {}
|
||||
|
||||
Reference in New Issue
Block a user