Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I call a function from another function using exec

Tags:

python

say, I have very simple piece of code

py = """
a = 1
print (f'before all {a=}')

def bar(n):
    print(f'bye {n=}')

def foo(n):
    print(f'hello {n=}')
    bar(n)

bar ('just works')    
foo ('sara')
"""

loc = {}
glo = {}
bytecode = compile(py, "script", "exec")
exec(bytecode, glo, loc)

as you can see I defined two functions: bar & foo and called both of them with results:

before all a=1
bye n='just works'
hello n='sara'
Traceback (most recent call last):
  File "/home/bla-bla/pythonProject/main_deco2.py", line 43, in <module>
    exec(bytecode, glo, loc)
  File "script", line 13, in <module>
  File "script", line 10, in foo
NameError: name 'bar' is not defined

and this leaves me puzzled, as I don't understand why function foo doesn't see bar when just a second ago I was able to call bar without a problem ?

like image 579
k31 Avatar asked Nov 15 '25 12:11

k31


2 Answers

From the docs:

If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.

That's not what you want, so don't provide a locals.

glo = {}
exec(bytecode, glo)

Output, for reference:

before all a=1
bye n='just works'
hello n='sara'
bye n='sara'
like image 60
wjandrea Avatar answered Nov 18 '25 04:11

wjandrea


An important diagnostic:

>>> dis.dis(bytecode)
  2           0 LOAD_CONST               0 (1)
              2 STORE_NAME               0 (a)

  3           4 LOAD_NAME                1 (print)
              6 LOAD_CONST               1 ('before all a=')
              8 LOAD_NAME                0 (a)
             10 FORMAT_VALUE             2 (repr)
             12 BUILD_STRING             2
             14 CALL_FUNCTION            1
             16 POP_TOP

I omitted most of the result, but the first few lines show what is going on. Accesses to a and bar use the LOAD_NAME and STORE_NAME opcodes, which use the locals first. (It cannot use LOAD_FAST because it is not a compiled function.) To work around this, simply mark the necessary names as global explicitly.

Unfortunately, I don't have a good answer for why it does this.

like image 24
Karl Knechtel Avatar answered Nov 18 '25 06:11

Karl Knechtel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!