Skip to content

Commit

Permalink
PEP-3151: backport FS exceptions to Python 2 (#1544)
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo committed Jun 28, 2019
1 parent adc28e4 commit c10df5a
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 218 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ DEPS = \
ipaddress \
mock==1.0.1 \
perf \
readline \
requests \
setuptools \
sphinx \
Expand Down
27 changes: 13 additions & 14 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import collections
import contextlib
import datetime
import errno
import functools
import os
import signal
Expand All @@ -44,6 +43,8 @@
from ._common import memoize_when_activated
from ._common import wrap_numbers as _wrap_numbers
from ._compat import long
from ._compat import PermissionError
from ._compat import ProcessLookupError
from ._compat import PY3 as _PY3

from ._common import STATUS_DEAD
Expand Down Expand Up @@ -221,7 +222,7 @@

__all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
__version__ = "5.6.3"
__version__ = "5.6.4"
version_info = tuple([int(num) for num in __version__.split('.')])

_timer = getattr(time, 'monotonic', time.time)
Expand Down Expand Up @@ -1290,18 +1291,16 @@ def _send_signal(self, sig):
"calling process (os.getpid()) instead of PID 0")
try:
os.kill(self.pid, sig)
except OSError as err:
if err.errno == errno.ESRCH:
if OPENBSD and pid_exists(self.pid):
# We do this because os.kill() lies in case of
# zombie processes.
raise ZombieProcess(self.pid, self._name, self._ppid)
else:
self._gone = True
raise NoSuchProcess(self.pid, self._name)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
except ProcessLookupError:
if OPENBSD and pid_exists(self.pid):
# We do this because os.kill() lies in case of
# zombie processes.
raise ZombieProcess(self.pid, self._name, self._ppid)
else:
self._gone = True
raise NoSuchProcess(self.pid, self._name)
except PermissionError:
raise AccessDenied(self.pid, self._name)

@_assert_pid_not_reused
def send_signal(self, sig):
Expand Down
72 changes: 71 additions & 1 deletion psutil/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
"""Module which provides compatibility with older Python versions."""

import collections
import errno
import functools
import os
import sys

__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b",
"lru_cache", "which", "get_terminal_size"]
"lru_cache", "which", "get_terminal_size",
"FileNotFoundError", "PermissionError", "ProcessLookupError",
"InterruptedError", "ChildProcessError", "FileExistsError"]

PY3 = sys.version_info[0] == 3

Expand Down Expand Up @@ -38,6 +41,73 @@ def b(s):
return s


# --- exceptions


if PY3:
FileNotFoundError = FileNotFoundError # NOQA
PermissionError = PermissionError # NOQA
ProcessLookupError = ProcessLookupError # NOQA
InterruptedError = InterruptedError # NOQA
ChildProcessError = ChildProcessError # NOQA
FileExistsError = FileExistsError # NOQA
else:
# https://github.com/PythonCharmers/python-future/blob/exceptions/
# src/future/types/exceptions/pep3151.py

def instance_checking_exception(base_exception=Exception):
def wrapped(instance_checker):
class TemporaryClass(base_exception):

def __init__(self, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], TemporaryClass):
unwrap_me = args[0]
for attr in dir(unwrap_me):
if not attr.startswith('__'):
setattr(self, attr, getattr(unwrap_me, attr))
else:
super(TemporaryClass, self).__init__(*args, **kwargs)

class __metaclass__(type):
def __instancecheck__(cls, inst):
return instance_checker(inst)

def __subclasscheck__(cls, classinfo):
value = sys.exc_info()[1]
return isinstance(value, cls)

TemporaryClass.__name__ = instance_checker.__name__
TemporaryClass.__doc__ = instance_checker.__doc__
return TemporaryClass

return wrapped

@instance_checking_exception(EnvironmentError)
def FileNotFoundError(inst):
return getattr(inst, 'errno', object()) == errno.ENOENT

