Strange namespace issue

Lele Gaifax lele at metapensiero.it
Tue Aug 11 02:39:51 EDT 2020


Chris Angelico <rosuav at gmail.com> writes:

> 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.

Thank you Chris,

yes, you are right and indeed passing the external context in the "globals"
argument *AND* omitting the "locals" one I get the expected outcome, and yes,
I'm surprised, the snake still bites sometime, after several dozens of years :)

However, I'm still unclear on *when* it is safe & useful to pass the third
argument to exec(): while in the particular case I was working it does not
make any difference (passing the context in the second "globals" argument,
that is), in at least one other case I am relying on the fact that passing the
third argument the script "stores" there whatever it defines, and the caller
examines that to make some further decision. In other words, something like
this:

    script = """\
    def function_a():
      return ['a' * repetitions]

    def function_b():
      return [['a'] * repetitions]

    precomputed = repetitions ** 10

    def function_c():
      return precomputed
    """

    code = compile(script, 'script', 'exec')
    globs = {'repetitions': 3}
    locs = {}
    exec(code, globs, locs)

    for n in locs:
        print(f"Oh, you defined {n!r}...")
        v = locs[n]
        if callable(v):
            print("  a callable, returning", v())
        else:
            print("  a value,", v)

In this case, it would be less practical to determine what the script defined:
by any chance this case is the first I wrote, and here the choice to pass the
third argument was surely "deliberate".

But I see this is somewhat fragile, and wonder about a proper fix, but isn't
that a reasonable usage of the "locals" argument to exec()?

Ciao, lele.
-- 
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
lele at metapensiero.it  |                 -- Fortunato Depero, 1929.



More information about the Python-list mailing list