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
7e213255
Commit
7e213255
authored
Mar 30, 2010
by
Benjamin Peterson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add inspect.getcallargs, which binds function arguments like a normal call #3135
Patch by George Sakkis
parent
ec71794c
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
306 additions
and
4 deletions
+306
-4
Doc/library/inspect.rst
Doc/library/inspect.rst
+26
-0
Lib/inspect.py
Lib/inspect.py
+84
-1
Lib/test/test_inspect.py
Lib/test/test_inspect.py
+193
-3
Misc/NEWS
Misc/NEWS
+3
-0
No files found.
Doc/library/inspect.rst
View file @
7e213255
...
...
@@ -504,6 +504,32 @@ Classes and functions
metatype is in use, cls will be the first element of the tuple.
.. function:: getcallargs(func[, *args][, **kwds])
Bind the *args* and *kwds* to the argument names of the Python function or
method *func*, as if it was called with them. For bound methods, bind also the
first argument (typically named ``self``) to the associated instance. A dict
is returned, mapping the argument names (including the names of the ``*`` and
``**`` arguments, if any) to their values from *args* and *kwds*. In case of
invoking *func* incorrectly, i.e. whenever ``func(*args, **kwds)`` would raise
an exception because of incompatible signature, an exception of the same type
and the same or similar message is raised. For example::
>>> from inspect import getcallargs
>>> def f(a, b=1, *pos, **named):
... pass
>>> getcallargs(f, 1, 2, 3)
{'a': 1, 'named': {}, 'b': 2, 'pos': (3,)}
>>> getcallargs(f, a=2, x=4)
{'a': 2, 'named': {'x': 4}, 'b': 1, 'pos': ()}
>>> getcallargs(f)
Traceback (most recent call last):
...
TypeError: f() takes at least 1 argument (0 given)
.. versionadded:: 2.7
.. _inspect-stack:
The interpreter stack
...
...
Lib/inspect.py
View file @
7e213255
...
...
@@ -17,7 +17,7 @@ Here are some of the useful functions provided by this module:
getmodule() - determine the module that an object came from
getclasstree() - arrange classes so as to represent their hierarchy
getargspec(), getargvalues() - get info about function arguments
getargspec(), getargvalues()
, getcallargs()
- get info about function arguments
formatargspec(), formatargvalues() - format an argument spec
getouterframes(), getinnerframes() - get info about frames
currentframe() - get the current stack frame
...
...
@@ -884,6 +884,89 @@ def formatargvalues(args, varargs, varkw, locals,
specs
.
append
(
formatvarkw
(
varkw
)
+
formatvalue
(
locals
[
varkw
]))
return
'('
+
string
.
join
(
specs
,
', '
)
+
')'
def
getcallargs
(
func
,
*
positional
,
**
named
):
"""Get the mapping of arguments to values.
A dict is returned, with keys the function argument names (including the
names of the * and ** arguments, if any), and values the respective bound
values from 'positional' and 'named'."""
args
,
varargs
,
varkw
,
defaults
=
getargspec
(
func
)
f_name
=
func
.
__name__
arg2value
=
{}
# The following closures are basically because of tuple parameter unpacking.
assigned_tuple_params
=
[]
def
assign
(
arg
,
value
):
if
isinstance
(
arg
,
str
):
arg2value
[
arg
]
=
value
else
:
assigned_tuple_params
.
append
(
arg
)
value
=
iter
(
value
)
for
i
,
subarg
in
enumerate
(
arg
):
try
:
subvalue
=
next
(
value
)
except
StopIteration
:
raise
ValueError
(
'need more than %d %s to unpack'
%
(
i
,
'values'
if
i
>
1
else
'value'
))
assign
(
subarg
,
subvalue
)
try
:
next
(
value
)
except
StopIteration
:
pass
else
:
raise
ValueError
(
'too many values to unpack'
)
def
is_assigned
(
arg
):
if
isinstance
(
arg
,
str
):
return
arg
in
arg2value
return
arg
in
assigned_tuple_params
if
ismethod
(
func
)
and
func
.
im_self
is
not
None
:
# implicit 'self' (or 'cls' for classmethods) argument
positional
=
(
func
.
im_self
,)
+
positional
num_pos
=
len
(
positional
)
num_total
=
num_pos
+
len
(
named
)
num_args
=
len
(
args
)
num_defaults
=
len
(
defaults
)
if
defaults
else
0
for
arg
,
value
in
zip
(
args
,
positional
):
assign
(
arg
,
value
)
if
varargs
:
if
num_pos
>
num_args
:
assign
(
varargs
,
positional
[
-
(
num_pos
-
num_args
):])
else
:
assign
(
varargs
,
())
elif
0
<
num_args
<
num_pos
:
raise
TypeError
(
'%s() takes %s %d %s (%d given)'
%
(
f_name
,
'at most'
if
defaults
else
'exactly'
,
num_args
,
'arguments'
if
num_args
>
1
else
'argument'
,
num_total
))
elif
num_args
==
0
and
num_total
:
raise
TypeError
(
'%s() takes no arguments (%d given)'
%
(
f_name
,
num_total
))
for
arg
in
args
:
if
isinstance
(
arg
,
str
)
and
arg
in
named
:
if
is_assigned
(
arg
):
raise
TypeError
(
"%s() got multiple values for keyword "
"argument '%s'"
%
(
f_name
,
arg
))
else
:
assign
(
arg
,
named
.
pop
(
arg
))
if
defaults
:
# fill in any missing values with the defaults
for
arg
,
value
in
zip
(
args
[
-
num_defaults
:],
defaults
):
if
not
is_assigned
(
arg
):
assign
(
arg
,
value
)
if
varkw
:
assign
(
varkw
,
named
)
elif
named
:
unexpected
=
next
(
iter
(
named
))
if
isinstance
(
unexpected
,
unicode
):
unexpected
=
unexpected
.
encode
(
sys
.
getdefaultencoding
(),
'replace'
)
raise
TypeError
(
"%s() got an unexpected keyword argument '%s'"
%
(
f_name
,
unexpected
))
unassigned
=
num_args
-
len
([
arg
for
arg
in
args
if
is_assigned
(
arg
)])
if
unassigned
:
num_required
=
num_args
-
num_defaults
raise
TypeError
(
'%s() takes %s %d %s (%d given)'
%
(
f_name
,
'at least'
if
defaults
else
'exactly'
,
num_required
,
'arguments'
if
num_required
>
1
else
'argument'
,
num_total
))
return
arg2value
# -------------------------------------------------- stack frame extraction
Traceback
=
namedtuple
(
'Traceback'
,
'filename lineno function code_context index'
)
...
...
Lib/test/test_inspect.py
View file @
7e213255
import
re
import
sys
import
types
import
unittest
import
inspect
import
datetime
from
UserList
import
UserList
from
UserDict
import
UserDict
from
test.test_support
import
run_unittest
,
check_py3k_warnings
...
...
@@ -557,10 +560,197 @@ class TestClassesAndFunctions(unittest.TestCase):
self
.
assertIn
((
'm1'
,
'method'
,
D
),
attrs
,
'missing plain method'
)
self
.
assertIn
((
'datablob'
,
'data'
,
A
),
attrs
,
'missing data'
)
class
TestGetcallargsFunctions
(
unittest
.
TestCase
):
# tuple parameters are named '.1', '.2', etc.
is_tuplename
=
re
.
compile
(
r'^\
.
\d+$'
).
match
def
assertEqualCallArgs
(
self
,
func
,
call_params_string
,
locs
=
None
):
locs
=
dict
(
locs
or
{},
func
=
func
)
r1
=
eval
(
'func(%s)'
%
call_params_string
,
None
,
locs
)
r2
=
eval
(
'inspect.getcallargs(func, %s)'
%
call_params_string
,
None
,
locs
)
self
.
assertEqual
(
r1
,
r2
)
def
assertEqualException
(
self
,
func
,
call_param_string
,
locs
=
None
):
locs
=
dict
(
locs
or
{},
func
=
func
)
try
:
eval
(
'func(%s)'
%
call_param_string
,
None
,
locs
)
except
Exception
,
ex1
:
pass
else
:
self
.
fail
(
'Exception not raised'
)
try
:
eval
(
'inspect.getcallargs(func, %s)'
%
call_param_string
,
None
,
locs
)
except
Exception
,
ex2
:
pass
else
:
self
.
fail
(
'Exception not raised'
)
self
.
assertIs
(
type
(
ex1
),
type
(
ex2
))
self
.
assertEqual
(
str
(
ex1
),
str
(
ex2
))
def
makeCallable
(
self
,
signature
):
"""Create a function that returns its locals(), excluding the
autogenerated '.1', '.2', etc. tuple param names (if any)."""
code
=
(
"lambda %s: dict(i for i in locals().items() "
"if not is_tuplename(i[0]))"
)
return
eval
(
code
%
signature
,
{
'is_tuplename'
:
self
.
is_tuplename
})
def
test_plain
(
self
):
f
=
self
.
makeCallable
(
'a, b=1'
)
self
.
assertEqualCallArgs
(
f
,
'2'
)
self
.
assertEqualCallArgs
(
f
,
'2, 3'
)
self
.
assertEqualCallArgs
(
f
,
'a=2'
)
self
.
assertEqualCallArgs
(
f
,
'b=3, a=2'
)
self
.
assertEqualCallArgs
(
f
,
'2, b=3'
)
# expand *iterable / **mapping
self
.
assertEqualCallArgs
(
f
,
'*(2,)'
)
self
.
assertEqualCallArgs
(
f
,
'*[2]'
)
self
.
assertEqualCallArgs
(
f
,
'*(2, 3)'
)
self
.
assertEqualCallArgs
(
f
,
'*[2, 3]'
)
self
.
assertEqualCallArgs
(
f
,
'**{"a":2}'
)
self
.
assertEqualCallArgs
(
f
,
'b=3, **{"a":2}'
)
self
.
assertEqualCallArgs
(
f
,
'2, **{"b":3}'
)
self
.
assertEqualCallArgs
(
f
,
'**{"b":3, "a":2}'
)
# expand UserList / UserDict
self
.
assertEqualCallArgs
(
f
,
'*UserList([2])'
)
self
.
assertEqualCallArgs
(
f
,
'*UserList([2, 3])'
)
self
.
assertEqualCallArgs
(
f
,
'**UserDict(a=2)'
)
self
.
assertEqualCallArgs
(
f
,
'2, **UserDict(b=3)'
)
self
.
assertEqualCallArgs
(
f
,
'b=2, **UserDict(a=3)'
)
# unicode keyword args
self
.
assertEqualCallArgs
(
f
,
'**{u"a":2}'
)
self
.
assertEqualCallArgs
(
f
,
'b=3, **{u"a":2}'
)
self
.
assertEqualCallArgs
(
f
,
'2, **{u"b":3}'
)
self
.
assertEqualCallArgs
(
f
,
'**{u"b":3, u"a":2}'
)
def
test_varargs
(
self
):
f
=
self
.
makeCallable
(
'a, b=1, *c'
)
self
.
assertEqualCallArgs
(
f
,
'2'
)
self
.
assertEqualCallArgs
(
f
,
'2, 3'
)
self
.
assertEqualCallArgs
(
f
,
'2, 3, 4'
)
self
.
assertEqualCallArgs
(
f
,
'*(2,3,4)'
)
self
.
assertEqualCallArgs
(
f
,
'2, *[3,4]'
)
self
.
assertEqualCallArgs
(
f
,
'2, 3, *UserList([4])'
)
def
test_varkw
(
self
):
f
=
self
.
makeCallable
(
'a, b=1, **c'
)
self
.
assertEqualCallArgs
(
f
,
'a=2'
)
self
.
assertEqualCallArgs
(
f
,
'2, b=3, c=4'
)
self
.
assertEqualCallArgs
(
f
,
'b=3, a=2, c=4'
)
self
.
assertEqualCallArgs
(
f
,
'c=4, **{"a":2, "b":3}'
)
self
.
assertEqualCallArgs
(
f
,
'2, c=4, **{"b":3}'
)
self
.
assertEqualCallArgs
(
f
,
'b=2, **{"a":3, "c":4}'
)
self
.
assertEqualCallArgs
(
f
,
'**UserDict(a=2, b=3, c=4)'
)
self
.
assertEqualCallArgs
(
f
,
'2, c=4, **UserDict(b=3)'
)
self
.
assertEqualCallArgs
(
f
,
'b=2, **UserDict(a=3, c=4)'
)
# unicode keyword args
self
.
assertEqualCallArgs
(
f
,
'c=4, **{u"a":2, u"b":3}'
)
self
.
assertEqualCallArgs
(
f
,
'2, c=4, **{u"b":3}'
)
self
.
assertEqualCallArgs
(
f
,
'b=2, **{u"a":3, u"c":4}'
)
def
test_tupleargs
(
self
):
f
=
self
.
makeCallable
(
'(b,c), (d,(e,f))=(0,[1,2])'
)
self
.
assertEqualCallArgs
(
f
,
'(2,3)'
)
self
.
assertEqualCallArgs
(
f
,
'[2,3]'
)
self
.
assertEqualCallArgs
(
f
,
'UserList([2,3])'
)
self
.
assertEqualCallArgs
(
f
,
'(2,3), (4,(5,6))'
)
self
.
assertEqualCallArgs
(
f
,
'(2,3), (4,[5,6])'
)
self
.
assertEqualCallArgs
(
f
,
'(2,3), [4,UserList([5,6])]'
)
def
test_multiple_features
(
self
):
f
=
self
.
makeCallable
(
'a, b=2, (c,(d,e))=(3,[4,5]), *f, **g'
)
self
.
assertEqualCallArgs
(
f
,
'2, 3, (4,[5,6]), 7'
)
self
.
assertEqualCallArgs
(
f
,
'2, 3, *[(4,[5,6]), 7], x=8'
)
self
.
assertEqualCallArgs
(
f
,
'2, 3, x=8, *[(4,[5,6]), 7]'
)
self
.
assertEqualCallArgs
(
f
,
'2, x=8, *[3, (4,[5,6]), 7], y=9'
)
self
.
assertEqualCallArgs
(
f
,
'x=8, *[2, 3, (4,[5,6])], y=9'
)
self
.
assertEqualCallArgs
(
f
,
'x=8, *UserList([2, 3, (4,[5,6])]), '
'**{"y":9, "z":10}'
)
self
.
assertEqualCallArgs
(
f
,
'2, x=8, *UserList([3, (4,[5,6])]), '
'**UserDict(y=9, z=10)'
)
def
test_errors
(
self
):
f0
=
self
.
makeCallable
(
''
)
f1
=
self
.
makeCallable
(
'a, b'
)
f2
=
self
.
makeCallable
(
'a, b=1'
)
# f0 takes no arguments
self
.
assertEqualException
(
f0
,
'1'
)
self
.
assertEqualException
(
f0
,
'x=1'
)
self
.
assertEqualException
(
f0
,
'1,x=1'
)
# f1 takes exactly 2 arguments
self
.
assertEqualException
(
f1
,
''
)
self
.
assertEqualException
(
f1
,
'1'
)
self
.
assertEqualException
(
f1
,
'a=2'
)
self
.
assertEqualException
(
f1
,
'b=3'
)
# f2 takes at least 1 argument
self
.
assertEqualException
(
f2
,
''
)
self
.
assertEqualException
(
f2
,
'b=3'
)
for
f
in
f1
,
f2
:
# f1/f2 takes exactly/at most 2 arguments
self
.
assertEqualException
(
f
,
'2, 3, 4'
)
self
.
assertEqualException
(
f
,
'1, 2, 3, a=1'
)
self
.
assertEqualException
(
f
,
'2, 3, 4, c=5'
)
self
.
assertEqualException
(
f
,
'2, 3, 4, a=1, c=5'
)
# f got an unexpected keyword argument
self
.
assertEqualException
(
f
,
'c=2'
)
self
.
assertEqualException
(
f
,
'2, c=3'
)
self
.
assertEqualException
(
f
,
'2, 3, c=4'
)
self
.
assertEqualException
(
f
,
'2, c=4, b=3'
)
self
.
assertEqualException
(
f
,
'**{u"
\
u03c0
\
u03b9
": 4}'
)
# f got multiple values for keyword argument
self
.
assertEqualException
(
f
,
'1, a=2'
)
self
.
assertEqualException
(
f
,
'1, **{"a":2}'
)
self
.
assertEqualException
(
f
,
'1, 2, b=3'
)
# XXX: Python inconsistency
# - for functions and bound methods: unexpected keyword 'c'
# - for unbound methods: multiple values for keyword 'a'
#self.assertEqualException(f, '1, c=3, a=2')
f
=
self
.
makeCallable
(
'(a,b)=(0,1)'
)
self
.
assertEqualException
(
f
,
'1'
)
self
.
assertEqualException
(
f
,
'[1]'
)
self
.
assertEqualException
(
f
,
'(1,2,3)'
)
class
TestGetcallargsMethods
(
TestGetcallargsFunctions
):
def
setUp
(
self
):
class
Foo
(
object
):
pass
self
.
cls
=
Foo
self
.
inst
=
Foo
()
def
makeCallable
(
self
,
signature
):
assert
'self'
not
in
signature
mk
=
super
(
TestGetcallargsMethods
,
self
).
makeCallable
self
.
cls
.
method
=
mk
(
'self, '
+
signature
)
return
self
.
inst
.
method
class
TestGetcallargsUnboundMethods
(
TestGetcallargsMethods
):
def
makeCallable
(
self
,
signature
):
super
(
TestGetcallargsUnboundMethods
,
self
).
makeCallable
(
signature
)
return
self
.
cls
.
method
def
assertEqualCallArgs
(
self
,
func
,
call_params_string
,
locs
=
None
):
return
super
(
TestGetcallargsUnboundMethods
,
self
).
assertEqualCallArgs
(
*
self
.
_getAssertEqualParams
(
func
,
call_params_string
,
locs
))
def
assertEqualException
(
self
,
func
,
call_params_string
,
locs
=
None
):
return
super
(
TestGetcallargsUnboundMethods
,
self
).
assertEqualException
(
*
self
.
_getAssertEqualParams
(
func
,
call_params_string
,
locs
))
def
_getAssertEqualParams
(
self
,
func
,
call_params_string
,
locs
=
None
):
assert
'inst'
not
in
call_params_string
locs
=
dict
(
locs
or
{},
inst
=
self
.
inst
)
return
(
func
,
'inst,'
+
call_params_string
,
locs
)
def
test_main
():
run_unittest
(
TestDecorators
,
TestRetrievingSourceCode
,
TestOneliners
,
TestBuggyCases
,
TestInterpreterStack
,
TestClassesAndFunctions
,
TestPredicates
)
run_unittest
(
TestDecorators
,
TestRetrievingSourceCode
,
TestOneliners
,
TestBuggyCases
,
TestInterpreterStack
,
TestClassesAndFunctions
,
TestPredicates
,
TestGetcallargsFunctions
,
TestGetcallargsMethods
,
TestGetcallargsUnboundMethods
)
if
__name__
==
"__main__"
:
test_main
()
Misc/NEWS
View file @
7e213255
...
...
@@ -32,6 +32,9 @@ Core and Builtins
Library
-------
- Issue #3135: Add inspect.getcallargs, which binds arguments to a function like
a normal call.
- Backwards incompatible change: Unicode codepoints line tabulation (0x0B) and
form feed (0x0C) are now considered linebreaks, as specified in Unicode
Standard Annex #14. See issue #7643.
...
...
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