kofhearts
kofhearts

Reputation: 3794

how to catch subprocess call exception in python?

I have a python code as follows:

    try:
        print("Running code " + str(sub.id))
        r = subprocess.call("node codes.js > outputs.txt", shell=True)
    except:
        print("Error running submission code id " + str(sub.id))

The code is running node command using subprocess.call. The node command is running codes.js file. Sometimes if there is error in code like if there is document. command then the code throws error.

With try and except it is not catching the error thrown when the node command fails.

The error thrown is as follows

There is document. line in the code so node cannot understand that line so it throws error.

/home/kofhearts/homework/codes.js:5
                document.getElementById("outputalert").innerHTML = "Hacked";
                ^

ReferenceError: document is not defined
    at solve (/home/kofhearts/homework/codes.js:5:3)
    at Object.<anonymous> (/home/kofhearts/homework/codes.js:13:28)
    at Module._compile (internal/modules/cjs/loader.js:1068:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
    at Module.load (internal/modules/cjs/loader.js:933:32)
    at Function.Module._load (internal/modules/cjs/loader.js:774:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at internal/main/run_main_module.js:17:47
Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    main()
  File "manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/home/kofhearts/.virtualenvs/myenv/lib/python3.7/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/home/kofhearts/.virtualenvs/myenv/lib/python3.7/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/kofhearts/.virtualenvs/myenv/lib/python3.7/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/kofhearts/.virtualenvs/myenv/lib/python3.7/site-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)
  File "/home/kofhearts/homework/assignments/management/commands/police.py", line 73, in handle
    if isCorrect(data.strip()[:-1], sub.question.outputs, sub.question, sub.code):
  File "/home/kofhearts/homework/assignments/views.py", line 566, in isCorrect
    givenans = [json.loads(e.strip()) for e in received.split('|')]
  File "/home/kofhearts/homework/assignments/views.py",

How is it possible to catch the error when subprocess.call fails? Thanks for the help!

Upvotes: 0

Views: 1995

Answers (1)

2e0byo
2e0byo

Reputation: 5954

How is it possible to catch the error when subprocess.call fails?

The 'standard' way to do this is to use subprocess.run:

from subprocess import run, CalledProcessError
cmd = ["node", "code.js"]
try:
    r = run(cmd, check=True, capture_output=True, encoding="utf8")
    with open("outputs.txt", "w") as f:
        f.write(r.stdout)
except CalledProcessError as e:
    print("oh no!")
    print(e.stderr)

Note that I have dropped the redirect and done it in python. You might be able to redirect with shell=True, but it's a whole security hole you don't need just for sending stdout to a file.

check=True ensures it will throw with non-zero return state.

capture_output=True is handy, because stderr and stdout are passed through to the exception, allowing you to retrieve them there. Thank to @OlvinRoght for pointing that out.

Lastly, it is possible to check manually:

r = run(cmd, capture_output=True, encoding="utf8")
if r.returncode:
    print("Failed", r.stderr, r.stdout)
else:
    print("Success", r.stdout)

I would generally avoid this pattern as

  • try is free for success (and we expect this to succeed)
  • catching exceptions is how we normally handle problems, so it's the Right Way (TM)

but YMMV.

Upvotes: 4

Related Questions