1

When I get hold of a code object (via internals like .func_code or __code__ in Python 3), is there any way of calling this piece of code? Simply calling it does not work:

def f(): pass

f.func_code()

results in

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'code' object is not callable

This can come in handy when you like to unit-test a nested function:

def f():
  def g():
    return 3
  f.x = g
  return g() + 1

f.func_code.co_consts[1]

results in

<code object g at 0x7f123991b930, file "<stdin>", line 2>

Of course, this piece of code still needs a context etc. but that's not my question here.

martineau
  • 119,623
  • 25
  • 170
  • 301
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • 1
    You can `eval` or `exec` them. – vaultah Nov 09 '16 at 11:03
  • You could create a new function and assign `func_code` to it. See the `add_self()` function in [this answer](http://stackoverflow.com/a/3454053/355230) that shows doing something like that. – martineau Nov 09 '16 at 11:30
  • @vaultah That's exactly the solution for me! Do you care to put that in an A, so I can accept it, just to close this Q properly? – Alfe Nov 09 '16 at 12:51
  • 1
    No, but you can post it yourself. :) – vaultah Nov 09 '16 at 12:53
  • @vaultah My code objects have "free variables" and I always get an error message when giving them to `eval()`. Any idea how to call these? – Alfe Nov 09 '16 at 15:03
  • @vaultah And how can I give parameters to the code object when evaluating it using `eval()`? – Alfe Nov 09 '16 at 15:17
  • 1
    @Alfe you may get the `function` type (`types.FunctionType`) and use it to construct a new function object. For the description of parameters see `types.FunctionType.__doc__`. – vaultah Nov 09 '16 at 15:30

1 Answers1

2

One can eval() or exec them.

If they have free variables (e. g. for the code of nested functions whose outer function has local variables defined before the nested function), this is not directly possible (eval or exec raise a TypeError in this case).

Also, passing arguments to the code is not directly possible.

But one can create a wrapping function for the given code on-the-fly. This function can normally be called (using f(…)), passing arguments in the usual way. This is done using types.FunctionType. To achieve references to free variables, one must use a trick in order to get the correct data type as Python expects it. See the code below for an example:

def f(v1=1):
  v2 = 2
  def g(v3=4):
    return v1 + v2 + v3 + 8
  return g() + 16

def freeVar(val):
  def nested():
    return val
  return nested.__closure__[0]

def nested(outer, innerName, **freeVars):
  if isinstance(outer, (types.FunctionType, types.MethodType)):
    outer = outer.func_code
  for const in outer.co_consts:
    if isinstance(const, types.CodeType) and const.co_name == innerName:
      return types.FunctionType(const, globals(), None, None, tuple(
          freeVar(freeVars[name]) for name in const.co_freevars))

nestedG = nested(f, 'g', v1=1, v2=2)
print nestedG(4)  # will print 15
Alfe
  • 56,346
  • 20
  • 107
  • 159