Skip to content

Commit a567bd1

Browse files
committed
Validate parameters from function-like (duck) objects
_signature_from_function() also handles third-party function-like objects (is_duck_function), whose code object's co_varnames are not guaranteed to be valid identifiers. Route those through the validating Parameter() constructor; keep the _from_valid_args fast path only for real Python functions.
1 parent 667c564 commit a567bd1

1 file changed

Lines changed: 16 additions & 5 deletions

File tree

Lib/inspect.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,6 +2339,17 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
23392339

23402340
Parameter = cls._parameter_cls
23412341

2342+
# A real Python function has valid identifier names in co_varnames, so its
2343+
# parameters can be built without re-validating them. A function-like
2344+
# object (is_duck_function) carries an arbitrary code object whose
2345+
# co_varnames are not guaranteed to be valid identifiers, so it must go
2346+
# through the validating constructor.
2347+
if is_duck_function:
2348+
def make_param(name, kind, default, annotation, _P=Parameter):
2349+
return _P(name, kind, default=default, annotation=annotation)
2350+
else:
2351+
make_param = Parameter._from_valid_args
2352+
23422353
# Parameter information.
23432354
func_code = func.__code__
23442355
pos_count = func_code.co_argcount
@@ -2366,23 +2377,23 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
23662377
for name in positional[:non_default_count]:
23672378
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
23682379
annotation = annotations.get(name, _empty)
2369-
parameters.append(Parameter._from_valid_args(name, kind, _empty, annotation))
2380+
parameters.append(make_param(name, kind, _empty, annotation))
23702381
if posonly_left:
23712382
posonly_left -= 1
23722383

23732384
# ... w/ defaults.
23742385
for offset, name in enumerate(positional[non_default_count:]):
23752386
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
23762387
annotation = annotations.get(name, _empty)
2377-
parameters.append(Parameter._from_valid_args(name, kind, defaults[offset], annotation))
2388+
parameters.append(make_param(name, kind, defaults[offset], annotation))
23782389
if posonly_left:
23792390
posonly_left -= 1
23802391

23812392
# *args
23822393
if func_code.co_flags & CO_VARARGS:
23832394
name = arg_names[pos_count + keyword_only_count]
23842395
annotation = annotations.get(name, _empty)
2385-
parameters.append(Parameter._from_valid_args(name, _VAR_POSITIONAL, _empty, annotation))
2396+
parameters.append(make_param(name, _VAR_POSITIONAL, _empty, annotation))
23862397

23872398
# Keyword-only parameters.
23882399
for name in keyword_only:
@@ -2391,7 +2402,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
23912402
default = kwdefaults.get(name, _empty)
23922403

23932404
annotation = annotations.get(name, _empty)
2394-
parameters.append(Parameter._from_valid_args(name, _KEYWORD_ONLY, default, annotation))
2405+
parameters.append(make_param(name, _KEYWORD_ONLY, default, annotation))
23952406
# **kwargs
23962407
if func_code.co_flags & CO_VARKEYWORDS:
23972408
index = pos_count + keyword_only_count
@@ -2400,7 +2411,7 @@ def _signature_from_function(cls, func, skip_bound_arg=True,
24002411

24012412
name = arg_names[index]
24022413
annotation = annotations.get(name, _empty)
2403-
parameters.append(Parameter._from_valid_args(name, _VAR_KEYWORD, _empty, annotation))
2414+
parameters.append(make_param(name, _VAR_KEYWORD, _empty, annotation))
24042415

24052416
# Is 'func' is a pure Python function - don't validate the
24062417
# parameters list (for correct order and defaults), it should be OK.

0 commit comments

Comments
 (0)