1

I'm writing a small IDE for myself in Python at the moment and want to execute the text of my editor as Python code. I get the text of my editor as a string. I want to execute the code, save the ouput & errors in string-variables (o and e) and then I render the output in my IDE.

It works fine as long as I don't have any errors (my_code). Once I execute code containing an error (my_code_with_error - commented below), it seems as if the code is not returning and "silently" crashing. In fact I even get Process finished with exit code 0 in Pycharm - probably because I switched sys.stdout for my own StringIO instance.

How can I execute the code-string, even if it has errors and then save the error / normal output in a variable as a String?

import sys
from io import StringIO

# create file-like string to capture output
codeOut = StringIO()
codeErr = StringIO()
print("Start")

my_code = """
print("Hello World!")
"""

my_code_with_error = """
print("Hello world!")
print(avfsd)  # error -> printing a variable that does not exist.
"""

print("Executing code...")
# capture output and errors
sys.stdout = codeOut
sys.stderr = codeErr

exec(my_code )  # this works fine
# exec(my_code_with_error)  # as soon as there is an error, it crashes silently.

# restore stdout and stderr
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
print("Finished code execution.")

e = codeErr.getvalue()
o = codeOut.getvalue()
print("Error: " + e)
print("Output: " + o)

codeOut.close()
codeErr.close()

PS: There was a really old question from 2009 where I got some of my code, but can't find any more recent questions concerning this topic. (How do I execute a string containing Python code in Python?)

Cribber
  • 2,513
  • 2
  • 21
  • 60
  • You likely want to spawn a child process, so you can have better control over the input and output channels. – Prune Oct 11 '20 at 18:23
  • Take a look at the [code module](https://docs.python.org/3/library/code.html#module-code) in the stdlib (which is what IDLE uses). It's mostly written in pure python, so [the source code](https://github.com/python/cpython/blob/3.9/Lib/code.py) should be instructive. – ekhumoro Oct 11 '20 at 18:38

2 Answers2

2

Try surrounding the execution code in a try/except block.

try:
    exec(my_code)
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise
Raymond Mutyaba
  • 950
  • 1
  • 9
  • 14
  • This works great! I love that I even get the class of the error with sys.exc_info. I have one problem though - if I have a correct `print` statement in front of my error-line, I don't get the output of that one, I only get the error back. Is it possible to get the "correct" output leading up to the error as well? – Cribber Oct 11 '20 at 18:39
  • You can place the string and the error output in 2 seperate print statements. `print("Unexpected error: ")` `print(sys.exc_info()[0])` – Raymond Mutyaba Oct 11 '20 at 18:45
  • Ah, I think you misunderstood me - I edited my question: The `my_code_with_error` contains two statements. The first one is working, the second one throws the error. If I would execute the two statements in Pycharm, I first get the output `Hello world!` and then it throws an error. In your solution, I only get the error from sys.exc_info() – Cribber Oct 11 '20 at 18:56
  • If you want the error-free code to run before the exception is thrown, you would have to split the commands into separate strings and call `try: exec()` for each string. – Raymond Mutyaba Oct 11 '20 at 18:57
  • 1
    I just realized your code already does what I want... sorry, my bad. The problem was EOL-errors (no quote-ending) are thrown before anything is executed so I didn't realize it is what I need. Thanks :D – Cribber Oct 11 '20 at 18:59
2

You can do that by catching the exceptions :

try:
    exec(my_code_with_error)
except Exception as e:
    print(e)

Raymond posted a similar answer as I was writting mine. Raymond's answer works in Python 2 also (see https://wiki.python.org/moin/HandlingExceptions, "General Error Handling" in "Questions") and has the advantage of outputing the error class too.

Rivers
  • 1,783
  • 1
  • 8
  • 27