Fix an argspec detection edge case in pecan.util.getargspec
In lieu of decorators that responsibly duplicate the argspec of underlying functions, pecan must make a best guess. When multiple nested decorators are discovered, the __closure__ of the wrapped function is searched for callables (one of which likely represents the underlying wrapped function - the controller method itself). This change tweaks the algorithm a bit further to place higher match precedence on in-scope functions that have `self` as a first argument.
This commit is contained in:
@@ -93,3 +93,23 @@ class TestArgSpec(unittest.TestCase):
|
||||
actual = util.getargspec(dec(True)(
|
||||
self.controller.static_index))
|
||||
assert expected == actual
|
||||
|
||||
def test_nested_cells(self):
|
||||
|
||||
def before(handler):
|
||||
def deco(f):
|
||||
def wrapped(*args, **kwargs):
|
||||
if callable(handler):
|
||||
handler()
|
||||
return f(*args, **kwargs)
|
||||
return wrapped
|
||||
return deco
|
||||
|
||||
class RootController(object):
|
||||
@expose()
|
||||
@before(lambda: True)
|
||||
def index(self, a, b, c):
|
||||
return 'Hello, World!'
|
||||
|
||||
argspec = util._cfg(RootController.index)['argspec']
|
||||
assert argspec.args == ['self', 'a', 'b', 'c']
|
||||
|
||||
@@ -26,15 +26,26 @@ def getargspec(method):
|
||||
# NOTE(sileht): if the closure is None we cannot look deeper,
|
||||
# so return actual argspec, this occurs when the method
|
||||
# is static for example.
|
||||
if func_closure is None:
|
||||
if not func_closure:
|
||||
return argspec
|
||||
|
||||
closure = next(
|
||||
(
|
||||
c for c in func_closure if six.callable(c.cell_contents)
|
||||
),
|
||||
None
|
||||
closure = None
|
||||
# In the case of deeply nested decorators (with arguments), it's possible
|
||||
# that there are several callables in scope; Take a best guess and go
|
||||
# with the one that looks most like a pecan controller function
|
||||
# ('self' is the first argument)
|
||||
func_closure = filter(
|
||||
lambda c: six.callable(c.cell_contents),
|
||||
func_closure
|
||||
)
|
||||
func_closure = sorted(
|
||||
func_closure,
|
||||
key=lambda c: 'self' in c.cell_contents.__code__.co_varnames,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
closure = func_closure[0]
|
||||
|
||||
method = closure.cell_contents
|
||||
return getargspec(method)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user