A much cleaner way to do this would be to wrap the function in another function, pass through the *args, **kwargs
, and then use those values when you need them, instead of trying to reconstruct them after the fact. But if you don't want to do that…
In Python 3.x (except very early versions), this is easy, as poke's answer explains. Even easier with 3.3+, with inspect.signature
, inspect.getargvalues
, and inspect.Signature.bind_partial
and friends.
In Python 2.x, there is no way to do this. The exception only has the string 'f() takes exactly 2 arguments (1 given)'
in its args
.
Except… in CPython 2.x specifically, it's possible with enough ugly and brittle hackery.
You've got a traceback, so you've got its tb_frame
and tb_lineno
… which is everything you need. So as long as the source is available, the inspect
module makes it easy to get the actual function call expression. Then you just need to parse it (via ast
) to get the arguments passed, and compare to the function's signature (which, unfortunately, isn't nearly as easy to get in 2.x as in 3.3+, but between f.func_defaults
, f.func_code.co_argcount
, etc., you can reconstruct it).
But what if the source isn't available? Well, between tb_frame.f_code
and tb_lasti
, you can find out where the function call was in the bytecode. And the dis
module makes that relatively easy to parse. In particular, right before the call, the positional arguments and the name-value pairs for keyword arguments were all pushed on the stack, so you can easily see which names got pushed, and how many positional values, and reconstruct the function call that way. Which you compare to the signature in the same way.
Of course that relies on the some assumptions about how CPython's compiler builds bytecode. It would be perfectly legal to do things in all kinds of different orders as long as the stack ended up with the right values. So, it's pretty brittle. But I think there are already better reasons not to do it.