From d59992759f56969ec2f7a8e0c6ac6f7ad81f8fcb Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 30 Apr 2020 15:12:45 -0700 Subject: [PATCH] revert #1736 (make psutil.Popen inherit from subprocess) --- HISTORY.rst | 4 +-- docs/index.rst | 47 +++++++++---------------------- psutil/__init__.py | 69 +++++++++++++++++++--------------------------- 3 files changed, 42 insertions(+), 78 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index c375934c8..6f8087db3 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,9 +7,7 @@ XXXX-XX-XX **Enhancements** -- 1729_: parallel tests on UNIX (make test-parallel). -- 1736_: psutil.Popen now inherits from subprocess.Popen instead of - psutil.Process. Also, wait(timeout=...) parameter is backported to Python 2.7. +- 1729_: parallel tests on UNIX (make test-parallel). They're twice as fast! - 1741_: "make build/install" is now run in parallel and it's about 15% faster on UNIX. diff --git a/docs/index.rst b/docs/index.rst index 08a69555a..133e69fe7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1959,24 +1959,18 @@ Process class >>> p.terminate() >>> p.wait() -Popen class ------------ - .. class:: Popen(*args, **kwargs) - A more convenient interface to stdlib `subprocess.Popen`_. - It starts a sub process and you deal with it exactly as when using - `subprocess.Popen`_, but in addition it also provides all the methods of - :class:`psutil.Process` class as a unified interface. - - .. note:: - - Unlike `subprocess.Popen`_ this class preemptively checks whether PID has - been reused on - :meth:`send_signal() `, - :meth:`terminate() ` and - :meth:`kill() ` - so that you can't accidentally terminate another process, fixing `BPO-6973`_. + Starts a sub-process via `subprocess.Popen`_, and in addition it provides + all the methods of :class:`psutil.Process` in a single class. + For method names common to both classes such as + :meth:`send_signal() `, + :meth:`terminate() `, + :meth:`kill() ` and + :meth:`wait() ` + :class:`psutil.Process` implementation takes precedence. + This may have some advantages, like making sure PID has not been reused, + fixing `BPO-6973`_. >>> import psutil >>> from subprocess import PIPE @@ -1992,25 +1986,10 @@ Popen class 0 >>> - *timeout* parameter of `subprocess.Popen.wait`_ is backported for Python < 3.3. - :class:`psutil.Popen` objects are supported as context managers via the with - statement (added to Python 3.2). On exit, standard file descriptors are - closed, and the process is waited for. This is supported on all Python - versions. - - >>> import psutil, subprocess - >>> with psutil.Popen(["ifconfig"], stdout=subprocess.PIPE) as proc: - >>> log.write(proc.stdout.read()) - - - .. versionchanged:: 4.4.0 added context manager support. - - .. versionchanged:: 5.7.1 inherit from `subprocess.Popen`_ instead of - :class:`psutil.Process`. - - .. versionchanged:: 5.7.1 backporint `subprocess.Popen.wait`_ **timeout** - parameter on old Python versions. + .. versionchanged:: 4.4.0 added context manager support + .. versionchanged:: 5.7.1 wait() invokes :meth:`wait() ` + instead of `subprocess.Popen.wait`_. Windows services ================ diff --git a/psutil/__init__.py b/psutil/__init__.py index 028ab0494..eb6d6c1be 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -1289,12 +1289,12 @@ def wait(self, timeout=None): # ===================================================================== -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 @@ def __init__(self, *args, **kwargs): # 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 @@ def __getattribute__(self, name): 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). On Python 2.7 we don't + # have timeout arg, so this acts as a backport. + ret = Process.wait(self, timeout) + self.__subproc.returncode = ret + return ret # =====================================================================