Skip to content

Commit

Permalink
revert #1736 (make psutil.Popen inherit from subprocess)
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo committed Apr 30, 2020
1 parent 3480e1b commit d599927
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 78 deletions.
4 changes: 1 addition & 3 deletions HISTORY.rst
Expand Up @@ -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.

Expand Down
47 changes: 13 additions & 34 deletions docs/index.rst
Expand Up @@ -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() <psutil.Process.send_signal()>`,
:meth:`terminate() <psutil.Process.terminate()>` and
:meth:`kill() <psutil.Process.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() <psutil.Process.send_signal()>`,
:meth:`terminate() <psutil.Process.terminate()>`,
:meth:`kill() <psutil.Process.kill()>` and
:meth:`wait() <psutil.Process.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
Expand All @@ -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() <psutil.Process.wait()>`
instead of `subprocess.Popen.wait`_.

Windows services
================
Expand Down
69 changes: 28 additions & 41 deletions psutil/__init__.py
Expand Up @@ -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)
Expand All @@ -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
"""
Expand All @@ -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:
Expand All @@ -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


# =====================================================================
Expand Down

0 comments on commit d599927

Please sign in to comment.