How to pass arguments to the __code__ of a function?

13,032

Solution 1

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

Solution 2

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.

Solution 3

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

Solution 4

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

Solution 5

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)
Share:
13,032
Turion
Author by

Turion

Updated on June 28, 2022

Comments

  • Turion
    Turion almost 2 years

    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.

    • Admin
      Admin about 13 years
      Just call the function, period, full stop.
    • Turion
      Turion about 13 years
      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?
    • Admin
      Admin about 13 years
      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.
    • user1066101
      user1066101 about 13 years
      @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?
    • Turion
      Turion about 13 years
      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.
    • Admin
      Admin about 13 years
      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 stackoverflow.com/questions/1253528/… if that's absolutely not an option.
    • Y.H Wong
      Y.H Wong about 13 years
      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.
    • XORcist
      XORcist about 13 years
      @Turion: the proper way to distribute code is with a python package.
    • user1066101
      user1066101 about 13 years
      What's wrong with simply sending the function's source back and forth?
    • Turion
      Turion about 13 years
      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.
    • user1066101
      user1066101 about 13 years
      @Turion: "the function is defined dynamically"? Without source? You mean a lambda?
    • Turion
      Turion about 13 years
      For example a definition in the body of another function. But I agree that this might be uncommon enough to forget about it.
  • Turion
    Turion about 13 years
    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
    Turion about 13 years
    I like this, like Gustav Larssons solution.
  • kindall
    kindall about 13 years
    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.
  • Turion
    Turion about 13 years
    That's an improvement. To increase the readability of your solution, I would recommend using types.FunctionType instead of type(lambda: 0).
  • Turion
    Turion almost 13 years
    This does not significantly differ from some other solutions.
  • kxr
    kxr over 8 years
    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)
  • Hack5
    Hack5 almost 5 years
    Combine this with 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
  • aliqandil
    aliqandil almost 5 years
    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__