Skip to content

Commit a9407b6

Browse files
bfraschersebastinas
authored andcommitted
Display the correct signature for a decorated function in Python 3
This patch changes it so that `inspect.signature` is used instead of `inspect.getfullargspec` to get a function's signature, when using Python 3. Python 3.3 introduced the `inspect.signature` function as a new way to get the signature of a function (as an alternative to `inspect.getargspec` and `inspect.getfullargspec`). `inspect.signature` has the advantage that it preserves the signature of a decorated function if `functools.wraps` is used to decorated the wrapper function. Having a function's signature available is very hepful, especially when testing things out in a REPL.
1 parent 286fd36 commit a9407b6

File tree

1 file changed

+51
-3
lines changed

1 file changed

+51
-3
lines changed

bpython/inspection.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,10 @@ def getfuncprops(func, f):
250250
return None
251251
try:
252252
if py3:
253-
argspec = inspect.getfullargspec(f)
253+
argspec = get_argspec_from_signature(f)
254254
else:
255-
argspec = inspect.getargspec(f)
255+
argspec = list(inspect.getargspec(f))
256256

257-
argspec = list(argspec)
258257
fixlongargs(f, argspec)
259258
if len(argspec) == 4:
260259
argspec = argspec + [list(), dict(), None]
@@ -284,6 +283,55 @@ def is_callable(obj):
284283
return callable(obj)
285284

286285

286+
def get_argspec_from_signature(f):
287+
"""Get callable signature from inspect.signature in argspec format.
288+
289+
inspect.signature is a Python 3 only function that returns the signature of
290+
a function. Its advantage over inspect.getfullargspec is that it returns
291+
the signature of a decorated function, if the wrapper function itself is
292+
decorated with functools.wraps.
293+
294+
"""
295+
args = []
296+
varargs = varkwargs = None
297+
defaults = []
298+
kwonly = []
299+
kwonly_defaults = {}
300+
annotations = {}
301+
302+
signature = inspect.signature(f)
303+
for parameter in signature.parameters.values():
304+
if parameter.annotation is not inspect._empty:
305+
annotations[parameter.name] = parameter.annotation
306+
307+
if parameter.kind == inspect._ParameterKind.POSITIONAL_OR_KEYWORD:
308+
args.append(parameter.name)
309+
if parameter.default is not inspect._empty:
310+
defaults.append(parameter.default)
311+
elif parameter.kind == inspect._ParameterKind.POSITIONAL_ONLY:
312+
args.append(parameter.name)
313+
elif parameter.kind == inspect._ParameterKind.VAR_POSITIONAL:
314+
varargs = parameter.name
315+
elif parameter.kind == inspect._ParameterKind.KEYWORD_ONLY:
316+
kwonly.append(parameter.name)
317+
kwonly_defaults[parameter.name] = parameter.default
318+
elif parameter.kind == inspect._ParameterKind.VAR_KEYWORD:
319+
varkwargs = parameter.name
320+
321+
# inspect.getfullargspec returns None for 'defaults', 'kwonly_defaults' and
322+
# 'annotations' if there are no values for them.
323+
if not defaults:
324+
defaults = None
325+
326+
if not kwonly_defaults:
327+
kwonly_defaults = None
328+
329+
if not annotations:
330+
annotations = None
331+
332+
return [args, varargs, varkwargs, defaults, kwonly, kwonly_defaults, annotations]
333+
334+
287335
get_encoding_line_re = LazyReCompile(r'^.*coding[:=]\s*([-\w.]+).*$')
288336

289337

0 commit comments

Comments
 (0)