32

The following works:

def spam():
    print "spam"
exec(spam.__code__)

spam

But what if spam takes arguments?

def spam(eggs):
    print "spam and", eggs
exec(spam.__code__)

TypeError: spam() takes exactly 1 argument (0 given)

Given, that I don't have access to the function itself but only to a code object, how can I pass arguments to the code object when executing it? Is it possible with eval?

Edit: Since most readers tend not to believe in the usefulness of this, see the following use case:

I want to save small Python functions to a file so that they can be called e.g. from another computer. (Needless to say here that this usecase restricts the possible functions severely.) Pickling the function object itself can't work because this only saves the name and the module where the function is defined. Instead, I could pickle the __code__ of the function. When I unpickle it again, of course the reference to the function vanished, which is why I can't call the function. I simply don't have it at runtime.

Another usecase:

I work on several functions in one file that calculate some data and store it on the hard drive. The calculations consume a lot of time so I don't want to execute the functions every time, but only when the implementation of the function changed.

I have a version of this running for a whole module instead of a function. It works by looking at the modification time of the file where the module is implemented in. But this is not an option if I have many functions that I don't want to separate in single files.

martineau
  • 119,623
  • 25
  • 170
  • 301
Turion
  • 5,684
  • 4
  • 26
  • 42
  • 4
    Just call the function, period, full stop. –  May 03 '11 at 19:25
  • 7
    Of course I would do that in this simple example case. I'm not so stupid not to see this. But what if all I have is the code object itself? – Turion May 03 '11 at 19:28
  • 2
    Then you have a more serious problem, I'd say. You rarely (**really** rarely) need to fiddle with stuff from this level of abstraction, and by default I doubt you're in a situation where this is the case, so I suggest you make sure you get a function instead of a code object. –  May 03 '11 at 19:29
  • @Turion: What is the distinction between a function and the code object itself? Where did you get this code object from? Why isn't it just a function? – S.Lott May 03 '11 at 19:34
  • See my edit to the question for clarification and a possible usecase. @delnan If there is a possibility to solve my above mentioned pickling problem without exec or eval, I would be happy to do so. – Turion May 03 '11 at 19:39
  • There *must* be an easier way. It's impossible to think about details without knowing what exactly the functions do, but it should be quite possible to serialize only a bit of data needed to reconstruct the function (e.g. `lambda x: 10 < x < 20` can be reduced to the upper and lower bound). Also see http://stackoverflow.com/questions/1253528/is-there-an-easy-way-to-pickle-a-python-function-or-otherwise-serialize-its-code if that's absolutely not an option. –  May 03 '11 at 19:44
  • 4
    So you want to send the pickled function bytecodes across the wire and you want the client to be able to just allow code execution arbitrarily from some random location? Whoa. This is so dangerous in so many ways... What you want is some form of RPC. Don't cut corners. – Y.H Wong May 03 '11 at 19:45
  • @Turion: the proper way to distribute code is with a python package. – XORcist May 03 '11 at 19:46
  • What's wrong with simply sending the function's source back and forth? – S.Lott May 03 '11 at 19:52
  • See my second, more realistic usecase. @Y.H. Wong, möter: You are right, this is a serious security hole and therefore not a realistic usecase. @S. Lott: Good idea, as could be done with the inspect module. But I fear this doesn't work if the function is defined dynamically. – Turion May 03 '11 at 20:01
  • @Turion: "the function is defined dynamically"? Without source? You mean a `lambda`? – S.Lott May 03 '11 at 20:09
  • For example a definition in the body of another function. But I agree that this might be uncommon enough to forget about it. – Turion May 03 '11 at 20:20

6 Answers6

14

I am completely against this use of __code__.

Although I am a curious person, and this is what someone theoretically could do:

code # This is your code object that you want to execute

def new_func(eggs): pass
new_func.__code__ = code
new_func('eggs')

Again, I never want to see this used, ever. You might want to look into __import__ if you want to load code during run-time.

Gustav Larsson
  • 8,199
  • 3
  • 31
  • 51
  • I like this solution, too, but unfortunately I can only tick one of the solutions, I think. But I agree, it's really hacky. – Turion May 03 '11 at 20:07
  • 4
    You can also construct a function using the function type. e.g. `new_func = type(lambda: 0)(code, globals())` This is a little less hacky. You could then write a function to construct a wrapper function from a code object and call the resulting function. – kindall May 03 '11 at 23:49
  • 4
    That's an improvement. To increase the readability of your solution, I would recommend using `types.FunctionType` instead of `type(lambda: 0)`. – Turion May 05 '11 at 09:47
11

Can you change the function to not take any arguments? The variables is then looked up from the locals/globals where you can supply into exec:

>>> def spam():
...   print "spam and", eggs
... 
>>> exec(spam.__code__, {'eggs':'pasta'})
spam and pasta

(Why not just send the whole function as a string? Pickle "def spam(eggs): print 'spam and', eggs", and exec the string (after verification) on the other side.)

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Combine this with https://stackoverflow.com/a/33112180/5509575 and I think you have a fully working answer, although I couldn't get the linked answer to do anything – Hack5 Sep 01 '19 at 07:46
7

I think there are probably some design considerations in your larger application that could make you not care about this problem, like perhaps having some collection of 'known good and valid' functions distributed as a module that the executing agents know about or something.

That said, one hacky solution would be:

>>> def spam(eggs):
...     print "spam and %s" % eggs
...     
... 
>>> spam('bacon')
spam and bacon
>>> def util():
...     pass
...     
... 
>>> util.__code__ = spam.__code__
>>> util('bacon')
spam and bacon
>>> 
jonesy
  • 3,502
  • 17
  • 23
5

A code object is part of a function, so several answers above suggest creating a dummy function and replacing its __code__ with your codeObject. Here's another way that avoids making and throwing away a new __code__:

import new
newFunction = new.function(codeObject, globals())

(Tested in Python 2.7, where spam.__code__ is named spam.func_code.)

Jim Pivarski
  • 5,568
  • 2
  • 35
  • 47
  • 1
    This should be tagged as most right answer to the point, though a comment in the other answer using still that odd hack mentions the same via `types.FunctionType` (place and idea well hidden for new readers). Yet referrals to other answers should be removed / not be on top; and the example should be complete enough like `new.function(spam_code_object, globals())("some eggs")`. (Python 2.7 and 2.6 also support `func.__code__` besides `func.func_code`) – kxr Jan 25 '16 at 14:39
4

My method, I thing it`s more beautiful

def f(x):
    print(x, x+1)

g = type(f)(f.__code__, globals(), "optional_name")

g(3)
atomAltera
  • 1,702
  • 2
  • 19
  • 38
  • This does not significantly differ from some other solutions. – Turion Aug 12 '11 at 11:39
  • This is quite interesting, Just to be more precise, you can also provide the default arguments just in case. Add this as a keyword argument to the constructor `argdefs = f.__defaults__` – aliqandil Sep 13 '19 at 11:16
2

I don't think you can pass arguments to either exec or eval, so that they are passed to the code object.

You could resort to the string version of exec/eval, e.g. exec("spam(3)").

You could create another code object that binds the argument, and then exec this:

def spam_with_eggs():
   return spam(3)
exec(spam_with_eggs.__code__)

(I thought you could also achieve this with functools.partial, but didn't get it to work).

EDIT:

After reading your additional explanations I thought of ways to re-establish a proper function from the code object. This simple approach worked for me (in python2.5):

def bar():pass
bar.func_code = spam.func_code
bar(3)  # yields "spam and 3"
ThomasH
  • 22,276
  • 13
  • 61
  • 62