Skip to content

Commit

Permalink
render *args, **kwargs properly in method call; make buildfacade meth…
Browse files Browse the repository at this point in the history
…od public
  • Loading branch information
tschorr committed Oct 7, 2018
1 parent 605ec75 commit 77f4826
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 7 deletions.
13 changes: 7 additions & 6 deletions src/AccessControl/requestmethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
_default = []


def _buildFacade(name, method, docstring):
def buildfacade(name, method, docstring):
"""Build a facade function, matching the decorated method in signature.
Note that defaults are replaced by _default, and _curried will reconstruct
Expand All @@ -34,13 +34,14 @@ def _buildFacade(name, method, docstring):
"""
sig = signature(method)
args = []
callargs = []
for v in sig.parameters.values():
argstr = str(v)
parts = str(v).split('=')
args.append(
argstr if '=' not in argstr else '{}=_default'.format(v.name))
callargs = ', '.join(sig.parameters.keys())
parts[0] if len(parts) == 1 else '{}=_default'.format(parts[0]))
callargs.append(parts[0])
return 'def %s(%s):\n """%s"""\n return _curried(%s)' % (
name, ', '.join(args), docstring, callargs)
name, ', '.join(args), docstring, ', '.join(callargs))


def requestmethod(*methods):
Expand Down Expand Up @@ -86,7 +87,7 @@ def _curried(*args, **kw):
# Build a facade, with a reference to our locally-scoped _curried
name = callable.__name__
facade_globs = dict(_curried=_curried, _default=_default)
exec(_buildFacade(name, callable, callable.__doc__), facade_globs)
exec(buildfacade(name, callable, callable.__doc__), facade_globs)
return facade_globs[name]

return _methodtest
Expand Down
15 changes: 14 additions & 1 deletion src/AccessControl/tests/test_requestmethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from AccessControl.requestmethod import requestmethod
from AccessControl.requestmethod import getfullargspec
from AccessControl.requestmethod import buildfacade
from zope.interface import implementer
from zope.publisher.interfaces.browser import IBrowserRequest
import unittest
Expand Down Expand Up @@ -84,7 +85,6 @@ def test_preserves_keyword_parameter_defaults(self):
# variables against callable signatures, the result of the decorator
# must match the original closely, and keyword parameter defaults must
# be preserved:
import inspect
mutabledefault = dict()

@requestmethod('POST')
Expand Down Expand Up @@ -121,3 +121,16 @@ def foo(bar, REQUEST=None):
foo('spam', POST)
self.assertEqual('Request must be GET, HEAD or PROPFIND',
str(err.exception))

def test_facade_render_correct_args_kwargs(self):
""" s. https://github.com/zopefoundation/AccessControl/issues/69
"""
def foo(bar, baz, *args, **kwargs):
"""foo doc"""
return baz
got = buildfacade('foo', foo, foo.__doc__)
expected = '\n'.join([
'def foo(bar, baz, *args, **kwargs):',
' """foo doc"""',
' return _curried(bar, baz, *args, **kwargs)'])
self.assertEqual(expected, got)

0 comments on commit 77f4826

Please sign in to comment.