Issue 14611: inspect.getargs fails on some anonymous tuples (original) (raw)

How to reproduce

Take the following two functions:

>>> def f(l, (x, y)):
...    sup = max(u*x + v*y for u, v in l)
...    return ((u, v) for u, v in l if u*x + v*y == sup)

>>> def g((x, y)):
...    def h():
...        return x + y
...    return h

Inspect.getargs will throw an exception on the former and return a wrong result on the latter::

>>> import inspect
>>> inspect.getargs(f.__code__)
Traceback (most recent call last):
...
IndexError: list index out of range

>>> inspect.getargs(g.__code__)
Arguments(args=['h'], varargs=None, keywords=None)

# h is most definitely not an argument of g!

Analysis

If you disassemble the two functions, you'll see that in both cases the anonymous tuples are unpacked using STORE_DEREF::

>>> import dis
>>> dis.disassemble(f.__code__)
  1           0 LOAD_FAST                1 (.1)
              3 UNPACK_SEQUENCE          2
              6 STORE_DEREF              0 (x)
              9 STORE_DEREF              2 (y)
<BLANKLINE>
  2          12 LOAD_GLOBAL              0 (max)
...

>>> dis.disassemble(g.__code__)
  1           0 LOAD_FAST                0 (.0)
              3 UNPACK_SEQUENCE          2
              6 STORE_DEREF              0 (x)
              9 STORE_DEREF              1 (y)
<BLANKLINE>
  2          12 LOAD_CLOSURE             1 (y)
             15 LOAD_CLOSURE             0 (x)
             18 BUILD_TUPLE              2
             21 LOAD_CONST               1 (<code object h ...>)
             24 MAKE_CLOSURE             0
             27 STORE_FAST               3 (h)
<BLANKLINE>
  4          30 LOAD_FAST                3 (h)
             33 RETURN_VALUE        \

However, the implementation of inspect.getargs only looks for UNPACK_TUPLE, UNPACK_SEQUENCE, STORE_FAST.

Notes

The version of Python used is::

>>> import sys
>>> sys.version_info[:3]
(2, 7, 3)

I think this should do.

inspect.getargs is now looking for STORE_DEREF besides STORE_FAST, and is making sure that the appropriate namespace (locals vs cell + free vars) is selected depending on the opcode.

The only changes to the test suite are three additional tests, based on the two examples above.