I last touched on executing code with the interactive console post but I’ve revisited the idea of entering Python code and executing it for a project I’ve been working on. Now before we start allowing users to execute code on your server is very dangerous. There is no way to make it 100% safe. If there is another way to achieve your aim without executing code then do it.
For an overview on what executing code (and importing modules) does behind the scenes, look at Armin Ronacher’s post on the subject. I will be following his suggestion of separating the compilation and execution steps. So, given the code we wish to run in a string, we will pass this to the compile function which returns bytecode. This bytecode can then be passed to the exec function to actually execute it. Just to be clear, we don’t have to split the process in two; you can run the Python code directly from a string as follows.
So having decided to split the process into separate compile and exec steps, the first issue we need to solve is capturing errors (whether compile time or runtime) and passing these back to the user. Placing the compile and exec function inside a try block is enough to detect an error and we can use the format_exc function from the traceback module to send a formatted exception message back to the user if the code causes and exception.
Now we have a robust compile and execute sequence we just need to get the results of a successful run back to the user. We could create a log type function but Jochen Ritzel posted a simple context manager to redirect the standard output. With this we can redirect the stdout to a string (actually a StringIO) and return this assuming everything runs fine.
Putting all this together gives us the final code. The function exec_code works like exec but returns exceptions or the stdout of the executed code to you as well. The last three lines are just test code to show the difference between compile time, runtime and code with no errors.