How to Get Exit Code When Using Python Subprocess Communicate Method

How to get exit code when using Python subprocess communicate method?

Popen.communicate will set the returncode attribute when it's done(*). Here's the relevant documentation section:

Popen.returncode 
The child return code, set by poll() and wait() (and indirectly by communicate()).
A None value indicates that the process hasn’t terminated yet.

A negative value -N indicates that the child was terminated by signal N (Unix only).

So you can just do (I didn't test it but it should work):

import subprocess as sp
child = sp.Popen(openRTSP + opts.split(), stdout=sp.PIPE)
streamdata = child.communicate()[0]
rc = child.returncode

(*) This happens because of the way it's implemented: after setting up threads to read the child's streams, it just calls wait.

How to get exit code from subprocess.Popen?

According to the linked to documentation

The child return code, set by poll() and wait() (and indirectly by
communicate()). A None value indicates that the process hasn’t terminated yet.

A negative value -N indicates that the child was terminated by signal N (Unix only).

You have not called poll or wait so returncode won't be set.

On the other hand if you look in to the source code for fx check_output you see that they are directly using the return value from poll to examine the return code. They know that the process has terminated at that point because they have called wait earlier. If you don't know that you would have to call the wait method instead (but note the deadlock possibility noted in the documentation).

Normally the program will have terminated when you've read all of stdout/stderr, but that's not guaranteed and that may be what you're seeing. Either the program or the OS can close stdout (and stderr) before the process actually terminates and then by just calling poll immediately after you've read all the output from the program might fail.

Why does subprocess.run return an exit code of 2 but no errors in log?

First of all it would be nice to know what OS you are using. Basically the stderr and stdout are file descriptors (1 and 2 by default.)

In your case the subcommand writes nothing to 1 and 2 file descriptors (so to stderr and stdout) and of course the return code is 2.

If you run the following code:

import subprocess

result = subprocess.run(["echo", "test"])
print(result.returncode, result.stdout, result.stderr)

You get:

>>> python3 test.py
test
0 None None

It means the echo runs correctly and writes the test to the STDOUT but the result.stdout doesn't contain it. This happens but you don't redirect the STDOUT of the command to a PIPE.

If you change the code to:

import subprocess

result = subprocess.run(["echo", "test"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(result.returncode, result.stdout, result.stderr)

You will get:

>>> python3 test.py
0 b'test\n' b''

So now the result.stdout contains the STDOT of the called command in bytes. It is True for STDERR.

If a set the shell parameter to True, I can run invalid (not existed) command as you can see below:

import subprocess

result = subprocess.run(["invalid_cmd"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
print(result.returncode, result.stdout, result.stderr)

In this case the output is:

>>> python3 test.py
127 b'' b'/bin/sh: invalid_cmd: command not found\n'

It means the return code is 127 and the STDOUT is empty but the STDERR contains /bin/sh: invalid_cmd: command not found\n message in bytes.

If you do the above things but you don't see any output then your called command doesn't write the STDOUT or STDERR so you should look into the called command.

Cannot get exit code when running subprocesses in python using Popen and start /wait

CMD's start command always succeeds overall if it successfully executes the given command via CreateProcess or ShellExecuteEx. It succeeds even if it's instructed to /wait and ends up setting %errorlevel% to a non-zero value. You can see this by running (start /wait exit 1) && echo success. The && operator only executes the right-hand expression if the left-hand expression succeeds.

To work around this, you can manually exit the initial shell using the !errorlevel! value that gets set by start. For example:

command = 'exit 1'
shell_path = os.environ.get('ComSpec', 'cmd.exe')
start_command = 'start "" /wait {}'.format(command)
cmdline = '"{shell}" /v:on /c "({command}) & exit !errorlevel!"'.format(
shell=shell_path, command=start_command)

proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

Note that the above Popen call does not use the shell=True argument because the shell needs to be run manually with the /v:on option. This enables delayed expansion of environment variables, using "!" instead of "%".

That said, you don't need the shell for your stated objective. Simply have the child process create a new console by passing the CREATE_NEW_CONSOLE creation flag as follows:

proc = subprocess.Popen(args, creationflags=subprocess.CREATE_NEW_CONSOLE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

Portable way to check if subprocess exit code indicates a success?

The docs for subprocess effectively require that 0 means success, e.g. for subprocess.run (the all-in-one high level synchronous API), when check=True, the docs specify:

If check is true, and the process exits with a non-zero exit code, a CalledProcessError exception will be raised.

OpenVMS isn't actually a supported target for CPython, and all supported targets obey that rule to my knowledge.

Presumably the third-party OpenVMS port would have to include porting proper error-checking (even if it doesn't change the exit codes, check=True should be changed to make it only raise the exception when the child fails). So the simplest solution would be to write your code as:

try:
proc = subprocess.run(..., check=True)
except subprocess.CalledProcessError:
# Handle failure here

and if OpenVMS didn't make check=True work the logically correct way in their port, file a bug against them to make it logically match the intended behavior (exception raised when subprocess fails, even if docs say "non-zero"). Aside from that, your only option is checking sys.platform and manually checking the error code a different way on OpenVMS (I have no idea what OpenVMS reports for sys.platform, you'd have to check it yourself).

Get exit code and stderr from subprocess call

Try this version:

import subprocess
try:
output = subprocess.check_output(
cmnd, stderr=subprocess.STDOUT, shell=True, timeout=3,
universal_newlines=True)
except subprocess.CalledProcessError as exc:
print("Status : FAIL", exc.returncode, exc.output)
else:
print("Output: \n{}\n".format(output))

This way you will print the output only if the call was successful.
In case of a CalledProcessError you print the return code and the output.

subprocess.check_output return code

You can get the error code and results from the exception that is raised.

This can be done through the fields returncode and output.

For example:

import subprocess

try:
grepOut = subprocess.check_output("grep " + "test" + " tmp", shell=True)
except subprocess.CalledProcessError as grepexc:
print("error code", grepexc.returncode, grepexc.output)

Retrieving the output of subprocess.call()

Output from subprocess.call() should only be redirected to files.

You should use subprocess.Popen() instead. Then you can pass subprocess.PIPE for the stderr, stdout, and/or stdin parameters and read from the pipes by using the communicate() method:

from subprocess import Popen, PIPE

p = Popen(['program', 'arg1'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, err = p.communicate(b"input data that is passed to subprocess' stdin")
rc = p.returncode

The reasoning is that the file-like object used by subprocess.call() must have a real file descriptor, and thus implement the fileno() method. Just using any file-like object won't do the trick.

See here for more info.



Related Topics



Leave a reply



Submit