Strange namespace issue

Chris Angelico rosuav at gmail.com
Mon Aug 10 16:09:00 EDT 2020


On Tue, Aug 11, 2020 at 5:44 AM Lele Gaifax <lele at metapensiero.it> wrote:
>
> Hi all,
>
> today I faced an issue that, although very easy to fix, left me wondering
> about what causes it.
>
> The context is an application that execute small scripts coming from an
> external source (say, a database). The application first compile the script,
> then execute it passing it some values: in the script I wrote this morning I
> used a list comprehension, executing a method of an instance passed in in the
> local context.
>
> The following example illustrates the problem:
>
>     class Test:
>         def create_value(self, a):
>             return a*2
>
>     script = """\
>     # This works
>     cv = test.create_value
>     x = []
>     for i in range(3):
>         x.append(cv(i))
>     print(x)
>
>     # This does not: NameError: name 'test' is not defined
>     x = [test.create_value(i) for i in range(3)]
>     print(x)
>
>     # This neither: NameError: name 'cv' is not defined
>     x = [cv(i) for i in range(3)]
>     print(x)
>     """
>
>     code = compile(script, 'script', 'exec')
>     exec(code, {}, {'test': Test()})
>
> I must be missing something, as I cannot understand why within the list
> comprehension the neither the name "test" nor the name "cv" are defined...
>
> Thanks in advance for any enlightenment!
>

Interesting. You're passing an empty globals and a non-empty locals
(the second and third arguments to exec, respectively). Is that
deliberate? By the look of this code, it's meant to be at global scope
(as if it's the top level code in a module), which is best done by
passing just a single dict to exec (it'll be both globals and locals).

The reason your first block works but the comprehensions don't is that
comprehensions act in a nested scope. I think you've hit on a curious
edge case where empty globals results in strange behaviour.

I tried your code with the empty dict removed and it appears to work,
but if you had a good reason for having separate dicts, that might
break.

ChrisA


More information about the Python-list mailing list