Change argument storage for compatibility with Python 3.6.
Review Request #9518 — Created Jan. 22, 2018 and submitted
The function call opcodes in Python 3.5 and earlier contained a
parameter indicating how many positional and keyword arguments were
being passed to the function. KGB 1.0 made use of this to determine what
specific positional and keyword parameters to pass on to the original or
fake function for a spy.Python 3.6 removed this information from the opcodes, instead depending
more on the data provided on the stack before the call. The parameter
contained the total count of arguments (positional and keyword) for most
of its function call opcodes, but one didn't even include that.
Unfortunately, we don't have access to the data passed on the stack, and
parsing the bytecode to get it is far too complex and slow.To solve this, and provide Python 3.6 support, we no longer try to
replicate the original function call exactly. Instead, we pass all
positional and keyword arguments defined on the funtction.
Realistically, this should be fine, because under the hood Python will
pass the function all positional and keyword arguments anyway
(positional is required, and defaults are injected into the keyword
arguments dictionary and then passed to the function).This does change some behavior, however. Prior to this, if a positional
value was passed to a keyword argument, or a positional argument was
specified as a keyword argument, consumers could check for this, since
the exact way the parameters were passed would be stored. Now, that's
not the case. No matter how you pass a parameter, it will go in the list
of positional arguments or keyword arguments based on the signature of
the function.In a way, this is better, because callers are more likely to change
than function signatures, so by testing against the function signature,
tests can be more stable.Unfortunately, this had to remove the pretty awesome bytecode inspection
and nestedexec()
that we had before, but in the end that's probably
for the best.
Unit tests pass on Python 2.7, 3.4, 3.5, and 3.6.