Skip to content

Commit 12b3b99

Browse files
committed
gh-150816: Speed up inspect.signature() for Python functions
Parameters built from a function's code object always have valid identifier names and canonical kinds, yet each one is constructed through the validating Parameter() constructor. Add Parameter._create() for these trusted callers to skip the redundant checks; comprehension implicit args still defer to __init__. The public constructor and its validation are unchanged.
1 parent 2f8f569 commit 12b3b99

2 files changed

Lines changed: 25 additions & 12 deletions

File tree

Lib/inspect.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2366,27 +2366,23 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
23662366
for name in positional[:non_default_count]:
23672367
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
23682368
annotation = annotations.get(name, _empty)
2369-
parameters.append(Parameter(name, annotation=annotation,
2370-
kind=kind))
2369+
parameters.append(Parameter._create(name, kind, _empty, annotation))
23712370
if posonly_left:
23722371
posonly_left -= 1
23732372

23742373
# ... w/ defaults.
23752374
for offset, name in enumerate(positional[non_default_count:]):
23762375
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
23772376
annotation = annotations.get(name, _empty)
2378-
parameters.append(Parameter(name, annotation=annotation,
2379-
kind=kind,
2380-
default=defaults[offset]))
2377+
parameters.append(Parameter._create(name, kind, defaults[offset], annotation))
23812378
if posonly_left:
23822379
posonly_left -= 1
23832380

23842381
# *args
23852382
if func_code.co_flags & CO_VARARGS:
23862383
name = arg_names[pos_count + keyword_only_count]
23872384
annotation = annotations.get(name, _empty)
2388-
parameters.append(Parameter(name, annotation=annotation,
2389-
kind=_VAR_POSITIONAL))
2385+
parameters.append(Parameter._create(name, _VAR_POSITIONAL, _empty, annotation))
23902386

23912387
# Keyword-only parameters.
23922388
for name in keyword_only:
@@ -2395,9 +2391,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
23952391
default = kwdefaults.get(name, _empty)
23962392

23972393
annotation = annotations.get(name, _empty)
2398-
parameters.append(Parameter(name, annotation=annotation,
2399-
kind=_KEYWORD_ONLY,
2400-
default=default))
2394+
parameters.append(Parameter._create(name, _KEYWORD_ONLY, default, annotation))
24012395
# **kwargs
24022396
if func_code.co_flags & CO_VARKEYWORDS:
24032397
index = pos_count + keyword_only_count
@@ -2406,8 +2400,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
24062400

24072401
name = arg_names[index]
24082402
annotation = annotations.get(name, _empty)
2409-
parameters.append(Parameter(name, annotation=annotation,
2410-
kind=_VAR_KEYWORD))
2403+
parameters.append(Parameter._create(name, _VAR_KEYWORD, _empty, annotation))
24112404

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

27372730
self._name = name
27382731

2732+
@classmethod
2733+
def _create(cls, name, kind, default, annotation):
2734+
# Fast path for trusted callers (e.g. _signature_from_function), where
2735+
# the name comes from a code object's co_varnames -- always a valid,
2736+
# non-keyword identifier -- and the kind is one of the module-level
2737+
# _ParameterKind constants. Skips the validation done in __init__.
2738+
# Comprehension implicit args ('.0') still need the recast, so defer
2739+
# those to __init__.
2740+
if name[0] == '.':
2741+
return cls(name, kind=kind, default=default, annotation=annotation)
2742+
self = cls.__new__(cls)
2743+
self._name = name
2744+
self._kind = kind
2745+
self._default = default
2746+
self._annotation = annotation
2747+
return self
2748+
27392749
def __reduce__(self):
27402750
return (type(self),
27412751
(self._name, self._kind),
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Speed up :func:`inspect.signature` for Python functions by skipping redundant
2+
:class:`inspect.Parameter` validation when the parameters are built from a
3+
function's own code object. Patch by Bernát Gábor.

0 commit comments

Comments
 (0)