How do I get the return value when using Python exec on the code object of a function?

41,728

Solution 1

Yes, you need to have the assignment within the exec statement:

>>> def foo():
...     return 5
...
>>> exec("a = foo()")
>>> a
5

This probably isn't relevant for your case since its being used in controlled testing, but be careful with using exec with user defined input.

Solution 2

A few years later, but the following snippet helped me:

the_code = '''
a = 1
b = 2
return_me = a + b
'''

loc = {}
exec(the_code, globals(), loc)
return_workaround = loc['return_me']
print(return_workaround)  # 3

exec() doesn't return anything itself, but you can pass a dict which has all the local variables stored in it after execution. By accessing it you have a something like a return.

I hope it helps someone.

Solution 3

While this is the ugliest beast ever seen by mankind, this is how you can do it by using a global variable inside your exec call:

def my_exec(code):
    exec('global i; i = %s' % code)
    global i
    return i

This is misusing global variables to get your data across the border.

>>> my_exec('1 + 2')
3

Needless to say that you should never allow any user inputs for the input of this function in there, as it poses an extreme security risk.

Solution 4

Something like this can work:

def outer():
    def inner(i):
        return i + 10


for f in outer.func_code.co_consts:
    if getattr(f, 'co_name', None) == 'inner':

        inner = type(outer)(f, globals())

        # can also use `types` module for readability:
        # inner = types.FunctionType(f, globals())

        print inner(42) # 52

The idea is to extract the code object from the inner function and create a new function based on it.

Additional work is required when an inner function can contain free variables. You'll have to extract them as well and pass to the function constructor in the last argument (closure).

Solution 5

Here's a way to return a value from exec'd code:

def exec_and_return(expression):
    exec(f"""locals()['temp'] = {expression}""")
    return locals()['temp']

I'd advise you to give an example of the problem you're trying to solve. Because I would only ever use this as a last resort.

Share:
41,728
user2433423
Author by

user2433423

Updated on July 09, 2022

Comments

  • user2433423
    user2433423 almost 2 years

    For testing purposes I want to directly execute a function defined inside of another function.

    I can get to the code object of the child function, through the code (func_code) of the parent function, but when I exec it, i get no return value.

    Is there a way to get the return value from the exec'ed code?

    • prashant
      prashant about 10 years
      I don't think you can do this with exec. You'll need to do something like georg's answer or use new as described in this answer.
    • gog
      gog about 10 years
      @DavidMoles: new is deprecated, but types are fine, thanks.
  • marsh
    marsh over 9 years
    That is so weird, Especially that you dont need to declare the variable before hand.
  • Chris
    Chris over 9 years
    This isn't working for me. exec('ds_id = kim_processors.%s.process(%s)' % (function.python_module, model_id)) does populate ds_id, as I can see in the Django locals debug output. However I then run cursor.execute("""select e.id as ... where dataset_id=%s""", (ds_id)) and get error "Exception Type: NameError Exception Value: name 'ds_id' is not defined". Yet locals shows ds_id is indeed set (to 14). Is there some restriction on its scope?
  • Chris
    Chris over 9 years
    Also tried exec('import kim_processors.%s as processor_module' % function.python_module) then running ds_id = processor_module.process(model_id). Again, I can see processor_module listed in localvars, but I get the same NameError exception "Exception Value: name 'processor_module' is not defined"
  • wnnmaw
    wnnmaw over 9 years
    @Chris, I'm not sure what the issue is, but it sounds like a good question for the site. Try posting it separately as a question and refer here to show what you've tried.
  • Chris
    Chris over 9 years
    Thanks @wnnmaw that's exactly what I did! Sorry forgot to follow up on this thread. The question and answers are here There is a better way of importing (importlib) and the duplicate question describes the scope issue beautifully. Thanks for responding.
  • Chris Fowl
    Chris Fowl about 5 years
    Thank you! +1. I struggled a lot with getting a reference to a module given a string and this is what works. You can use this by giving the string as parameter code and it will return the actual module.
  • misantroop
    misantroop over 4 years
    This doesn't work, exec needs to be launched with globals, exec("a = foo()", globals())
  • wnnmaw
    wnnmaw over 4 years
    @misantroop, just tested on Python 3.7.0 and 3.8.1, works fine on both. What system are you using where it doesn't work?
  • Doug
    Doug almost 4 years
    @wnnmaw this doesn't work in general only when a is a global variable. You can see it not working by simply moving your code into a def foo(): ... exec('a ==' statement. May I suggest updating your answer to show that it only works when the variable you modify is a global? eg. In a function you need to, eg. def foo(): exec('global a\na = 5').
  • GoodJuJu
    GoodJuJu over 3 years
    Whilst your answer may be useful, the question was asked 6 years ago and already has 5 answers, although none have been accepted.
  • Patrick McGloin
    Patrick McGloin about 3 years
    This is good. But you could just use this code and not have a seperate dictionary for "loc": exec(the_code) return_workaround = locals()['return_me']
  • Ank
    Ank about 3 years
    Similar answers are already there posted. Better to look at new questions to answer where community needs you.
  • Namwon Kim
    Namwon Kim about 3 years
    @Ank You are so right. But, I also want to share a simple method. Thank you.
  • bomben
    bomben over 2 years
    This works, but inside the code I am unable to reference to variables that where defined outside it.
  • Mr. B.
    Mr. B. over 2 years
    @bomben I didn't test it, but can't you pass your outer variables? It should be mentioned in the docs of exec(). :)
  • Mathieu Longtin
    Mathieu Longtin over 2 years
    or: return eval(expression)
  • Mr. B.
    Mr. B. over 2 years
    @aceminer Happy to read that! :)