@instance_checking_exception(EnvironmentError)
def ProcessLookupError(inst):
return getattr(inst, 'errno', object()) == errno.ESRCH

@instance_checking_exception(EnvironmentError)
def PermissionError(inst):
return getattr(inst, 'errno', object()) in (
errno.EACCES, errno.EPERM)

@instance_checking_exception(EnvironmentError)
def InterruptedError(inst):
return getattr(inst, 'errno', object()) == errno.EINTR

@instance_checking_exception(EnvironmentError)
def ChildProcessError(inst):
return getattr(inst, 'errno', object()) == errno.ECHILD

@instance_checking_exception(EnvironmentError)
def FileExistsError(inst):
return getattr(inst, 'errno', object()) == errno.EEXIST


# --- stdlib additions


Expand Down
32 changes: 13 additions & 19 deletions psutil/_psaix.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

"""AIX platform implementation."""

import errno
import functools
import glob
import os
Expand All @@ -26,6 +25,9 @@
from ._common import NIC_DUPLEX_HALF
from ._common import NIC_DUPLEX_UNKNOWN
from ._common import usage_percent
from ._compat import FileNotFoundError
from ._compat import PermissionError
from ._compat import ProcessLookupError
from ._compat import PY3


Expand Down Expand Up @@ -314,22 +316,16 @@ def wrap_exceptions(fun):
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except EnvironmentError as err:
# support for private module import
if (NoSuchProcess is None or AccessDenied is None or
ZombieProcess is None):
raise
except (FileNotFoundError, ProcessLookupError):
# ENOENT (no such file or directory) gets raised on open().
# ESRCH (no such process) can get raised on read() if
# process is gone in meantime.
if err.errno in (errno.ENOENT, errno.ESRCH):
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
except PermissionError:
raise AccessDenied(self.pid, self._name)
return wrapper


Expand Down Expand Up @@ -488,11 +484,9 @@ def cwd(self):
try:
result = os.readlink("%s/%s/cwd" % (procfs_path, self.pid))
return result.rstrip('/')
except OSError as err:
if err.errno == errno.ENOENT:
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
return None
raise
except FileNotFoundError:
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
return None

@wrap_exceptions
def memory_info(self):
Expand Down
36 changes: 19 additions & 17 deletions psutil/_psbsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@
from ._common import NETBSD
from ._common import OPENBSD
from ._common import usage_percent
from ._compat import FileNotFoundError
from ._compat import PermissionError
from ._compat import ProcessLookupError
from ._compat import which


__extra__all__ = []


Expand Down Expand Up @@ -550,19 +554,19 @@ def wrap_exceptions(fun):
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
except ProcessLookupError:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
except PermissionError:
raise AccessDenied(self.pid, self._name)
except OSError:
if self.pid == 0:
if 0 in pids():
raise AccessDenied(self.pid, self._name)
else:
raise
if err.errno == errno.ESRCH:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper

Expand All @@ -572,18 +576,16 @@ def wrap_exceptions_procfs(inst):
"""Same as above, for routines relying on reading /proc fs."""
try:
yield
except EnvironmentError as err:
except (ProcessLookupError, FileNotFoundError):
# ENOENT (no such file or directory) gets raised on open().
# ESRCH (no such process) can get raised on read() if
# process is gone in meantime.
if err.errno in (errno.ENOENT, errno.ESRCH):
if not pid_exists(inst.pid):
raise NoSuchProcess(inst.pid, inst._name)
else:
raise ZombieProcess(inst.pid, inst._name, inst._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(inst.pid, inst._name)
raise
if not pid_exists(inst.pid):
raise NoSuchProcess(inst.pid, inst._name)
else:
raise ZombieProcess(inst.pid, inst._name, inst._ppid)
except PermissionError:
raise AccessDenied(inst.pid, inst._name)


class Process(object):
Expand Down

0 comments on commit c10df5a

Please sign in to comment.