• 
      

    Change argument storage for compatibility with Python 3.6.

    Review Request #9518 — Created Jan. 22, 2018 and submitted — Latest diff uploaded

    Information

    kgb
    master
    ce9d18b...

    Reviewers

    kgb

    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 nested exec() 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.