10000 Variable Not Defined When Using Dictionary Comprehension Inside a Function Scope [Pdb] · Issue #100185 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

Variable Not Defined When Using Dictionary Comprehension Inside a Function Scope [Pdb] #100185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
e-baumer opened this issue Dec 12, 2022 · 6 comments
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@e-baumer
Copy link

Bug report

I receive a NameError for a variable defined in a function scope when trying to execute a dictionary comprehension in pdb. The variable is part of the local scope as seen with locals()

For the following code:

def somefunc(fltrlist):
    d1 = {
        "A": [1, 2, 3],
        "B": [4, 5, 6],
        "C": [7, 8, 9]
    }

    import pdb;pdb.set_trace()
    d2 = {
        k: v for k, v in d1.items() if k in fltrlist
    }

klist = ["A", "B"]
somefunc(klist)

When I enter pdb and try to run the dictionary comprehension I get the following:

➜ python3 scope_test.py 
> scope_test.py(10)somefunc()
-> d2 = {
(Pdb) {k: v for k, v in d1.items() if k in fltrlist}
*** NameError: name 'fltrlist' is not defined
(Pdb)

The variable is part of the local scope,

(Pdb) locals()
{'d1': {'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}, 'pdb': <module 'pdb' from '/usr/lib/python3.10/pdb.py'>, 'fltrlist': ['A', 'B']}
(Pdb) fltrlist
['A', 'B']
(Pdb) 

If I try the same thing but not defined within a function it works.

fltrlist  = ["A", "B"]
d1 = {
    "A": [1, 2, 3],
    "B": [4, 5, 6],
    "C": [7, 8, 9]
}

import pdb;pdb.set_trace()
d2 = {
    k: v for k, v in d1.items() if k in fltrlist
}

When in pdb the dictionary comprehension works fine.

➜ python3 scope_test.py
> scope_test.py(11)<module>()
-> d2 = {
(Pdb) {k: v for k, v in d1.items() if k in fltrlist}
{'A': [1, 2, 3], 'B': [4, 5, 6]}
(Pdb) 

Your environment

  • Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
  • Pop!_OS 22.04 (jammy) - 6.0.6-76060006-generic
@e-baumer e-baumer added the type-bug An unexpected behavior, bug, or error label Dec 12, 2022
@sobolevn sobolevn added the stdlib Python modules in the Lib dir label Dec 12, 2022
@sobolevn
Copy link
Member
sobolevn commented Dec 12, 2022

This can be reproduced with just compile and exec (this is how they are used in pdb):

def somefunc(fltrlist):
    d1 = {"A": [1, 2, 3]}
    source = '{k: v for k, v in d1.items() if k in fltrlist}'
    code = compile(source + '\n', '<stdin>', 'single')
    exec(code, globals(), locals())

klist = ["A", "B"]
somefunc(klist)

Outputs:

Traceback (most recent call last):
  File "/Users/sobolev/Desktop/cpython/ex.py", line 8, in <module>
    somefunc(klist)
  File "/Users/sobolev/Desktop/cpython/ex.py", line 5, in somefunc
    exec(code, globals(), locals())
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <dictcomp>
NameError: name 'fltrlist' is not defined

@sobolevn
Copy link
Member

Looks like this happens because LOAD_GLOBAL is used for loading fltrlist, the result of dis.dis(code):

Disassembly of <code object <dictcomp> at 0x10a812240, file "<stdin>", line 1>:
  1           0 RESUME                   0
              2 BUILD_MAP                0
              4 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                18 (to 46)
             10 UNPACK_SEQUENCE          2
             14 STORE_FAST               1 (k)
             16 STORE_FAST               2 (v)
             18 LOAD_FAST                1 (k)
             20 LOAD_GLOBAL              0 (fltrlist)
             32 CONTAINS_OP              0
             34 POP_JUMP_IF_TRUE         1 (to 38)
             36 JUMP_BACKWARD           16 (to 6)
        >>   38 LOAD_FAST                1 (k)
             40 LOAD_FAST                2 (v)
             42 MAP_ADD                  2
             44 JUMP_BACKWARD           20 (to 6)
        >>   46 END_FOR
             48 RETURN_VALUE

@sobolevn
Copy link
Member

One more note: this happens on all versions of python, starting from 3.6 (I don't have any earlier versions installed). And even 2.7 🥼

@wrongnull
Copy link
Contributor

@sobolevn I think it is impossible to fix without reimplementing this builtin function due to compile knows nothing about enclosing scope and as a consequence generates incorrect instructions

@terryjreedy
Copy link
Member

https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries explains:

The iterable expression in the leftmost for clause is evaluated directly in the enclosing scope and then passed as an argument to the implicitly nested scope. Subsequent for clauses and any filter condition in the leftmost for clause cannot be evaluated in the enclosing scope ...

Note that the global scope is always available.

@e-baumer It would have been better to have asked for an explanation under Help at https://discuss.python.org/. The docs should be consulted before reporting puzzling behavior as a bug.

@carljm
Copy link
Member
carljm commented Apr 10, 2023

I think the original report from @e-baumer here is valid, but is a duplicate of #65360. The key factor that makes this confusing in this particular case is that comprehensions (the dict comprehension, in this case) are compiled as nested functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

5 participants
0