From c10df5aa04e1ced58d19501fa42f08c1b909b83d Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 28 Jun 2019 15:12:56 +0200 Subject: [PATCH] PEP-3151: backport FS exceptions to Python 2 (#1544) --- Makefile | 1 + psutil/__init__.py | 27 ++++--- psutil/_compat.py | 72 ++++++++++++++++++- psutil/_psaix.py | 32 ++++----- psutil/_psbsd.py | 36 +++++----- psutil/_pslinux.py | 115 +++++++++++++----------------- psutil/_psosx.py | 12 ++-- psutil/_psposix.py | 63 ++++++++-------- psutil/_pssunos.py | 50 ++++++------- psutil/tests/__init__.py | 31 ++++---- psutil/tests/test_bsd.py | 2 +- psutil/tests/test_linux.py | 7 +- psutil/tests/test_memory_leaks.py | 7 +- psutil/tests/test_misc.py | 3 +- psutil/tests/test_system.py | 4 +- psutil/tests/test_windows.py | 10 ++- 16 files changed, 254 insertions(+), 218 deletions(-) diff --git a/Makefile b/Makefile index b14978b21..a37638483 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ DEPS = \ ipaddress \ mock==1.0.1 \ perf \ + readline \ requests \ setuptools \ sphinx \ diff --git a/psutil/__init__.py b/psutil/__init__.py index b400ec852..62dadc90c 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -25,7 +25,6 @@ import collections import contextlib import datetime -import errno import functools import os import signal @@ -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 @@ -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) @@ -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): diff --git a/psutil/_compat.py b/psutil/_compat.py index c772f61d6..07ab909a8 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -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 @@ -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 diff --git a/psutil/_psaix.py b/psutil/_psaix.py index 3a949d257..79e3be15a 100644 --- a/psutil/_psaix.py +++ b/psutil/_psaix.py @@ -6,7 +6,6 @@ """AIX platform implementation.""" -import errno import functools import glob import os @@ -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 @@ -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 @@ -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): diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index eccc96a15..2f41dc0be 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -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__ = [] @@ -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 @@ -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): diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 50094b40e..7a3a164b0 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -41,6 +41,9 @@ from ._common import usage_percent from ._compat import b from ._compat import basestring +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 if sys.version_info >= (3, 4): @@ -772,17 +775,16 @@ def get_proc_inodes(self, pid): for fd in os.listdir("%s/%s/fd" % (self._procfs_path, pid)): try: inode = readlink("%s/%s/fd/%s" % (self._procfs_path, pid, fd)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # ENOENT == file which is gone in the meantime; # os.stat('/proc/%s' % self.pid) will be done later # to force NSP (if it's the case) - if err.errno in (errno.ENOENT, errno.ESRCH): - continue - elif err.errno == errno.EINVAL: + continue + except OSError as err: + if err.errno == errno.EINVAL: # not a link continue - else: - raise + raise else: if inode.startswith('socket:['): # the process is using a socket @@ -795,7 +797,7 @@ def get_all_inodes(self): for pid in pids(): try: inodes.update(self.get_proc_inodes(pid)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError, PermissionError): # os.listdir() is gonna raise a lot of access denied # exceptions in case of unprivileged user; that's fine # as we'll just end up returning a connection with PID @@ -803,9 +805,7 @@ def get_all_inodes(self): # Both netstat -an and lsof does the same so it's # unlikely we can do any better. # ENOENT just means a PID disappeared on us. - if err.errno not in ( - errno.ENOENT, errno.ESRCH, errno.EPERM, errno.EACCES): - raise + continue return inodes @staticmethod @@ -1490,11 +1490,10 @@ def ppid_map(): try: with open_binary("%s/%s/stat" % (procfs_path, pid)) as f: data = f.read() - except EnvironmentError as err: + except (FileNotFoundError, ProcessLookupError): # Note: we should be able to access /stat for all processes # aka it's unlikely we'll bump into EPERM, which is good. - if err.errno not in (errno.ENOENT, errno.ESRCH): - raise + pass else: rpar = data.rfind(b')') dset = data[rpar + 2:].split() @@ -1511,16 +1510,12 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - # ESRCH (no such process) can be raised on read() if - # process is gone in the meantime. - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - # ENOENT (no such file or directory) can be raised on open(). - if err.errno == errno.ENOENT and not os.path.exists("%s/%s" % ( - self._procfs_path, self.pid)): + except PermissionError: + raise AccessDenied(self.pid, self._name) + except ProcessLookupError: + raise NoSuchProcess(self.pid, self._name) + except FileNotFoundError: + if not os.path.exists("%s/%s" % (self._procfs_path, self.pid)): raise NoSuchProcess(self.pid, self._name) # Note: zombies will keep existing under /proc until they're # gone so there's no way to distinguish them in here. @@ -1617,21 +1612,19 @@ def name(self): def exe(self): try: return readlink("%s/%s/exe" % (self._procfs_path, self.pid)) - except OSError as err: - if err.errno in (errno.ENOENT, errno.ESRCH): - # no such file error; might be raised also if the - # path actually exists for system processes with - # low pids (about 0-20) - if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): - return "" + except (FileNotFoundError, ProcessLookupError): + # no such file error; might be raised also if the + # path actually exists for system processes with + # low pids (about 0-20) + if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): + return "" + else: + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) else: - 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 + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) @wrap_exceptions def cmdline(self): @@ -1856,14 +1849,12 @@ def get_blocks(lines, current_block): def cwd(self): try: return readlink("%s/%s/cwd" % (self._procfs_path, self.pid)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # https://github.com/giampaolo/psutil/issues/986 - 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) - raise + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) @wrap_exceptions def num_ctx_switches(self, @@ -1899,13 +1890,11 @@ def threads(self): try: with open_binary(fname) as f: st = f.read().strip() - except IOError as err: - if err.errno == errno.ENOENT: - # no such file or directory; it means thread - # disappeared on us - hit_enoent = True - continue - raise + except FileNotFoundError: + # no such file or directory; it means thread + # disappeared on us + hit_enoent = True + continue # ignore the first two values ("pid (exe)") st = st[st.find(b')') + 2:] values = st.split(b' ') @@ -2029,16 +2018,15 @@ def open_files(self): file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd) try: path = readlink(file) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # ENOENT == file which is gone in the meantime - if err.errno in (errno.ENOENT, errno.ESRCH): - hit_enoent = True - continue - elif err.errno == errno.EINVAL: + hit_enoent = True + continue + except OSError as err: + if err.errno == errno.EINVAL: # not a link continue - else: - raise + raise else: # If path is not an absolute there's no way to tell # whether it's a regular file or not, so we skip it. @@ -2052,13 +2040,10 @@ def open_files(self): with open_binary(file) as f: pos = int(f.readline().split()[1]) flags = int(f.readline().split()[1], 8) - except IOError as err: - if err.errno == errno.ENOENT: - # fd gone in the meantime; process may - # still be alive - hit_enoent = True - else: - raise + except FileNotFoundError: + # fd gone in the meantime; process may + # still be alive + hit_enoent = True else: mode = file_flags_to_mode(flags) ntuple = popenfile( diff --git a/psutil/_psosx.py b/psutil/_psosx.py index 54e572027..7f28447bb 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -19,6 +19,8 @@ from ._common import isfile_strict from ._common import memoize_when_activated from ._common import parse_environ_block +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._common import usage_percent @@ -334,12 +336,10 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError as err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + except ProcessLookupError: + raise NoSuchProcess(self.pid, self._name) + except PermissionError: + raise AccessDenied(self.pid, self._name) except cext.ZombieProcessError: raise ZombieProcess(self.pid, self._name, self._ppid) return wrapper diff --git a/psutil/_psposix.py b/psutil/_psposix.py index d362143f6..24570224f 100644 --- a/psutil/_psposix.py +++ b/psutil/_psposix.py @@ -4,7 +4,6 @@ """Routines common to all posix systems.""" -import errno import glob import os import sys @@ -13,6 +12,11 @@ from ._common import memoize from ._common import sdiskusage from ._common import usage_percent +from ._compat import ChildProcessError +from ._compat import FileNotFoundError +from ._compat import InterruptedError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 from ._compat import unicode @@ -36,19 +40,13 @@ def pid_exists(pid): return True try: os.kill(pid, 0) - except OSError as err: - if err.errno == errno.ESRCH: - # ESRCH == No such process - return False - elif err.errno == errno.EPERM: - # EPERM clearly means there's a process to deny access to - return True - else: - # According to "man 2 kill" possible error values are - # (EINVAL, EPERM, ESRCH) therefore we should never get - # here. If we do let's be explicit in considering this - # an error. - raise err + except ProcessLookupError: + return False + except PermissionError: + # EPERM clearly means there's a process to deny access to + return True + # According to "man 2 kill" possible error values are + # (EINVAL, EPERM, ESRCH) else: return True @@ -84,24 +82,20 @@ def waitcall(): while True: try: retpid, status = waitcall() - except OSError as err: - if err.errno == errno.EINTR: - delay = check_timeout(delay) - continue - elif err.errno == errno.ECHILD: - # This has two meanings: - # - pid is not a child of os.getpid() in which case - # we keep polling until it's gone - # - pid never existed in the first place - # In both cases we'll eventually return None as we - # can't determine its exit status code. - while True: - if pid_exists(pid): - delay = check_timeout(delay) - else: - return - else: - raise + except InterruptedError: + delay = check_timeout(delay) + except ChildProcessError: + # This has two meanings: + # - pid is not a child of os.getpid() in which case + # we keep polling until it's gone + # - pid never existed in the first place + # In both cases we'll eventually return None as we + # can't determine its exit status code. + while True: + if pid_exists(pid): + delay = check_timeout(delay) + else: + return else: if retpid == 0: # WNOHANG was used, pid is still running @@ -180,7 +174,6 @@ def get_terminal_map(): assert name not in ret, name try: ret[os.stat(name).st_rdev] = name - except OSError as err: - if err.errno != errno.ENOENT: - raise + except FileNotFoundError: + pass return ret diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index 07298a738..2aa2a8661 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -25,6 +25,9 @@ from ._common import socktype_to_enum from ._common import usage_percent from ._compat import b +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 @@ -342,22 +345,22 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: + 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 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 - # 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 return wrapper @@ -515,11 +518,9 @@ def terminal(self): try: return os.readlink( '%s/%d/path/%d' % (procfs_path, self.pid, x)) - except OSError as err: - if err.errno == errno.ENOENT: - hit_enoent = True - continue - raise + except FileNotFoundError: + hit_enoent = True + continue if hit_enoent: self._assert_alive() @@ -532,11 +533,9 @@ def cwd(self): procfs_path = self._procfs_path try: return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid)) - 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): @@ -597,12 +596,9 @@ def open_files(self): if os.path.islink(path): try: file = os.readlink(path) - except OSError as err: - # ENOENT == file which is gone in the meantime - if err.errno == errno.ENOENT: - hit_enoent = True - continue - raise + except FileNotFoundError: + hit_enoent = True + continue else: if isfile_strict(file): retlist.append(_common.popenfile(file, int(fd))) diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 5e4f37b39..6c6293687 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -41,6 +41,9 @@ from psutil import SUNOS from psutil import WINDOWS from psutil._common import supports_ipv6 +from psutil._compat import ChildProcessError +from psutil._compat import FileExistsError +from psutil._compat import FileNotFoundError from psutil._compat import PY3 from psutil._compat import u from psutil._compat import unicode @@ -514,9 +517,8 @@ def assert_gone(pid): # Wait for the process to terminate, to avoid zombies. try: subp.wait() - except OSError as err: - if err.errno != errno.ECHILD: - raise + except ChildProcessError: + pass # Terminate started pids. while _pids_started: @@ -710,13 +712,12 @@ def retry_fun(fun): while time.time() < stop_at: try: return fun() + except FileNotFoundError: + pass except WindowsError as _: err = _ - if err.errno != errno.ENOENT: - raise - else: - warn("ignoring %s" % (str(err))) - time.sleep(0.01) + warn("ignoring %s" % (str(err))) + time.sleep(0.01) raise err try: @@ -729,18 +730,16 @@ def retry_fun(fun): fun() else: retry_fun(fun) - except OSError as err: - if err.errno != errno.ENOENT: - raise + except FileNotFoundError: + pass def safe_mkdir(dir): "Convenience function for creating a directory" try: os.mkdir(dir) - except OSError as err: - if err.errno != errno.EEXIST: - raise + except FileExistsError: + pass @contextlib.contextmanager @@ -868,7 +867,6 @@ def wrapper(*args, **kwargs): def get_free_port(host='127.0.0.1'): """Return an unused TCP port.""" with contextlib.closing(socket.socket()) as sock: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((host, 0)) return sock.getsockname()[1] @@ -895,7 +893,8 @@ def bind_socket(family=AF_INET, type=SOCK_STREAM, addr=None): addr = ("", 0) sock = socket.socket(family, type) try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if os.name not in ('nt', 'cygwin'): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(addr) if type == socket.SOCK_STREAM: sock.listen(5) diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py index f994bc5dc..7cba4b78a 100755 --- a/psutil/tests/test_bsd.py +++ b/psutil/tests/test_bsd.py @@ -7,7 +7,7 @@ # TODO: (FreeBSD) add test for comparing connections with 'sockstat' cmd. -"""Tests sprcific to all BSD platforms.""" +"""Tests specific to all BSD platforms.""" import datetime diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index d732e90d7..bb74ab313 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -24,6 +24,7 @@ import psutil from psutil import LINUX from psutil._compat import basestring +from psutil._compat import FileNotFoundError from psutil._compat import PY3 from psutil._compat import u from psutil.tests import call_until @@ -1080,10 +1081,9 @@ def test_emulate_realpath_fail(self): try: with mock.patch('os.path.realpath', return_value='/non/existent') as m: - with self.assertRaises(OSError) as cm: + with self.assertRaises(FileNotFoundError): psutil.disk_partitions() assert m.called - self.assertEqual(cm.exception.errno, errno.ENOENT) finally: psutil.PROCFS_PATH = "/proc" @@ -1920,9 +1920,8 @@ def test_issue_1014(self): '/proc/%s/smaps' % os.getpid(), IOError(errno.ENOENT, "")) as m: p = psutil.Process() - with self.assertRaises(IOError) as err: + with self.assertRaises(FileNotFoundError): p.memory_maps() - self.assertEqual(err.exception.errno, errno.ENOENT) assert m.called @unittest.skipIf(not HAS_RLIMIT, "not supported") diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py index 543dbf710..ba75eef00 100755 --- a/psutil/tests/test_memory_leaks.py +++ b/psutil/tests/test_memory_leaks.py @@ -14,7 +14,6 @@ """ from __future__ import print_function -import errno import functools import gc import os @@ -31,6 +30,7 @@ from psutil import SUNOS from psutil import WINDOWS from psutil._common import bytes2human +from psutil._compat import ProcessLookupError from psutil._compat import xrange from psutil.tests import create_sockets from psutil.tests import get_test_subprocess @@ -423,9 +423,8 @@ def test_proc_info(self): def call(): try: return cext.proc_info(self.proc.pid) - except OSError as err: - if err.errno != errno.ESRCH: - raise + except ProcessLookupError: + pass self.execute(call) diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index 4b688288d..4e476871d 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -180,7 +180,8 @@ def test__all__(self): for name in dir_psutil: if name in ('callable', 'error', 'namedtuple', 'tests', 'long', 'test', 'NUM_CPUS', 'BOOT_TIME', - 'TOTAL_PHYMEM'): + 'TOTAL_PHYMEM', 'PermissionError', + 'ProcessLookupError'): continue if not name.startswith('_'): try: diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index eb9016f08..38843b0ee 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -29,6 +29,7 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS +from psutil._compat import FileNotFoundError from psutil._compat import long from psutil.tests import APPVEYOR from psutil.tests import ASCII_FS @@ -468,9 +469,8 @@ def test_disk_usage(self): # if path does not exist OSError ENOENT is expected across # all platforms fname = tempfile.mktemp() - with self.assertRaises(OSError) as exc: + with self.assertRaises(FileNotFoundError): psutil.disk_usage(fname) - self.assertEqual(exc.exception.errno, errno.ENOENT) def test_disk_usage_unicode(self): # See: https://github.com/giampaolo/psutil/issues/416 diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index 5a998dd4f..51b53f0eb 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -21,6 +21,7 @@ import psutil from psutil import WINDOWS +from psutil._compat import FileNotFoundError from psutil.tests import APPVEYOR from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY @@ -161,12 +162,9 @@ def test_disks(self): break try: usage = psutil.disk_usage(ps_part.mountpoint) - except OSError as err: - if err.errno == errno.ENOENT: - # usually this is the floppy - break - else: - raise + except FileNotFoundError: + # usually this is the floppy + break self.assertEqual(usage.total, int(wmi_part.Size)) wmi_free = int(wmi_part.FreeSpace) self.assertEqual(usage.free, wmi_free)