3

I am trying to execute an external process in Python 3.4. When my "command" is wrong the program just crashes. How can I handle the errors gracefully and recover?

    # Call a system process
    try:
        pipe = subprocess.Popen(command,
                                stdout=subprocess.PIPE)
    except (OSError,
            subprocess.CalledProcessError) as error:
        print("Failed to execute command")
        print(command)
        print("Error: " + error.output)

    while True:
        output = pipe.stdout.readline()
        if not output:
            print("Finished...")
            break

        output = output.decode()
        output = output.strip("\r")
        output = output.strip("\n")
        print(output)

When I call an invalid command. I get a crash like:

C:\SDKs\Python34\python.exe C:\Users\user\Documents\GitHub\PyQtApps\QtDeploy\src\main.py
Traceback (most recent call last):
Executing:  windeployqt.exe  C:\Users\user\Documents\GitHub\SpaCentralSoftBin\GUIController.exe
  File "C:\Users\user\Documents\GitHub\PyQtApps\QtDeploy\src\forms\maindialog.py", line 81, in execute_windeployqt
Failed to execute command
    stdout=subprocess.PIPE)
windeployqt.exe  C:\Users\user\Documents\GitHub\SpaCentralSoftBin\GUIController.exe
  File "C:\SDKs\Python34\lib\subprocess.py", line 858, in __init__
    restore_signals, start_new_session)
  File "C:\SDKs\Python34\lib\subprocess.py", line 1111, in _execute_child
    startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\user\Documents\GitHub\PyQtApps\QtDeploy\src\forms\maindialog.py", line 159, in on_buttonBox_clicked
    self.execute_windeployqt()
  File "C:\Users\user\Documents\GitHub\PyQtApps\QtDeploy\src\forms\maindialog.py", line 86, in execute_windeployqt
    print("Error: " + error.output)
AttributeError: 'FileNotFoundError' object has no attribute 'output'
dano
  • 91,354
  • 19
  • 222
  • 219
Zingam
  • 4,498
  • 6
  • 28
  • 48
  • 1
    unrelated: you could read the output line by line using a for-loop: `for line in p.stdout: sys.stdout.buffer.write(line)` `# + do something else with the line`. To avoid decoding bytes manually, you could specify `universal_newlines=True` (it also converts `'\r'`, `'\r\n'` to `'\n'`) then you could read it as: `for line in p.stdout: print(line, end='')`. To merge stderr (where errors might go) with stdout, specify `stderr=subprocess.STDOUT`. – jfs Jul 31 '14 at 18:11
  • @J.F.Sebastian Thank you! This is useful. Currently I read stderr separately to display if the process returned an error. Do you know how can I check if there was an error without reading the buffer (like that in the for-loop). I need to set a boolean variable. – Zingam Jul 31 '14 at 19:03

2 Answers2

2

The larger problem is printing an error message does not "handle" an error in any meaningful sense of "handle" (although many people think it does).

Log an error message if you want and then exit your program. There are few instances where you can handle an error, for example if a configuration file can't be read, see if you can read a global configuration file or KeyboardInterrupt can be passed up to a main loop that is reading commands.

In your example, if you didn't fault in the except block, the output = pipe.stdout... would yield:

NameError: name 'pipe' is not defined

and if you caught that, there'd still be nothing meaningful to do to recover the state your code expects to be in. You'll also move the traceback you do get to a place where the fault isn't.

A good idiom for your situation is

except SomeError:
    log what you want
    raise

where an empty raise re-raises the last exception which will propagate up, forming a full stacktrace which will give you a better idea of where the fault is. If you don't do this, you are discarding useful information.

msw
  • 42,753
  • 9
  • 87
  • 112
  • My program is just a simple GUI in PyQt for a console application. A simple message box is just enough to inform the user to select different parameters. Based on the previous poster's advice I found a serious flaw in my code anyway. Thank you for your advice. Honestly, I am a bit confused and I need to think about it. Now I have a different problem. The app reads all output when the execution is successful but fails to return when it fails. I get a message in the console only. – Zingam Jul 29 '14 at 20:23
1
FileNotFoundError' object has no attribute 'output'

Use

print("Error: " + error)

FileNotFoundErrr has these properties which might be useful:

 args
 characters_written
 errno
 filename
 filename2