Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
10a7aab6
Commit
10a7aab6
authored
Apr 08, 2014
by
Yury Selivanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
inspect.signautre: Fix functools.partial support. Issue #21117
parent
0803eeae
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
146 additions
and
93 deletions
+146
-93
Lib/inspect.py
Lib/inspect.py
+62
-73
Lib/test/test_inspect.py
Lib/test/test_inspect.py
+79
-20
Misc/NEWS
Misc/NEWS
+5
-0
No files found.
Lib/inspect.py
View file @
10a7aab6
...
@@ -1511,7 +1511,8 @@ def _signature_get_partial(wrapped_sig, partial, extra_args=()):
...
@@ -1511,7 +1511,8 @@ def _signature_get_partial(wrapped_sig, partial, extra_args=()):
# look like after applying a 'functools.partial' object (or alike)
# look like after applying a 'functools.partial' object (or alike)
# on it.
# on it.
new_params
=
OrderedDict
(
wrapped_sig
.
parameters
.
items
())
old_params
=
wrapped_sig
.
parameters
new_params
=
OrderedDict
(
old_params
.
items
())
partial_args
=
partial
.
args
or
()
partial_args
=
partial
.
args
or
()
partial_keywords
=
partial
.
keywords
or
{}
partial_keywords
=
partial
.
keywords
or
{}
...
@@ -1525,32 +1526,57 @@ def _signature_get_partial(wrapped_sig, partial, extra_args=()):
...
@@ -1525,32 +1526,57 @@ def _signature_get_partial(wrapped_sig, partial, extra_args=()):
msg
=
'partial object {!r} has incorrect arguments'
.
format
(
partial
)
msg
=
'partial object {!r} has incorrect arguments'
.
format
(
partial
)
raise
ValueError
(
msg
)
from
ex
raise
ValueError
(
msg
)
from
ex
for
arg_name
,
arg_value
in
ba
.
arguments
.
items
():
param
=
new_params
[
arg_name
]
transform_to_kwonly
=
False
if
arg_name
in
partial_keywords
:
for
param_name
,
param
in
old_params
.
items
():
# We set a new default value, because the following code
try
:
# is correct:
arg_value
=
ba
.
arguments
[
param_name
]
#
except
KeyError
:
# >>> def foo(a): print(a)
pass
# >>> print(partial(partial(foo, a=10), a=20)())
else
:
# 20
if
param
.
kind
is
_POSITIONAL_ONLY
:
# >>> print(partial(partial(foo, a=10), a=20)(a=30))
# If positional-only parameter is bound by partial,
# 30
# it effectively disappears from the signature
#
new_params
.
pop
(
param_name
)
# So, with 'partial' objects, passing a keyword argument is
continue
# like setting a new default value for the corresponding
# parameter
if
param
.
kind
is
_POSITIONAL_OR_KEYWORD
:
if
param_name
in
partial_keywords
:
# This means that this parameter, and all parameters
# after it should be keyword-only (and var-positional
# should be removed). Here's why. Consider the following
# function:
# foo(a, b, *args, c):
# pass
#
#
# We also mark this parameter with '_partial_kwarg'
# "partial(foo, a='spam')" will have the following
# flag. Later, in '_bind', the 'default' value of this
# signature: "(*, a='spam', b, c)". Because attempting
# parameter will be added to 'kwargs', to simulate
# to call that partial with "(10, 20)" arguments will
# the 'functools.partial' real call.
# raise a TypeError, saying that "a" argument received
new_params
[
arg_name
]
=
param
.
replace
(
default
=
arg_value
,
# multiple values.
_partial_kwarg
=
True
)
transform_to_kwonly
=
True
# Set the new default value
new_params
[
param_name
]
=
param
.
replace
(
default
=
arg_value
)
else
:
# was passed as a positional argument
new_params
.
pop
(
param
.
name
)
continue
elif
(
param
.
kind
not
in
(
_VAR_KEYWORD
,
_VAR_POSITIONAL
)
and
if
param
.
kind
is
_KEYWORD_ONLY
:
not
param
.
_partial_kwarg
):
# Set the new default value
new_params
.
pop
(
arg_name
)
new_params
[
param_name
]
=
param
.
replace
(
default
=
arg_value
)
if
transform_to_kwonly
:
assert
param
.
kind
is
not
_POSITIONAL_ONLY
if
param
.
kind
is
_POSITIONAL_OR_KEYWORD
:
new_param
=
new_params
[
param_name
].
replace
(
kind
=
_KEYWORD_ONLY
)
new_params
[
param_name
]
=
new_param
new_params
.
move_to_end
(
param_name
)
elif
param
.
kind
in
(
_KEYWORD_ONLY
,
_VAR_KEYWORD
):
new_params
.
move_to_end
(
param_name
)
elif
param
.
kind
is
_VAR_POSITIONAL
:
new_params
.
pop
(
param
.
name
)
return
wrapped_sig
.
replace
(
parameters
=
new_params
.
values
())
return
wrapped_sig
.
replace
(
parameters
=
new_params
.
values
())
...
@@ -2069,7 +2095,7 @@ class Parameter:
...
@@ -2069,7 +2095,7 @@ class Parameter:
`Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
`Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
'''
'''
__slots__
=
(
'_name'
,
'_kind'
,
'_default'
,
'_annotation'
,
'_partial_kwarg'
)
__slots__
=
(
'_name'
,
'_kind'
,
'_default'
,
'_annotation'
)
POSITIONAL_ONLY
=
_POSITIONAL_ONLY
POSITIONAL_ONLY
=
_POSITIONAL_ONLY
POSITIONAL_OR_KEYWORD
=
_POSITIONAL_OR_KEYWORD
POSITIONAL_OR_KEYWORD
=
_POSITIONAL_OR_KEYWORD
...
@@ -2079,8 +2105,7 @@ class Parameter:
...
@@ -2079,8 +2105,7 @@ class Parameter:
empty
=
_empty
empty
=
_empty
def
__init__
(
self
,
name
,
kind
,
*
,
default
=
_empty
,
annotation
=
_empty
,
def
__init__
(
self
,
name
,
kind
,
*
,
default
=
_empty
,
annotation
=
_empty
):
_partial_kwarg
=
False
):
if
kind
not
in
(
_POSITIONAL_ONLY
,
_POSITIONAL_OR_KEYWORD
,
if
kind
not
in
(
_POSITIONAL_ONLY
,
_POSITIONAL_OR_KEYWORD
,
_VAR_POSITIONAL
,
_KEYWORD_ONLY
,
_VAR_KEYWORD
):
_VAR_POSITIONAL
,
_KEYWORD_ONLY
,
_VAR_KEYWORD
):
...
@@ -2105,8 +2130,6 @@ class Parameter:
...
@@ -2105,8 +2130,6 @@ class Parameter:
self
.
_name
=
name
self
.
_name
=
name
self
.
_partial_kwarg
=
_partial_kwarg
@
property
@
property
def
name
(
self
):
def
name
(
self
):
return
self
.
_name
return
self
.
_name
...
@@ -2123,8 +2146,8 @@ class Parameter:
...
@@ -2123,8 +2146,8 @@ class Parameter:
def
kind
(
self
):
def
kind
(
self
):
return
self
.
_kind
return
self
.
_kind
def
replace
(
self
,
*
,
name
=
_void
,
kind
=
_void
,
annotation
=
_void
,
def
replace
(
self
,
*
,
name
=
_void
,
kind
=
_void
,
default
=
_void
,
_partial_kwarg
=
_void
):
annotation
=
_void
,
default
=
_void
):
'''Creates a customized copy of the Parameter.'''
'''Creates a customized copy of the Parameter.'''
if
name
is
_void
:
if
name
is
_void
:
...
@@ -2139,11 +2162,7 @@ class Parameter:
...
@@ -2139,11 +2162,7 @@ class Parameter:
if
default
is
_void
:
if
default
is
_void
:
default
=
self
.
_default
default
=
self
.
_default
if
_partial_kwarg
is
_void
:
return
type
(
self
)(
name
,
kind
,
default
=
default
,
annotation
=
annotation
)
_partial_kwarg
=
self
.
_partial_kwarg
return
type
(
self
)(
name
,
kind
,
default
=
default
,
annotation
=
annotation
,
_partial_kwarg
=
_partial_kwarg
)
def
__str__
(
self
):
def
__str__
(
self
):
kind
=
self
.
kind
kind
=
self
.
kind
...
@@ -2169,17 +2188,6 @@ class Parameter:
...
@@ -2169,17 +2188,6 @@ class Parameter:
id
(
self
),
self
.
name
)
id
(
self
),
self
.
name
)
def
__eq__
(
self
,
other
):
def
__eq__
(
self
,
other
):
# NB: We deliberately do not compare '_partial_kwarg' attributes
# here. Imagine we have a following situation:
#
# def foo(a, b=1): pass
# def bar(a, b): pass
# bar2 = functools.partial(bar, b=1)
#
# For the above scenario, signatures for `foo` and `bar2` should
# be equal. '_partial_kwarg' attribute is an internal flag, to
# distinguish between keyword parameters with defaults and
# keyword parameters which got their defaults from functools.partial
return
(
issubclass
(
other
.
__class__
,
Parameter
)
and
return
(
issubclass
(
other
.
__class__
,
Parameter
)
and
self
.
_name
==
other
.
_name
and
self
.
_name
==
other
.
_name
and
self
.
_kind
==
other
.
_kind
and
self
.
_kind
==
other
.
_kind
and
...
@@ -2219,12 +2227,7 @@ class BoundArguments:
...
@@ -2219,12 +2227,7 @@ class BoundArguments:
def
args
(
self
):
def
args
(
self
):
args
=
[]
args
=
[]
for
param_name
,
param
in
self
.
_signature
.
parameters
.
items
():
for
param_name
,
param
in
self
.
_signature
.
parameters
.
items
():
if
(
param
.
kind
in
(
_VAR_KEYWORD
,
_KEYWORD_ONLY
)
or
if
param
.
kind
in
(
_VAR_KEYWORD
,
_KEYWORD_ONLY
):
param
.
_partial_kwarg
):
# Keyword arguments mapped by 'functools.partial'
# (Parameter._partial_kwarg is True) are mapped
# in 'BoundArguments.kwargs', along with VAR_KEYWORD &
# KEYWORD_ONLY
break
break
try
:
try
:
...
@@ -2249,8 +2252,7 @@ class BoundArguments:
...
@@ -2249,8 +2252,7 @@ class BoundArguments:
kwargs_started
=
False
kwargs_started
=
False
for
param_name
,
param
in
self
.
_signature
.
parameters
.
items
():
for
param_name
,
param
in
self
.
_signature
.
parameters
.
items
():
if
not
kwargs_started
:
if
not
kwargs_started
:
if
(
param
.
kind
in
(
_VAR_KEYWORD
,
_KEYWORD_ONLY
)
or
if
param
.
kind
in
(
_VAR_KEYWORD
,
_KEYWORD_ONLY
):
param
.
_partial_kwarg
):
kwargs_started
=
True
kwargs_started
=
True
else
:
else
:
if
param_name
not
in
self
.
arguments
:
if
param_name
not
in
self
.
arguments
:
...
@@ -2332,18 +2334,14 @@ class Signature:
...
@@ -2332,18 +2334,14 @@ class Signature:
name
=
param
.
name
name
=
param
.
name
if
kind
<
top_kind
:
if
kind
<
top_kind
:
msg
=
'wrong parameter order: {
} before {
}'
msg
=
'wrong parameter order: {
!r} before {!r
}'
msg
=
msg
.
format
(
top_kind
,
kind
)
msg
=
msg
.
format
(
top_kind
,
kind
)
raise
ValueError
(
msg
)
raise
ValueError
(
msg
)
elif
kind
>
top_kind
:
elif
kind
>
top_kind
:
kind_defaults
=
False
kind_defaults
=
False
top_kind
=
kind
top_kind
=
kind
if
(
kind
in
(
_POSITIONAL_ONLY
,
_POSITIONAL_OR_KEYWORD
)
and
if
kind
in
(
_POSITIONAL_ONLY
,
_POSITIONAL_OR_KEYWORD
):
not
param
.
_partial_kwarg
):
# If we have a positional-only or positional-or-keyword
# parameter, that does not have its default value set
# by 'functools.partial' or other "partial" signature:
if
param
.
default
is
_empty
:
if
param
.
default
is
_empty
:
if
kind_defaults
:
if
kind_defaults
:
# No default for this parameter, but the
# No default for this parameter, but the
...
@@ -2518,15 +2516,6 @@ class Signature:
...
@@ -2518,15 +2516,6 @@ class Signature:
parameters_ex
=
()
parameters_ex
=
()
arg_vals
=
iter
(
args
)
arg_vals
=
iter
(
args
)
if
partial
:
# Support for binding arguments to 'functools.partial' objects.
# See 'functools.partial' case in 'signature()' implementation
# for details.
for
param_name
,
param
in
self
.
parameters
.
items
():
if
(
param
.
_partial_kwarg
and
param_name
not
in
kwargs
):
# Simulating 'functools.partial' behavior
kwargs
[
param_name
]
=
param
.
default
while
True
:
while
True
:
# Let's iterate through the positional arguments and corresponding
# Let's iterate through the positional arguments and corresponding
# parameters
# parameters
...
...
Lib/test/test_inspect.py
View file @
10a7aab6
...
@@ -1959,6 +1959,8 @@ class TestSignatureObject(unittest.TestCase):
...
@@ -1959,6 +1959,8 @@ class TestSignatureObject(unittest.TestCase):
def
test_signature_on_partial
(
self
):
def
test_signature_on_partial
(
self
):
from
functools
import
partial
from
functools
import
partial
Parameter
=
inspect
.
Parameter
def
test
():
def
test
():
pass
pass
...
@@ -1994,15 +1996,22 @@ class TestSignatureObject(unittest.TestCase):
...
@@ -1994,15 +1996,22 @@ class TestSignatureObject(unittest.TestCase):
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
b
=
1
,
c
=
2
)),
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
b
=
1
,
c
=
2
)),
(((
'a'
,
...,
...,
"positional_or_keyword"
),
(((
'a'
,
...,
...,
"positional_or_keyword"
),
(
'b'
,
1
,
...,
"
positional_or_keyword
"
),
(
'b'
,
1
,
...,
"
keyword_only
"
),
(
'c'
,
2
,
...,
"keyword_only"
),
(
'c'
,
2
,
...,
"keyword_only"
),
(
'd'
,
...,
...,
"keyword_only"
)),
(
'd'
,
...,
...,
"keyword_only"
)),
...))
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
0
,
b
=
1
,
c
=
2
)),
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
0
,
b
=
1
,
c
=
2
)),
(((
'b'
,
1
,
...,
"
positional_or_keyword
"
),
(((
'b'
,
1
,
...,
"
keyword_only
"
),
(
'c'
,
2
,
...,
"keyword_only"
),
(
'c'
,
2
,
...,
"keyword_only"
),
(
'd'
,
...,
...,
"keyword_only"
),),
(
'd'
,
...,
...,
"keyword_only"
)),
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
a
=
1
)),
(((
'a'
,
1
,
...,
"keyword_only"
),
(
'b'
,
...,
...,
"keyword_only"
),
(
'c'
,
...,
...,
"keyword_only"
),
(
'd'
,
...,
...,
"keyword_only"
)),
...))
...))
def
test
(
a
,
*
args
,
b
,
**
kwargs
):
def
test
(
a
,
*
args
,
b
,
**
kwargs
):
...
@@ -2014,13 +2023,18 @@ class TestSignatureObject(unittest.TestCase):
...
@@ -2014,13 +2023,18 @@ class TestSignatureObject(unittest.TestCase):
(
'kwargs'
,
...,
...,
"var_keyword"
)),
(
'kwargs'
,
...,
...,
"var_keyword"
)),
...))
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
a
=
1
)),
(((
'a'
,
1
,
...,
"keyword_only"
),
(
'b'
,
...,
...,
"keyword_only"
),
(
'kwargs'
,
...,
...,
"var_keyword"
)),
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
1
,
2
,
3
)),
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
1
,
2
,
3
)),
(((
'args'
,
...,
...,
"var_positional"
),
(((
'args'
,
...,
...,
"var_positional"
),
(
'b'
,
...,
...,
"keyword_only"
),
(
'b'
,
...,
...,
"keyword_only"
),
(
'kwargs'
,
...,
...,
"var_keyword"
)),
(
'kwargs'
,
...,
...,
"var_keyword"
)),
...))
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
1
,
2
,
3
,
test
=
True
)),
self
.
assertEqual
(
self
.
signature
(
partial
(
test
,
1
,
2
,
3
,
test
=
True
)),
(((
'args'
,
...,
...,
"var_positional"
),
(((
'args'
,
...,
...,
"var_positional"
),
(
'b'
,
...,
...,
"keyword_only"
),
(
'b'
,
...,
...,
"keyword_only"
),
...
@@ -2067,7 +2081,7 @@ class TestSignatureObject(unittest.TestCase):
...
@@ -2067,7 +2081,7 @@ class TestSignatureObject(unittest.TestCase):
return
a
return
a
_foo
=
partial
(
partial
(
foo
,
a
=
10
),
a
=
20
)
_foo
=
partial
(
partial
(
foo
,
a
=
10
),
a
=
20
)
self
.
assertEqual
(
self
.
signature
(
_foo
),
self
.
assertEqual
(
self
.
signature
(
_foo
),
(((
'a'
,
20
,
...,
"
positional_or_keyword
"
),),
(((
'a'
,
20
,
...,
"
keyword_only
"
),),
...))
...))
# check that we don't have any side-effects in signature(),
# check that we don't have any side-effects in signature(),
# and the partial object is still functioning
# and the partial object is still functioning
...
@@ -2076,42 +2090,87 @@ class TestSignatureObject(unittest.TestCase):
...
@@ -2076,42 +2090,87 @@ class TestSignatureObject(unittest.TestCase):
def
foo
(
a
,
b
,
c
):
def
foo
(
a
,
b
,
c
):
return
a
,
b
,
c
return
a
,
b
,
c
_foo
=
partial
(
partial
(
foo
,
1
,
b
=
20
),
b
=
30
)
_foo
=
partial
(
partial
(
foo
,
1
,
b
=
20
),
b
=
30
)
self
.
assertEqual
(
self
.
signature
(
_foo
),
self
.
assertEqual
(
self
.
signature
(
_foo
),
(((
'b'
,
30
,
...,
"
positional_or_keyword
"
),
(((
'b'
,
30
,
...,
"
keyword_only
"
),
(
'c'
,
...,
...,
"
positional_or_keyword
"
)),
(
'c'
,
...,
...,
"
keyword_only
"
)),
...))
...))
self
.
assertEqual
(
_foo
(
c
=
10
),
(
1
,
30
,
10
))
self
.
assertEqual
(
_foo
(
c
=
10
),
(
1
,
30
,
10
))
_foo
=
partial
(
_foo
,
2
)
# now 'b' has two values -
# positional and keyword
with
self
.
assertRaisesRegex
(
ValueError
,
"has incorrect arguments"
):
inspect
.
signature
(
_foo
)
def
foo
(
a
,
b
,
c
,
*
,
d
):
def
foo
(
a
,
b
,
c
,
*
,
d
):
return
a
,
b
,
c
,
d
return
a
,
b
,
c
,
d
_foo
=
partial
(
partial
(
foo
,
d
=
20
,
c
=
20
),
b
=
10
,
d
=
30
)
_foo
=
partial
(
partial
(
foo
,
d
=
20
,
c
=
20
),
b
=
10
,
d
=
30
)
self
.
assertEqual
(
self
.
signature
(
_foo
),
self
.
assertEqual
(
self
.
signature
(
_foo
),
(((
'a'
,
...,
...,
"positional_or_keyword"
),
(((
'a'
,
...,
...,
"positional_or_keyword"
),
(
'b'
,
10
,
...,
"positional_or_keyword"
),
(
'b'
,
10
,
...,
"keyword_only"
),
(
'c'
,
20
,
...,
"positional_or_keyword"
),
(
'c'
,
20
,
...,
"keyword_only"
),
(
'd'
,
30
,
...,
"keyword_only"
)),
(
'd'
,
30
,
...,
"keyword_only"
),
),
...))
...))
ba
=
inspect
.
signature
(
_foo
).
bind
(
a
=
200
,
b
=
11
)
ba
=
inspect
.
signature
(
_foo
).
bind
(
a
=
200
,
b
=
11
)
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
200
,
11
,
20
,
30
))
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
200
,
11
,
20
,
30
))
def
foo
(
a
=
1
,
b
=
2
,
c
=
3
):
def
foo
(
a
=
1
,
b
=
2
,
c
=
3
):
return
a
,
b
,
c
return
a
,
b
,
c
_foo
=
partial
(
foo
,
a
=
10
,
c
=
13
)
_foo
=
partial
(
foo
,
c
=
13
)
# (a=1, b=2, *, c=13)
ba
=
inspect
.
signature
(
_foo
).
bind
(
11
)
ba
=
inspect
.
signature
(
_foo
).
bind
(
a
=
11
)
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
11
,
2
,
13
))
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
11
,
2
,
13
))
ba
=
inspect
.
signature
(
_foo
).
bind
(
11
,
12
)
ba
=
inspect
.
signature
(
_foo
).
bind
(
11
,
12
)
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
11
,
12
,
13
))
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
11
,
12
,
13
))
ba
=
inspect
.
signature
(
_foo
).
bind
(
11
,
b
=
12
)
ba
=
inspect
.
signature
(
_foo
).
bind
(
11
,
b
=
12
)
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
11
,
12
,
13
))
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
11
,
12
,
13
))
ba
=
inspect
.
signature
(
_foo
).
bind
(
b
=
12
)
ba
=
inspect
.
signature
(
_foo
).
bind
(
b
=
12
)
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
10
,
12
,
13
))
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
1
,
12
,
13
))
_foo
=
partial
(
_foo
,
b
=
10
)
ba
=
inspect
.
signature
(
_foo
).
bind
(
12
,
14
)
_foo
=
partial
(
_foo
,
b
=
10
,
c
=
20
)
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
12
,
14
,
13
))
ba
=
inspect
.
signature
(
_foo
).
bind
(
12
)
self
.
assertEqual
(
_foo
(
*
ba
.
args
,
**
ba
.
kwargs
),
(
12
,
10
,
20
))
def
foo
(
a
,
b
,
c
,
d
,
**
kwargs
):
pass
sig
=
inspect
.
signature
(
foo
)
params
=
sig
.
parameters
.
copy
()
params
[
'a'
]
=
params
[
'a'
].
replace
(
kind
=
Parameter
.
POSITIONAL_ONLY
)
params
[
'b'
]
=
params
[
'b'
].
replace
(
kind
=
Parameter
.
POSITIONAL_ONLY
)
foo
.
__signature__
=
inspect
.
Signature
(
params
.
values
())
sig
=
inspect
.
signature
(
foo
)
self
.
assertEqual
(
str
(
sig
),
'(a, b, /, c, d, **kwargs)'
)
self
.
assertEqual
(
self
.
signature
(
partial
(
foo
,
1
)),
(((
'b'
,
...,
...,
'positional_only'
),
(
'c'
,
...,
...,
'positional_or_keyword'
),
(
'd'
,
...,
...,
'positional_or_keyword'
),
(
'kwargs'
,
...,
...,
'var_keyword'
)),
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
foo
,
1
,
2
)),
(((
'c'
,
...,
...,
'positional_or_keyword'
),
(
'd'
,
...,
...,
'positional_or_keyword'
),
(
'kwargs'
,
...,
...,
'var_keyword'
)),
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
foo
,
1
,
2
,
3
)),
(((
'd'
,
...,
...,
'positional_or_keyword'
),
(
'kwargs'
,
...,
...,
'var_keyword'
)),
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
foo
,
1
,
2
,
c
=
3
)),
(((
'c'
,
3
,
...,
'keyword_only'
),
(
'd'
,
...,
...,
'keyword_only'
),
(
'kwargs'
,
...,
...,
'var_keyword'
)),
...))
self
.
assertEqual
(
self
.
signature
(
partial
(
foo
,
1
,
c
=
3
)),
(((
'b'
,
...,
...,
'positional_only'
),
(
'c'
,
3
,
...,
'keyword_only'
),
(
'd'
,
...,
...,
'keyword_only'
),
(
'kwargs'
,
...,
...,
'var_keyword'
)),
...))
def
test_signature_on_partialmethod
(
self
):
def
test_signature_on_partialmethod
(
self
):
from
functools
import
partialmethod
from
functools
import
partialmethod
...
...
Misc/NEWS
View file @
10a7aab6
...
@@ -106,6 +106,11 @@ Library
...
@@ -106,6 +106,11 @@ Library
(Original patches by Hirokazu Yamamoto and Amaury Forgeot d'
Arc
,
with
(Original patches by Hirokazu Yamamoto and Amaury Forgeot d'
Arc
,
with
suggested
wording
by
David
Gutteridge
)
suggested
wording
by
David
Gutteridge
)
-
Issue
#
21117
:
Fix
inspect
.
signature
to
better
support
functools
.
partial
.
Due
to
the
specifics
of
functools
.
partial
implementation
,
positional
-
or
-
keyword
arguments
passed
as
keyword
arguments
become
keyword
-
only
.
IDLE
IDLE
----
----
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment