summaryrefslogtreecommitdiff
path: root/psutil
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-05-01 02:20:52 -0700
committerGitHub <noreply@github.com>2020-05-01 11:20:52 +0200
commit8d8a7804d159e5b80378000b57bbfbaf63ce6e8f (patch)
treef8a0b45d618ef69faca670bdde8077bb0c20d427 /psutil
parent3480e1b05f3e98744a1b6aa6fe286caac86e6bbd (diff)
downloadpsutil-8d8a7804d159e5b80378000b57bbfbaf63ce6e8f.tar.gz
Revert #1736: Popen inheriting from subprocess (#1744)
Diffstat (limited to 'psutil')
-rw-r--r--psutil/__init__.py69
-rw-r--r--psutil/tests/__init__.py21
-rwxr-xr-xpsutil/tests/runner.py40
3 files changed, 55 insertions, 75 deletions
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 028ab049..acf5ee79 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -1289,12 +1289,12 @@ _as_dict_attrnames = set(
# =====================================================================
-class Popen(subprocess.Popen):
+class Popen(Process):
"""A more convenient interface to stdlib subprocess.Popen class.
It starts a sub process and deals with it exactly as when using
- subprocess.Popen class, but in addition it also provides all the
- methods of psutil.Process class as a unified interface:
-
+ subprocess.Popen class but in addition also provides all the
+ properties and methods of psutil.Process class as a unified
+ interface:
>>> import psutil
>>> from subprocess import PIPE
>>> p = psutil.Popen(["python", "-c", "print 'hi'"], stdout=PIPE)
@@ -1310,16 +1310,12 @@ class Popen(subprocess.Popen):
>>> p.wait(timeout=2)
0
>>>
-
- In addition, it backports the following functionality:
- * "with" statement (Python 3.2)
- * wait(timeout=...) parameter (Python 3.3)
-
+ For method names common to both classes such as kill(), terminate()
+ and wait(), psutil.Process implementation takes precedence.
Unlike subprocess.Popen this class pre-emptively checks whether PID
- has been reused on send_signal(), terminate() and kill(), so that
+ has been reused on send_signal(), terminate() and kill() so that
you don't accidentally terminate another process, fixing
http://bugs.python.org/issue6973.
-
For a complete documentation refer to:
http://docs.python.org/3/library/subprocess.html
"""
@@ -1328,21 +1324,21 @@ class Popen(subprocess.Popen):
# Explicitly avoid to raise NoSuchProcess in case the process
# spawned by subprocess.Popen terminates too quickly, see:
# https://github.com/giampaolo/psutil/issues/193
- self.__psproc = None
- subprocess.Popen.__init__(self, *args, **kwargs)
- self.__psproc = Process(self.pid)
- self.__psproc._init(self.pid, _ignore_nsp=True)
+ self.__subproc = subprocess.Popen(*args, **kwargs)
+ self._init(self.__subproc.pid, _ignore_nsp=True)
def __dir__(self):
- return sorted(set(dir(subprocess.Popen) + dir(Process)))
+ return sorted(set(dir(Popen) + dir(subprocess.Popen)))
- # Introduced in Python 3.2.
- if not hasattr(subprocess.Popen, '__enter__'):
+ def __enter__(self):
+ if hasattr(self.__subproc, '__enter__'):
+ self.__subproc.__enter__()
+ return self
- def __enter__(self):
- return self
-
- def __exit__(self, *args, **kwargs):
+ def __exit__(self, *args, **kwargs):
+ if hasattr(self.__subproc, '__exit__'):
+ return self.__subproc.__exit__(*args, **kwargs)
+ else:
if self.stdout:
self.stdout.close()
if self.stderr:
@@ -1360,30 +1356,21 @@ class Popen(subprocess.Popen):
return object.__getattribute__(self, name)
except AttributeError:
try:
- return object.__getattribute__(self.__psproc, name)
+ return object.__getattribute__(self.__subproc, name)
except AttributeError:
raise AttributeError("%s instance has no attribute '%s'"
% (self.__class__.__name__, name))
- def send_signal(self, sig):
- return self.__psproc.send_signal(sig)
-
- def terminate(self):
- return self.__psproc.terminate()
-
- def kill(self):
- return self.__psproc.kill()
-
def wait(self, timeout=None):
- if sys.version_info < (3, 3):
- # backport of timeout parameter
- if self.returncode is not None:
- return self.returncode
- ret = self.__psproc.wait(timeout)
- self.returncode = ret
- return ret
- else:
- return super(Popen, self).wait(timeout)
+ if self.__subproc.returncode is not None:
+ return self.__subproc.returncode
+ # Note: using psutil's wait() on UNIX should make no difference.
+ # On Windows it does, because PID can still be alive (see
+ # _pswindows.py counterpart addressing this). Python 2.7 doesn't
+ # have timeout arg, so this acts as a backport.
+ ret = Process.wait(self, timeout)
+ self.__subproc.returncode = ret
+ return ret
# =====================================================================
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 8ce76304..6cdf3bc8 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -486,21 +486,12 @@ def terminate(proc_or_pid, sig=signal.SIGTERM, wait_timeout=GLOBAL_TIMEOUT):
from psutil._psposix import wait_pid
def wait(proc, timeout):
- if sys.version_info < (3, 3) and \
- isinstance(proc, subprocess.Popen) and \
- not isinstance(proc, psutil.Popen):
- # subprocess.Popen instance: emulate missing timeout arg.
- ret = None
- try:
- ret = psutil.Process(proc.pid).wait(timeout)
- except psutil.NoSuchProcess:
- # Needed to kill zombies.
- if POSIX:
- ret = wait_pid(proc.pid, timeout)
- proc.returncode = ret
- return ret
- else:
- return proc.wait(timeout)
+ try:
+ return psutil.Process(proc.pid).wait(timeout)
+ except psutil.NoSuchProcess:
+ # Needed to kill zombies.
+ if POSIX:
+ return wait_pid(proc.pid, timeout)
def term_subproc(proc, timeout):
try:
diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py
index ef135c8d..a7f74964 100755
--- a/psutil/tests/runner.py
+++ b/psutil/tests/runner.py
@@ -53,11 +53,21 @@ from psutil.tests import TOX
VERBOSITY = 1 if TOX else 2
FAILED_TESTS_FNAME = '.failed-tests.txt'
NWORKERS = psutil.cpu_count() or 1
+USE_COLORS = term_supports_colors() and not APPVEYOR
HERE = os.path.abspath(os.path.dirname(__file__))
loadTestsFromTestCase = unittest.defaultTestLoader.loadTestsFromTestCase
+def cprint(msg, color, bold=False, file=None):
+ if file is None:
+ file = sys.stderr if color == 'red' else sys.stdout
+ if USE_COLORS:
+ print_color(msg, color, bold=bold, file=file)
+ else:
+ print(msg, file=file)
+
+
class TestLoader:
testdir = HERE
@@ -110,24 +120,21 @@ class TestLoader:
class ColouredResult(unittest.TextTestResult):
- def _print_color(self, s, color, bold=False):
- print_color(s, color, bold=bold, file=self.stream)
-
def addSuccess(self, test):
unittest.TestResult.addSuccess(self, test)
- self._print_color("OK", "green")
+ cprint("OK", "green")
def addError(self, test, err):
unittest.TestResult.addError(self, test, err)
- self._print_color("ERROR", "red", bold=True)
+ cprint("ERROR", "red", bold=True)
def addFailure(self, test, err):
unittest.TestResult.addFailure(self, test, err)
- self._print_color("FAIL", "red")
+ cprint("FAIL", "red")
def addSkip(self, test, reason):
unittest.TestResult.addSkip(self, test, reason)
- self._print_color("skipped: %s" % reason.strip(), "brown")
+ cprint("skipped: %s" % reason.strip(), "brown")
def printErrorList(self, flavour, errors):
flavour = hilite(flavour, "red", bold=flavour == 'ERROR')
@@ -139,11 +146,7 @@ class ColouredTextRunner(unittest.TextTestRunner):
A coloured text runner which also prints failed tests on KeyboardInterrupt
and save failed tests in a file so that they can be re-run.
"""
-
- if term_supports_colors() and not APPVEYOR:
- resultclass = ColouredResult
- else:
- resultclass = unittest.TextTestResult
+ resultclass = ColouredResult if USE_COLORS else unittest.TextTestResult
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -180,11 +183,11 @@ class ColouredTextRunner(unittest.TextTestRunner):
def _exit(self, success):
if success:
- print_color("SUCCESS", "green", bold=True)
+ cprint("SUCCESS", "green", bold=True)
safe_rmpath(FAILED_TESTS_FNAME)
sys.exit(0)
else:
- print_color("FAILED", "red", bold=True)
+ cprint("FAILED", "red", bold=True)
self._write_last_failed()
sys.exit(1)
@@ -228,8 +231,8 @@ class ParallelRunner(ColouredTextRunner):
par_suite = self._parallelize(par_suite)
# run parallel
- print_color("starting parallel tests using %s workers" % NWORKERS,
- "green", bold=True)
+ cprint("starting parallel tests using %s workers" % NWORKERS,
+ "green", bold=True)
t = time.time()
par = self._run(par_suite)
par_elapsed = time.time() - t
@@ -239,7 +242,7 @@ class ParallelRunner(ColouredTextRunner):
orphans = psutil.Process().children()
gone, alive = psutil.wait_procs(orphans, timeout=1)
if alive:
- print_color("alive processes %s" % alive, "red")
+ cprint("alive processes %s" % alive, "red")
reap_children()
# run serial
@@ -274,8 +277,7 @@ class ParallelRunner(ColouredTextRunner):
def get_runner(parallel=False):
def warn(msg):
- print_color(msg + " Running serial tests instead.",
- "red", file=sys.stderr)
+ cprint(msg + " Running serial tests instead.", "red")
if parallel:
if psutil.WINDOWS:
warn("Can't run parallel tests on Windows.")