Skip to content
Open
Show file tree
Hide file tree
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
46 changes: 34 additions & 12 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2339,6 +2339,17 @@ def _signature_from_function(cls, func, skip_bound_arg=True,

Parameter = cls._parameter_cls

# A real Python function has valid identifier names in co_varnames, so its
# parameters can be built without re-validating them. A function-like
# object (is_duck_function) carries an arbitrary code object whose
# co_varnames are not guaranteed to be valid identifiers, so it must go
# through the validating constructor.
if is_duck_function:
def make_param(name, kind, default, annotation, _P=Parameter):
return _P(name, kind, default=default, annotation=annotation)
else:
make_param = Parameter._from_valid_args

# Parameter information.
func_code = func.__code__
pos_count = func_code.co_argcount
Expand Down Expand Up @@ -2366,27 +2377,23 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
for name in positional[:non_default_count]:
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=kind))
parameters.append(make_param(name, kind, _empty, annotation))
if posonly_left:
posonly_left -= 1

# ... w/ defaults.
for offset, name in enumerate(positional[non_default_count:]):
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=kind,
default=defaults[offset]))
parameters.append(make_param(name, kind, defaults[offset], annotation))
if posonly_left:
posonly_left -= 1

# *args
if func_code.co_flags & CO_VARARGS:
name = arg_names[pos_count + keyword_only_count]
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=_VAR_POSITIONAL))
parameters.append(make_param(name, _VAR_POSITIONAL, _empty, annotation))

# Keyword-only parameters.
for name in keyword_only:
Expand All @@ -2395,9 +2402,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
default = kwdefaults.get(name, _empty)

annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=_KEYWORD_ONLY,
default=default))
parameters.append(make_param(name, _KEYWORD_ONLY, default, annotation))
# **kwargs
if func_code.co_flags & CO_VARKEYWORDS:
index = pos_count + keyword_only_count
Expand All @@ -2406,8 +2411,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True,

name = arg_names[index]
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=_VAR_KEYWORD))
parameters.append(make_param(name, _VAR_KEYWORD, _empty, annotation))

# Is 'func' is a pure Python function - don't validate the
# parameters list (for correct order and defaults), it should be OK.
Expand Down Expand Up @@ -2736,6 +2740,24 @@ def __init__(self, name, kind, *, default=_empty, annotation=_empty):

self._name = name

@classmethod
def _from_valid_args(cls, name, kind, default, annotation):
# Fast path for trusted callers (e.g. _signature_from_function), where
# the name comes from a code object's co_varnames -- always a valid,
# non-keyword identifier -- and the kind is one of the module-level
# _ParameterKind constants. Skips the validation done in __init__.
# Implicit comprehension arguments ('.0') are rare and need the
# recast __init__ performs, so defer those to it rather than
# duplicate the logic here.
if name[0] == '.':
return cls(name, kind=kind, default=default, annotation=annotation)
self = cls.__new__(cls)
self._name = name
self._kind = kind
self._default = default
self._annotation = annotation
return self

def __reduce__(self):
return (type(self),
(self._name, self._kind),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Speed up :func:`inspect.signature` for Python functions by skipping redundant
:class:`inspect.Parameter` validation when the parameters are built from a
function's own code object. Patch by Bernát Gábor.
Loading