List comprehension scope error from Python debugger
In Python 3, you have to use the interact
command in pdb before you can access any non-global variables due to a change in the way comprehensions are implemented.
>>> def foo(): [][0]... >>> foo()Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in fooIndexError: list index out of range>>> import pdb;pdb.pm()> <stdin>(1)foo()(Pdb) x = 4(Pdb) [x for _ in range(2)]*** NameError: name 'x' is not defined(Pdb) interact*interactive*>>> [x for _ in range(2)][4, 4]>>>
pdb
seems to be running the code with:
eval(compiled_code, globals(), locals())
(or maybe even just eval(string, globals(), locals())
).
Unfortunately, on compilation Python doesn't know of the local variables. This doesn't matter normally:
import dis
dis.dis(compile("x", "", "eval"))#>>> 1 0 LOAD_NAME 0 (x)#>>> 3 RETURN_VALUE
but when another scope is introduced, such as with a list comprehension of lambda
, this compiles badly:
dis.dis(compile("(lambda: x)()", "", "eval"))#>>> 1 0 LOAD_CONST 0 (<code object <lambda> at 0x7fac20708d20, file "", line 1>)#>>> 3 LOAD_CONST 1 ('<lambda>')#>>> 6 MAKE_FUNCTION 0#>>> 9 CALL_FUNCTION 0 (0 positional, 0 keyword pair)#>>> 12 RETURN_VALUE
# The code of the internal lambdadis.dis(compile("(lambda: x)()", "", "eval").co_consts[0])#>>> 1 0 LOAD_GLOBAL 0 (x)#>>> 3 RETURN_VALUE
Note how that's a LOAD_GLOBAL
where x
is in the local scope.
Here's a totally stupid hack to get around it:
(Pdb) eval("(lambda: x)()", vars())[1, 2, 3, 3, 4]