This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: inspect.getargs fails on some anonymous tuples
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Scott Sanderson, iritkatriel, michael.foord, ncoghlan, r.david.murray, taschini
Priority: low Keywords: needs review, patch

Created on 2012-04-18 08:35 by taschini, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
issue_14611.patch taschini, 2012-04-19 09:13 review
Messages (7)
msg158599 - (view) Author: Stefano Taschini (taschini) * Date: 2012-04-18 08:35
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)
msg158610 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-04-18 12:47
Formal parameter tuple unpacking was removed in Python3, so this is a Python2-only issue.  Would you like to submit a patch for Python2?
msg158620 - (view) Author: Stefano Taschini (taschini) * Date: 2012-04-18 13:52
I'll give it a try.
msg158708 - (view) Author: Stefano Taschini (taschini) * Date: 2012-04-19 09:13
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.
msg252489 - (view) Author: Scott Sanderson (Scott Sanderson) * Date: 2015-10-07 23:51
This issue is the root cause of at least two open issues in IPython:

https://github.com/ipython/ipython/issues/8293
https://github.com/ipython/ipython/issues/8205

Testing locally, the patch supplied here fixes both of those issues.  Is there still work that needs to be done here?
msg252491 - (view) Author: Scott Sanderson (Scott Sanderson) * Date: 2015-10-07 23:54
Note also that a much simpler repro for this issue is:

inspect.getargs(((x for _ in [0]) for x in [0]).gi_code)

This triggers the same issue because the inner generator expression closes over the loop variable of the outer expression, which causes us to hit the STORE_DEREF case instead of the STORE_FAST case.
msg375822 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2020-08-23 18:48
I think this was fixed here:

https://github.com/python/cpython/commit/3b23004112aefffa72a3763916d78f12b9e056fe

and in any case it's a 2.7-only issue, so can this ticket be closed?
History
Date User Action Args
2022-04-11 14:57:29adminsetgithub: 58816
2020-08-31 08:47:03corona10setstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2020-08-23 18:48:25iritkatrielsetnosy: + iritkatriel
messages: + msg375822
2015-10-07 23:54:34Scott Sandersonsetmessages: + msg252491
2015-10-07 23:51:55Scott Sandersonsetnosy: + Scott Sanderson
messages: + msg252489
2012-05-21 06:17:08eric.araujosetkeywords: + needs review
stage: needs patch -> patch review
2012-04-19 09:13:10taschinisetfiles: + issue_14611.patch
keywords: + patch
messages: + msg158708
2012-04-18 13:52:07taschinisetmessages: + msg158620
2012-04-18 12:47:58r.david.murraysetpriority: normal -> low

nosy: + r.david.murray
messages: + msg158610

stage: needs patch
2012-04-18 10:51:54pitrousetnosy: + ncoghlan, michael.foord
2012-04-18 08:35:08taschinicreate