Skip to content

Display the correct signature for a decorated function in Python 3 #765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 52 additions & 4 deletions bpython/inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,17 +250,16 @@ def getfuncprops(func, f):
return None
try:
if py3:
argspec = inspect.getfullargspec(f)
argspec = get_argspec_from_signature(f)
else:
argspec = inspect.getargspec(f)
argspec = list(inspect.getargspec(f))

argspec = list(argspec)
fixlongargs(f, argspec)
if len(argspec) == 4:
argspec = argspec + [list(), dict(), None]
argspec = ArgSpec(*argspec)
fprops = FuncProps(func, argspec, is_bound_method)
except (TypeError, KeyError):
except (TypeError, KeyError, ValueError):
with AttrCleaner(f):
argspec = getpydocspec(f, func)
if argspec is None:
Expand All @@ -284,6 +283,55 @@ def is_callable(obj):
return callable(obj)


def get_argspec_from_signature(f):
"""Get callable signature from inspect.signature in argspec format.

inspect.signature is a Python 3 only function that returns the signature of
a function. Its advantage over inspect.getfullargspec is that it returns
the signature of a decorated function, if the wrapper function itself is
decorated with functools.wraps.

"""
args = []
varargs = varkwargs = None
defaults = []
kwonly = []
kwonly_defaults = {}
annotations = {}

signature = inspect.signature(f)
for parameter in signature.parameters.values():
if parameter.annotation is not inspect._empty:
annotations[parameter.name] = parameter.annotation

if parameter.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
args.append(parameter.name)
if parameter.default is not inspect._empty:
defaults.append(parameter.default)
elif parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
args.append(parameter.name)
elif parameter.kind == inspect.Parameter.VAR_POSITIONAL:
varargs = parameter.name
elif parameter.kind == inspect.Parameter.KEYWORD_ONLY:
kwonly.append(parameter.name)
kwonly_defaults[parameter.name] = parameter.default
elif parameter.kind == inspect.Parameter.VAR_KEYWORD:
varkwargs = parameter.name

# inspect.getfullargspec returns None for 'defaults', 'kwonly_defaults' and
# 'annotations' if there are no values for them.
if not defaults:
defaults = None

if not kwonly_defaults:
kwonly_defaults = None

if not annotations:
annotations = None

return [args, varargs, varkwargs, defaults, kwonly, kwonly_defaults, annotations]


get_encoding_line_re = LazyReCompile(r'^.*coding[:=]\s*([-\w.]+).*$')


Expand Down