32
votes

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?

8
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. - David Moles
@DavidMoles: new is deprecated, but types are fine, thanks. - georg

8 Answers

27
votes

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.

19
votes

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.

10
votes

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.

3
votes

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

2
votes

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.

0
votes

Here's a solution with a simple code:

# -*- coding: utf-8 -*-
import math

x = [0]
exec("x[0] = 3*2")
print(x[0]) # 6
0
votes

This doesn't get the return value per say, but you can provide an empty dictionary when calling exec to retrieve any variables defined in the code.

# Python 3
ex_locals = {}
exec("a = 'Hello world!'", None, ex_locals)
print(ex_locals['a'])
# Output: Hello world!

From the Python 3 documentation on exec:

The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.

For more information, see How does exec work with locals?

-1
votes

if we need a function that is in a file in another directory, eg
we need the function1 in file my_py_file.py
located in /home/.../another_directory
we can use the following code:

def cl_import_function(a_func,py_file,in_Dir):
... import sys
... sys.path.insert(0, in_Dir)
... ax='from %s import %s'%(py_file,a_func)
... loc={}
... exec(ax, globals(), loc)
... getFx = loc[afunc]
... return getFx

test = cl_import_function('function1',r'my_py_file',r'/home/.../another_directory/')

test()
(a simple way for newbies...)