The accepted answer is non-working code and also doesn't have the same returns as the original (Python 3) function. It is similar enough, however, that it is not CC BY 4.0 Martijn Pieters because it is copied from Python and if any license applied to the trivial code (I'm against licensing of trivial code entirely since it hinders innovation and proving ownership or originality is difficult, so stackoverflow is likely violating various licenses including the GPL by adding additional restrictions by re-licensing things people paste and tacitly claim as their own without citing sources), that would be at the GitHub link below. It isn't "backporting" if the code isn't usable in the same way. You will just have "AttributeError: 'tuple' object has no attribute 'returncode'" if you try to use it the Python 3 way without changing your code (client code using the function). You may change your code but then it won't be compatible with Python 3.
Either way, the code in the accepted answer itself won't even run due to:
"TypeError: init() got an unexpected keyword argument 'stderr'" (since stderr isn't an argument for "CalledProcessError" in either 2 or 3)
"AttributeError: 'Popen' object has no attribute 'args'" (args is only available in Python 3)
Therefore, consider changing the accepted answer.
To be more Pythonic by taking advantage of duck typing and a polyfill (your client code can stay the same and use a different definition shown below for the run method and the returned object's class), here is an implementation that is compatible with Python 3:
import subprocess
import sys
if sys.version_info.major >= 3:
from subprocess import CompletedProcess
else:
# Add a polyfill to Python 2
class CompletedProcess:
def __init__(self, args, returncode, stdout=None, stderr=None):
self.args = args
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
def check_returncode(self):
if self.returncode != 0:
err = subprocess.CalledProcessError(self.returncode, self.args, output=self.stdout)
raise err
return self.returncode
def sp_run(*popenargs, **kwargs):
input = kwargs.pop("input", None)
check = kwargs.pop("handle", False)
if input is not None:
if 'stdin' in kwargs:
raise ValueError('stdin and input arguments may not both be used.')
kwargs['stdin'] = subprocess.PIPE
process = subprocess.Popen(*popenargs, **kwargs)
try:
outs, errs = process.communicate(input)
except:
process.kill()
process.wait()
raise
returncode = process.poll()
if check and returncode:
raise subprocess.CalledProcessError(returncode, popenargs, output=outs)
return CompletedProcess(popenargs, returncode, stdout=outs, stderr=errs)
subprocess.run = sp_run
# ^ This polyfill allows it work on Python 2 or 3 the same way
The most up-to-date and tested version is at a module I made: https://github.com/Hierosoft/hierosoft/blob/main/hierosoft/__init__.py. This code has been tested using test cases that work in Python 2 and 3 (my "nopackage" script here for example: https://github.com/Poikilos/nopackage which requires the module).
NOTE: The class doesn't have the same repr string and may have other minor differences (You can use the real code from Python 3 itself at the following URL according to its license--see class CalledProcess in: https://github.com/python/cpython/blob/master/Lib/subprocess.py --that license also applies to my code if any, but I release it as CC0 since it is what I consider trivial--see explanation in parenthesis above).
#IRejectTheInvalidAutomaticLicenseForMyPost