Skip to content

Commit

Permalink
implement formatargspec with Signature
Browse files Browse the repository at this point in the history
  • Loading branch information
jouve committed Jan 18, 2022
1 parent 1c84deb commit 49c9f88
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 40 deletions.
69 changes: 29 additions & 40 deletions src/wrapt/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,48 +35,37 @@ def exec_(_code_, _globs_=None, _locs_=None):
from threading import Lock, RLock

try:
from inspect import Parameter, Signature
except ImportError: # < py3.5
from inspect import formatargspec
except ImportError: # > py3.10
from inspect import formatannotation
else:
def formatargspec(args, varargs=None, varkw=None, defaults=None,
kwonlyargs=(), kwonlydefaults={}, annotations={},
formatarg=str,
formatvarargs=lambda name: '*' + name,
formatvarkw=lambda name: '**' + name,
formatvalue=lambda value: '=' + repr(value),
formatreturns=lambda text: ' -> ' + text,
formatannotation=formatannotation):

def formatargandannotation(arg):
result = formatarg(arg)
if arg in annotations:
result += ': ' + formatannotation(annotations[arg])
return result
specs = []
if defaults:
firstdefault = len(args) - len(defaults)
for i, arg in enumerate(args):
spec = formatargandannotation(arg)
if defaults and i >= firstdefault:
spec = spec + formatvalue(defaults[i - firstdefault])
specs.append(spec)
if varargs is not None:
specs.append(formatvarargs(formatargandannotation(varargs)))
else:
if kwonlyargs:
specs.append('*')
if kwonlyargs:
for kwonlyarg in kwonlyargs:
spec = formatargandannotation(kwonlyarg)
if kwonlydefaults and kwonlyarg in kwonlydefaults:
spec += formatvalue(kwonlydefaults[kwonlyarg])
specs.append(spec)
if varkw is not None:
specs.append(formatvarkw(formatargandannotation(varkw)))
result = '(' + ', '.join(specs) + ')'
if 'return' in annotations:
result += formatreturns(formatannotation(annotations['return']))
return result
kwonlyargs=(), kwonlydefaults={}, annotations={}):
if kwonlydefaults is None:
kwonlydefaults = {}
ndefaults = len(defaults) if defaults else 0
parameters = [
Parameter(
arg,
Parameter.POSITIONAL_OR_KEYWORD,
default=defaults[i] if i >= 0 else Parameter.empty,
annotation=annotations.get(arg, Parameter.empty),
) for i, arg in enumerate(args, ndefaults - len(args))
]
if varargs:
parameters.append(Parameter(varargs, Parameter.VAR_POSITIONAL))
parameters.extend(
Parameter(
kwonlyarg,
Parameter.KEYWORD_ONLY,
default=kwonlydefaults.get(kwonlyarg, Parameter.empty),
annotation=annotations.get(kwonlyarg, Parameter.empty),
) for kwonlyarg in kwonlyargs
)
if varkw:
parameters.append(Parameter(varkw, Parameter.VAR_KEYWORD))
return_annotation = annotations.get('return', Signature.empty)
return str(Signature(parameters, return_annotation=return_annotation))
try:
from inspect import signature
except ImportError:
Expand Down
35 changes: 35 additions & 0 deletions tests/test_formatargspec_py35.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import unittest
import sys
from inspect import getfullargspec

from wrapt.decorators import formatargspec

class TestFormatargspec35(unittest.TestCase):

def assertFormatEqual(self, func, ref):
formatted = formatargspec(*getfullargspec(func))
self.assertEqual(formatted, ref)

def test_formatargspec(self):
def foo1(): pass
self.assertFormatEqual(foo1, '()')

def foo2(a, b='c'): pass
self.assertFormatEqual(foo2, ("(a, b='c')"))

def foo3(a, b, *args, **kwargs): pass
self.assertFormatEqual(foo3, '(a, b, *args, **kwargs)')

def foo4(a: int, b) -> list: pass
if sys.version_info[:2] < (3, 7):
formatted4 = '(a:int, b) -> list'
else:
formatted4 = '(a: int, b) -> list'
self.assertFormatEqual(foo4, formatted4)

# examples from https://www.python.org/dev/peps/pep-3102/
def sortwords(*wordlist, case_sensitive=False): pass
self.assertFormatEqual(sortwords, '(*wordlist, case_sensitive=False)')

def compare(a, b, *, key=None): pass
self.assertFormatEqual(compare, '(a, b, *, key=None)')
37 changes: 37 additions & 0 deletions tests/test_formatargspec_py38.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import unittest
import sys
from inspect import getfullargspec

from wrapt.decorators import formatargspec

class TestFormatargspec38(unittest.TestCase):

def assertFormatEqual(self, func, ref):
formatted = formatargspec(*getfullargspec(func))
self.assertEqual(formatted, ref)

def test_formatargspec(self):
# exemples from https://www.python.org/dev/peps/pep-0570/
def name1(p1, p2, /, p_or_kw, *, kw): pass
self.assertFormatEqual(name1, '(p1, p2, p_or_kw, *, kw)')

def name2(p1, p2=None, /, p_or_kw=None, *, kw): pass
self.assertFormatEqual(name2, '(p1, p2=None, p_or_kw=None, *, kw)')

def name3(p1, p2=None, /, *, kw): pass
self.assertFormatEqual(name3, '(p1, p2=None, *, kw)')

def name4(p1, p2=None, /): pass
self.assertFormatEqual(name4, '(p1, p2=None)')

def name5(p1, p2, /, p_or_kw): pass
self.assertFormatEqual(name5, '(p1, p2, p_or_kw)')

def name6(p1, p2, /): pass
self.assertFormatEqual(name6, '(p1, p2)')

def name7(p_or_kw, *, kw): pass
self.assertFormatEqual(name7, '(p_or_kw, *, kw)')

def name8(*, kw): pass
self.assertFormatEqual(name8, '(*, kw)')

0 comments on commit 49c9f88

Please sign in to comment.