-3

I'm looking for a function that will let me execute code passed as a string, but also return a value upon completion. I have found Python's exec and eval, each of which manage to do a part of what I want:

  • exec lets me execute some lines of code which I pass as a string: print to the console; set or change variables; write to files etc.
  • eval lets me evaluate a single expression and returns the value of this expression.

However, the functionality I want is to combine these: with a single function call, I want to execute some arbitrary code, and then return a value, which might be dependent on the code executed.

To contextualise, I want to modify the in-built Pickle __reduce__ method so that I can execute some code in the background while the object un-pickles. However, at the end of that code execution, I still want to return the original object that was pickled.

Pickle's __reduce__ has to return a function which is used to reassemble the object on un-pickling, so I want a use of eval and exec that lets me combine their usage into a single function call.

As an example, my code might look something like this:

def __reduce__(self):
    code = """with open("flag.txt", "w") as f:\n\tf.write("A flag I have left!")\ndict()"""

    return exec, (code, ), None, None, iter(self.items())

The odd return formatting is a quirk of Pickle. The oddly formatted code string should do this:

with open("flag.txt", "w") as f:
    f.write("A flag I have left")
dict()  # I'm trying to get the intepreter to 'evaluate' this final line
    

However, this doesn't work, as exec just does nothing with this final line, and returns None. If I swap, and use eval instead, then I get an error too, as eval can't do anything with the lines above.

I ave tried using the in-built compile method, but this doesn't actually seem to help because eval still won't evaluate compiled execution code.

I also see that this problem has popped up elsewhere on SO (here and here) but I'm unsatisfied with the answers provided, because they involve defining new functions, which are then useless in the context of getting Pickle to execute them on un-pickling, where the interpreter is naive of their definition.

Is there any way to neatly combine these expressions to achieve arbitrary execution as well as returning a value?

whatf0xx
  • 108
  • 7
  • Okay, so what is your **question**? What happened when you tried using `exec` and `eval`? What is supposed to happen instead, and how is that different? What exactly do you need a "solution" for, and why is "the behaviour of Python's return when it is faced with an or statement." relevant to the problem? What actually is the difficulty you are encountering? – Karl Knechtel Dec 24 '22 at 17:33
  • Or did you mean "how can I have the code automatically choose whether to use `exec` or `eval`, according to the input?" In this case - it isn't remotely clear how you intend for the system to work, or what **concrete problem you are trying to solve** (where is the input coming from? Why is it useful to use either `exec` or `eval` on that input at all?). – Karl Knechtel Dec 24 '22 at 17:35

1 Answers1

-2

The best solution I could find to this problem is one based on some code from Yannic Kilcher.

You can combine the functions like this:

eval("exec(exec_code) or to_return")

eval will always try to return the value of the expression you have passed. If you pass a conditional expression, like the one above, then it will try and evaluate each part in turn to find the value of the whole conditional. As such, it will run your exec code, achieving what you need there, and then, finding that it evaluates to None, will return whatever the value of to_return is, because of the or. Therefore, if you make to_return your dictionary object constructor, then your code will run the exec statement first upon un-pickling, and then return a dictionary object as intended.

whatf0xx
  • 108
  • 7