Browse Source

Intermediate changes
commit_hash:7c10655440be1add16ba4307493e0143ef552dbf

robot-piglet 1 week ago
parent
commit
e4edb48196

+ 11 - 13
contrib/python/psutil/py3/.dist-info/METADATA

@@ -1,7 +1,7 @@
 Metadata-Version: 2.1
 Name: psutil
-Version: 6.1.1
-Summary: Cross-platform lib for process and system monitoring in Python.
+Version: 7.0.0
+Summary: Cross-platform lib for process and system monitoring in Python.  NOTE: the syntax of this script MUST be kept compatible with Python 2.7.
 Home-page: https://github.com/giampaolo/psutil
 Author: Giampaolo Rodola
 Author-email: g.rodola@gmail.com
@@ -34,8 +34,6 @@ Classifier: Operating System :: POSIX :: Linux
 Classifier: Operating System :: POSIX :: SunOS/Solaris
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: C
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -52,12 +50,15 @@ Classifier: Topic :: System :: Networking
 Classifier: Topic :: System :: Operating System
 Classifier: Topic :: System :: Systems Administration
 Classifier: Topic :: Utilities
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*
+Requires-Python: >=3.6
 Description-Content-Type: text/x-rst
 License-File: LICENSE
 Provides-Extra: dev
+Requires-Dist: pytest ; extra == 'dev'
+Requires-Dist: pytest-xdist ; extra == 'dev'
+Requires-Dist: setuptools ; extra == 'dev'
 Requires-Dist: abi3audit ; extra == 'dev'
-Requires-Dist: black ; extra == 'dev'
+Requires-Dist: black (==24.10.0) ; extra == 'dev'
 Requires-Dist: check-manifest ; extra == 'dev'
 Requires-Dist: coverage ; extra == 'dev'
 Requires-Dist: packaging ; extra == 'dev'
@@ -82,7 +83,7 @@ Requires-Dist: setuptools ; extra == 'test'
 
 |  |downloads| |stars| |forks| |contributors| |coverage|
 |  |version| |py-versions| |packages| |license|
-|  |github-actions-wheels|  |github-actions-bsd| |appveyor| |doc| |twitter| |tidelift|
+|  |github-actions-wheels|  |github-actions-bsd| |doc| |twitter| |tidelift|
 
 .. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
     :target: https://pepy.tech/project/psutil
@@ -108,10 +109,6 @@ Requires-Dist: setuptools ; extra == 'test'
     :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Absd-tests
     :alt: FreeBSD, NetBSD, OpenBSD
 
-.. |appveyor| image:: https://img.shields.io/appveyor/build/giampaolo/psutil/master.svg?maxAge=3600&label=Windows%20(py2)
-    :target: https://ci.appveyor.com/project/giampaolo/psutil
-    :alt: Windows (Appveyor)
-
 .. |coverage| image:: https://coveralls.io/repos/github/giampaolo/psutil/badge.svg?branch=master
     :target: https://coveralls.io/github/giampaolo/psutil?branch=master
     :alt: Test coverage (coverall.io)
@@ -177,8 +174,9 @@ psutil currently supports the following platforms:
 - **Sun Solaris**
 - **AIX**
 
-Supported Python versions are **2.7**, **3.6+** and
-`PyPy <http://pypy.org/>`__.
+Supported Python versions are cPython 3.6+ and `PyPy <https://pypy.org/>`__.
+Latest psutil version supporting Python 2.7 is
+`psutil 6.1.1 <https://pypi.org/project/psutil/6.1.1/>`__.
 
 Funding
 =======

+ 4 - 7
contrib/python/psutil/py3/README.rst

@@ -1,6 +1,6 @@
 |  |downloads| |stars| |forks| |contributors| |coverage|
 |  |version| |py-versions| |packages| |license|
-|  |github-actions-wheels|  |github-actions-bsd| |appveyor| |doc| |twitter| |tidelift|
+|  |github-actions-wheels|  |github-actions-bsd| |doc| |twitter| |tidelift|
 
 .. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
     :target: https://pepy.tech/project/psutil
@@ -26,10 +26,6 @@
     :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Absd-tests
     :alt: FreeBSD, NetBSD, OpenBSD
 
-.. |appveyor| image:: https://img.shields.io/appveyor/build/giampaolo/psutil/master.svg?maxAge=3600&label=Windows%20(py2)
-    :target: https://ci.appveyor.com/project/giampaolo/psutil
-    :alt: Windows (Appveyor)
-
 .. |coverage| image:: https://coveralls.io/repos/github/giampaolo/psutil/badge.svg?branch=master
     :target: https://coveralls.io/github/giampaolo/psutil?branch=master
     :alt: Test coverage (coverall.io)
@@ -98,8 +94,9 @@ psutil currently supports the following platforms:
 - **Sun Solaris**
 - **AIX**
 
-Supported Python versions are **2.7**, **3.6+** and
-`PyPy <http://pypy.org/>`__.
+Supported Python versions are cPython 3.6+ and `PyPy <https://pypy.org/>`__.
+Latest psutil version supporting Python 2.7 is
+`psutil 6.1.1 <https://pypi.org/project/psutil/6.1.1/>`__.
 
 Funding
 =======

+ 0 - 14
contrib/python/psutil/py3/patches/01-arcadia.patch

@@ -1,17 +1,3 @@
---- contrib/python/psutil/py3/psutil/_pslinux.py	(index)
-+++ contrib/python/psutil/py3/psutil/_pslinux.py	(working tree)
-@@ -322,7 +322,10 @@ try:
- except ImportError:
-     import ctypes
- 
--    libc = ctypes.CDLL(None, use_errno=True)
-+    try:
-+        libc = ctypes.CDLL(None, use_errno=True)
-+    except:
-+        libc = None
- 
-     if hasattr(libc, "prlimit"):
- 
 --- contrib/python/psutil/py3/psutil/arch/windows/disk.c	(index)
 +++ contrib/python/psutil/py3/psutil/arch/windows/disk.c	(working tree)
 @@ -7,6 +7,7 @@

+ 122 - 201
contrib/python/psutil/py3/psutil/__init__.py

@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -17,17 +15,16 @@ sensors) in Python. Supported platforms:
  - Sun Solaris
  - AIX
 
-Works with Python versions 2.7 and 3.6+.
+Supported Python versions are cPython 3.6+ and PyPy.
 """
 
-from __future__ import division
-
 import collections
 import contextlib
 import datetime
 import functools
 import os
 import signal
+import socket
 import subprocess
 import sys
 import threading
@@ -54,16 +51,16 @@ from ._common import CONN_NONE
 from ._common import CONN_SYN_RECV
 from ._common import CONN_SYN_SENT
 from ._common import CONN_TIME_WAIT
-from ._common import FREEBSD  # NOQA
+from ._common import FREEBSD
 from ._common import LINUX
 from ._common import MACOS
-from ._common import NETBSD  # NOQA
+from ._common import NETBSD
 from ._common import NIC_DUPLEX_FULL
 from ._common import NIC_DUPLEX_HALF
 from ._common import NIC_DUPLEX_UNKNOWN
-from ._common import OPENBSD  # NOQA
+from ._common import OPENBSD
 from ._common import OSX  # deprecated alias
-from ._common import POSIX  # NOQA
+from ._common import POSIX
 from ._common import POWER_TIME_UNKNOWN
 from ._common import POWER_TIME_UNLIMITED
 from ._common import STATUS_DEAD
@@ -88,11 +85,6 @@ from ._common import ZombieProcess
 from ._common import debug
 from ._common import memoize_when_activated
 from ._common import wrap_numbers as _wrap_numbers
-from ._compat import PY3 as _PY3
-from ._compat import PermissionError
-from ._compat import ProcessLookupError
-from ._compat import SubprocessTimeoutExpired as _SubprocessTimeoutExpired
-from ._compat import long
 
 
 if LINUX:
@@ -101,24 +93,24 @@ if LINUX:
     PROCFS_PATH = "/proc"
 
     from . import _pslinux as _psplatform
-    from ._pslinux import IOPRIO_CLASS_BE  # NOQA
-    from ._pslinux import IOPRIO_CLASS_IDLE  # NOQA
-    from ._pslinux import IOPRIO_CLASS_NONE  # NOQA
-    from ._pslinux import IOPRIO_CLASS_RT  # NOQA
+    from ._pslinux import IOPRIO_CLASS_BE  # noqa: F401
+    from ._pslinux import IOPRIO_CLASS_IDLE  # noqa: F401
+    from ._pslinux import IOPRIO_CLASS_NONE  # noqa: F401
+    from ._pslinux import IOPRIO_CLASS_RT  # noqa: F401
 
 elif WINDOWS:
     from . import _pswindows as _psplatform
-    from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS  # NOQA
-    from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS  # NOQA
-    from ._psutil_windows import HIGH_PRIORITY_CLASS  # NOQA
-    from ._psutil_windows import IDLE_PRIORITY_CLASS  # NOQA
-    from ._psutil_windows import NORMAL_PRIORITY_CLASS  # NOQA
-    from ._psutil_windows import REALTIME_PRIORITY_CLASS  # NOQA
-    from ._pswindows import CONN_DELETE_TCB  # NOQA
-    from ._pswindows import IOPRIO_HIGH  # NOQA
-    from ._pswindows import IOPRIO_LOW  # NOQA
-    from ._pswindows import IOPRIO_NORMAL  # NOQA
-    from ._pswindows import IOPRIO_VERYLOW  # NOQA
+    from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS  # noqa: F401
+    from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS  # noqa: F401
+    from ._psutil_windows import HIGH_PRIORITY_CLASS  # noqa: F401
+    from ._psutil_windows import IDLE_PRIORITY_CLASS  # noqa: F401
+    from ._psutil_windows import NORMAL_PRIORITY_CLASS  # noqa: F401
+    from ._psutil_windows import REALTIME_PRIORITY_CLASS  # noqa: F401
+    from ._pswindows import CONN_DELETE_TCB  # noqa: F401
+    from ._pswindows import IOPRIO_HIGH  # noqa: F401
+    from ._pswindows import IOPRIO_LOW  # noqa: F401
+    from ._pswindows import IOPRIO_NORMAL  # noqa: F401
+    from ._pswindows import IOPRIO_VERYLOW  # noqa: F401
 
 elif MACOS:
     from . import _psosx as _psplatform
@@ -128,8 +120,8 @@ elif BSD:
 
 elif SUNOS:
     from . import _pssunos as _psplatform
-    from ._pssunos import CONN_BOUND  # NOQA
-    from ._pssunos import CONN_IDLE  # NOQA
+    from ._pssunos import CONN_BOUND  # noqa: F401
+    from ._pssunos import CONN_IDLE  # noqa: F401
 
     # This is public writable API which is read from _pslinux.py and
     # _pssunos.py via sys.modules.
@@ -143,7 +135,8 @@ elif AIX:
     PROCFS_PATH = "/proc"
 
 else:  # pragma: no cover
-    raise NotImplementedError('platform %s is not supported' % sys.platform)
+    msg = f"platform {sys.platform} is not supported"
+    raise NotImplementedError(msg)
 
 
 # fmt: off
@@ -214,8 +207,8 @@ if hasattr(_psplatform.Process, "rlimit"):
 AF_LINK = _psplatform.AF_LINK
 
 __author__ = "Giampaolo Rodola'"
-__version__ = "6.1.1"
-version_info = tuple([int(num) for num in __version__.split('.')])
+__version__ = "7.0.0"
+version_info = tuple(int(num) for num in __version__.split('.'))
 
 _timer = getattr(time, 'monotonic', time.time)
 _TOTAL_PHYMEM = None
@@ -231,22 +224,19 @@ _SENTINEL = object()
 if int(__version__.replace('.', '')) != getattr(
     _psplatform.cext, 'version', None
 ):
-    msg = "version conflict: %r C extension " % _psplatform.cext.__file__
+    msg = f"version conflict: {_psplatform.cext.__file__!r} C extension "
     msg += "module was built for another version of psutil"
     if hasattr(_psplatform.cext, 'version'):
-        msg += " (%s instead of %s)" % (
-            '.'.join([x for x in str(_psplatform.cext.version)]),
-            __version__,
-        )
+        v = ".".join(list(str(_psplatform.cext.version)))
+        msg += f" ({v} instead of {__version__})"
     else:
-        msg += " (different than %s)" % __version__
-    msg += "; you may try to 'pip uninstall psutil', manually remove %s" % (
-        getattr(
-            _psplatform.cext,
-            "__file__",
-            "the existing psutil install directory",
-        )
+        msg += f" (different than {__version__})"
+    what = getattr(
+        _psplatform.cext,
+        "__file__",
+        "the existing psutil install directory",
     )
+    msg += f"; you may try to 'pip uninstall psutil', manually remove {what}"
     msg += " or clean the virtual env somehow, then reinstall"
     raise ImportError(msg)
 
@@ -282,12 +272,20 @@ def _pprint_secs(secs):
     return datetime.datetime.fromtimestamp(secs).strftime(fmt)
 
 
+def _check_conn_kind(kind):
+    """Check net_connections()'s `kind` parameter."""
+    kinds = tuple(_common.conn_tmap)
+    if kind not in kinds:
+        msg = f"invalid kind argument {kind!r}; valid ones are: {kinds}"
+        raise ValueError(msg)
+
+
 # =====================================================================
 # --- Process class
 # =====================================================================
 
 
-class Process(object):  # noqa: UP004
+class Process:
     """Represents an OS process with the given PID.
     If PID is omitted current process PID (os.getpid()) is used.
     Raise NoSuchProcess if PID does not exist.
@@ -322,17 +320,14 @@ class Process(object):  # noqa: UP004
         if pid is None:
             pid = os.getpid()
         else:
-            if not _PY3 and not isinstance(pid, (int, long)):
-                msg = "pid must be an integer (got %r)" % pid
-                raise TypeError(msg)
             if pid < 0:
-                msg = "pid must be a positive integer (got %s)" % pid
+                msg = f"pid must be a positive integer (got {pid})"
                 raise ValueError(msg)
             try:
                 _psplatform.cext.check_pid_range(pid)
-            except OverflowError:
-                msg = "process PID out of range (got %s)" % pid
-                raise NoSuchProcess(pid, msg=msg)
+            except OverflowError as err:
+                msg = "process PID out of range"
+                raise NoSuchProcess(pid, msg=msg) from err
 
         self._pid = pid
         self._name = None
@@ -365,9 +360,8 @@ class Process(object):  # noqa: UP004
         except NoSuchProcess:
             if not _ignore_nsp:
                 msg = "process PID not found"
-                raise NoSuchProcess(pid, msg=msg)
-            else:
-                self._gone = True
+                raise NoSuchProcess(pid, msg=msg) from None
+            self._gone = True
 
     def _get_ident(self):
         """Return a (pid, uid) tuple which is supposed to identify a
@@ -419,10 +413,10 @@ class Process(object):  # noqa: UP004
             if self._create_time is not None:
                 info['started'] = _pprint_secs(self._create_time)
 
-            return "%s.%s(%s)" % (
+            return "{}.{}({})".format(
                 self.__class__.__module__,
                 self.__class__.__name__,
-                ", ".join(["%s=%r" % (k, v) for k, v in info.items()]),
+                ", ".join([f"{k}={v!r}" for k, v in info.items()]),
             )
 
     __repr__ = __str__
@@ -556,12 +550,12 @@ class Process(object):  # noqa: UP004
         valid_names = _as_dict_attrnames
         if attrs is not None:
             if not isinstance(attrs, (list, tuple, set, frozenset)):
-                msg = "invalid attrs type %s" % type(attrs)
+                msg = f"invalid attrs type {type(attrs)}"
                 raise TypeError(msg)
             attrs = set(attrs)
             invalid_names = attrs - valid_names
             if invalid_names:
-                msg = "invalid attr name%s %s" % (
+                msg = "invalid attr name{} {}".format(
                     "s" if len(invalid_names) > 1 else "",
                     ", ".join(map(repr, invalid_names)),
                 )
@@ -1049,7 +1043,7 @@ class Process(object):  # noqa: UP004
         """
         blocking = interval is not None and interval > 0.0
         if interval is not None and interval < 0:
-            msg = "interval is not positive (got %r)" % interval
+            msg = f"interval is not positive (got {interval!r})"
             raise ValueError(msg)
         num_cpus = cpu_count() or 1
 
@@ -1127,10 +1121,6 @@ class Process(object):  # noqa: UP004
         """
         return self._proc.memory_info()
 
-    @_common.deprecated_method(replacement="memory_info")
-    def memory_info_ex(self):
-        return self.memory_info()
-
     def memory_full_info(self):
         """This method returns the same information as memory_info(),
         plus, on some platform (Linux, macOS, Windows), also provides
@@ -1159,9 +1149,9 @@ class Process(object):  # noqa: UP004
         """
         valid_types = list(_psplatform.pfullmem._fields)
         if memtype not in valid_types:
-            msg = "invalid memtype %r; valid types are %r" % (
-                memtype,
-                tuple(valid_types),
+            msg = (
+                f"invalid memtype {memtype!r}; valid types are"
+                f" {tuple(valid_types)!r}"
             )
             raise ValueError(msg)
         fun = (
@@ -1178,7 +1168,7 @@ class Process(object):  # noqa: UP004
             # we should never get here
             msg = (
                 "can't calculate process memory percent because total physical"
-                " system memory is not positive (%r)" % (total_phymem)
+                f" system memory is not positive ({total_phymem!r})"
             )
             raise ValueError(msg)
         return (value / float(total_phymem)) * 100
@@ -1203,11 +1193,11 @@ class Process(object):  # noqa: UP004
                     path = tupl[2]
                     nums = tupl[3:]
                     try:
-                        d[path] = map(lambda x, y: x + y, d[path], nums)
+                        d[path] = list(map(lambda x, y: x + y, d[path], nums))
                     except KeyError:
                         d[path] = nums
                 nt = _psplatform.pmmap_grouped
-                return [nt(path, *d[path]) for path in d]  # NOQA
+                return [nt(path, *d[path]) for path in d]
             else:
                 nt = _psplatform.pmmap_ext
                 return [nt(*x) for x in it]
@@ -1241,6 +1231,7 @@ class Process(object):  # noqa: UP004
         | all        | the sum of all the possible families and protocols |
         +------------+----------------------------------------------------+
         """
+        _check_conn_kind(kind)
         return self._proc.net_connections(kind)
 
     @_common.deprecated_method(replacement="net_connections")
@@ -1254,7 +1245,9 @@ class Process(object):  # noqa: UP004
         def _send_signal(self, sig):
             assert not self.pid < 0, self.pid
             self._raise_if_pid_reused()
-            if self.pid == 0:
+
+            pid, ppid, name = self.pid, self._ppid, self._name
+            if pid == 0:
                 # see "man 2 kill"
                 msg = (
                     "preventing sending signal to process with PID 0 as it "
@@ -1263,17 +1256,16 @@ class Process(object):  # noqa: UP004
                 )
                 raise ValueError(msg)
             try:
-                os.kill(self.pid, sig)
-            except ProcessLookupError:
-                if OPENBSD and pid_exists(self.pid):
+                os.kill(pid, sig)
+            except ProcessLookupError as err:
+                if OPENBSD and pid_exists(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)
+                    raise ZombieProcess(pid, name, ppid) from err
+                self._gone = True
+                raise NoSuchProcess(pid, name) from err
+            except PermissionError as err:
+                raise AccessDenied(pid, name) from err
 
     def send_signal(self, sig):
         """Send a signal *sig* to process pre-emptively checking
@@ -1358,11 +1350,12 @@ class Process(object):  # noqa: UP004
 
 # The valid attr names which can be processed by Process.as_dict().
 # fmt: off
-_as_dict_attrnames = set(
-    [x for x in dir(Process) if not x.startswith('_') and x not in
+_as_dict_attrnames = {
+    x for x in dir(Process) if not x.startswith("_") and x not in
      {'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
       'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit',
-      'memory_info_ex', 'connections', 'oneshot'}])
+      'connections', 'oneshot'}
+}
 # fmt: on
 
 
@@ -1439,16 +1432,13 @@ class Popen(Process):
             try:
                 return object.__getattribute__(self.__subproc, name)
             except AttributeError:
-                msg = "%s instance has no attribute '%s'" % (
-                    self.__class__.__name__,
-                    name,
-                )
-                raise AttributeError(msg)
+                msg = f"{self.__class__!r} has no attribute {name!r}"
+                raise AttributeError(msg) from None
 
     def wait(self, timeout=None):
         if self.__subproc.returncode is not None:
             return self.__subproc.returncode
-        ret = super(Popen, self).wait(timeout)  # noqa
+        ret = super().wait(timeout)
         self.__subproc.returncode = ret
         return ret
 
@@ -1525,7 +1515,7 @@ def process_iter(attrs=None, ad_value=None):
         remove(pid)
     while _pids_reused:
         pid = _pids_reused.pop()
-        debug("refreshing Process instance for reused PID %s" % pid)
+        debug(f"refreshing Process instance for reused PID {pid}")
         remove(pid)
     try:
         ls = sorted(list(pmap.items()) + list(dict.fromkeys(new_pids).items()))
@@ -1542,7 +1532,7 @@ def process_iter(attrs=None, ad_value=None):
         _pmap = pmap
 
 
-process_iter.cache_clear = lambda: _pmap.clear()  # noqa
+process_iter.cache_clear = lambda: _pmap.clear()  # noqa: PLW0108
 process_iter.cache_clear.__doc__ = "Clear process_iter() internal cache."
 
 
@@ -1586,9 +1576,7 @@ def wait_procs(procs, timeout=None, callback=None):
     def check_gone(proc, timeout):
         try:
             returncode = proc.wait(timeout=timeout)
-        except TimeoutExpired:
-            pass
-        except _SubprocessTimeoutExpired:
+        except (TimeoutExpired, subprocess.TimeoutExpired):
             pass
         else:
             if returncode is not None or not proc.is_running():
@@ -1599,12 +1587,12 @@ def wait_procs(procs, timeout=None, callback=None):
                     callback(proc)
 
     if timeout is not None and not timeout >= 0:
-        msg = "timeout must be a positive integer, got %s" % timeout
+        msg = f"timeout must be a positive integer, got {timeout}"
         raise ValueError(msg)
     gone = set()
     alive = set(procs)
     if callback is not None and not callable(callback):
-        msg = "callback %r is not a callable" % callback
+        msg = f"callback {callback!r} is not a callable"
         raise TypeError(msg)
     if timeout is not None:
         deadline = _timer() + timeout
@@ -1627,7 +1615,7 @@ def wait_procs(procs, timeout=None, callback=None):
                 check_gone(proc, timeout)
             else:
                 check_gone(proc, max_timeout)
-        alive = alive - gone  # noqa PLR6104
+        alive = alive - gone  # noqa: PLR6104
 
     if alive:
         # Last attempt over processes survived so far.
@@ -1646,7 +1634,7 @@ def wait_procs(procs, timeout=None, callback=None):
 
 def cpu_count(logical=True):
     """Return the number of logical CPUs in the system (same as
-    os.cpu_count() in Python 3.4).
+    os.cpu_count()).
 
     If *logical* is False return the number of physical cores only
     (e.g. hyper thread CPUs are excluded).
@@ -1804,7 +1792,7 @@ def cpu_percent(interval=None, percpu=False):
     tid = threading.current_thread().ident
     blocking = interval is not None and interval > 0.0
     if interval is not None and interval < 0:
-        msg = "interval is not positive (got %r)" % interval
+        msg = f"interval is not positive (got {interval})"
         raise ValueError(msg)
 
     def calculate(t1, t2):
@@ -1864,7 +1852,7 @@ def cpu_times_percent(interval=None, percpu=False):
     tid = threading.current_thread().ident
     blocking = interval is not None and interval > 0.0
     if interval is not None and interval < 0:
-        msg = "interval is not positive (got %r)" % interval
+        msg = f"interval is not positive (got {interval!r})"
         raise ValueError(msg)
 
     def calculate(t1, t2):
@@ -2202,6 +2190,7 @@ def net_connections(kind='inet'):
 
     On macOS this function requires root privileges.
     """
+    _check_conn_kind(kind)
     return _psplatform.net_connections(kind)
 
 
@@ -2223,35 +2212,46 @@ def net_if_addrs():
     Note: you can have more than one address of the same family
     associated with each interface.
     """
-    has_enums = _PY3
-    if has_enums:
-        import socket
     rawlist = _psplatform.net_if_addrs()
     rawlist.sort(key=lambda x: x[1])  # sort by family
     ret = collections.defaultdict(list)
     for name, fam, addr, mask, broadcast, ptp in rawlist:
-        if has_enums:
-            try:
-                fam = socket.AddressFamily(fam)
-            except ValueError:
-                if WINDOWS and fam == -1:
-                    fam = _psplatform.AF_LINK
-                elif (
-                    hasattr(_psplatform, "AF_LINK")
-                    and fam == _psplatform.AF_LINK
-                ):
-                    # Linux defines AF_LINK as an alias for AF_PACKET.
-                    # We re-set the family here so that repr(family)
-                    # will show AF_LINK rather than AF_PACKET
-                    fam = _psplatform.AF_LINK
+        try:
+            fam = socket.AddressFamily(fam)
+        except ValueError:
+            if WINDOWS and fam == -1:
+                fam = _psplatform.AF_LINK
+            elif (
+                hasattr(_psplatform, "AF_LINK") and fam == _psplatform.AF_LINK
+            ):
+                # Linux defines AF_LINK as an alias for AF_PACKET.
+                # We re-set the family here so that repr(family)
+                # will show AF_LINK rather than AF_PACKET
+                fam = _psplatform.AF_LINK
+
         if fam == _psplatform.AF_LINK:
             # The underlying C function may return an incomplete MAC
             # address in which case we fill it with null bytes, see:
             # https://github.com/giampaolo/psutil/issues/786
             separator = ":" if POSIX else "-"
             while addr.count(separator) < 5:
-                addr += "%s00" % separator
-        ret[name].append(_common.snicaddr(fam, addr, mask, broadcast, ptp))
+                addr += f"{separator}00"
+
+        nt = _common.snicaddr(fam, addr, mask, broadcast, ptp)
+
+        # On Windows broadcast is None, so we determine it via
+        # ipaddress module.
+        if WINDOWS and fam in {socket.AF_INET, socket.AF_INET6}:
+            try:
+                broadcast = _common.broadcast_addr(nt)
+            except Exception as err:  # noqa: BLE001
+                debug(err)
+            else:
+                if broadcast is not None:
+                    nt._replace(broadcast=broadcast)
+
+        ret[name].append(nt)
+
     return dict(ret)
 
 
@@ -2404,83 +2404,4 @@ def _set_debug(value):
     _psplatform.cext.set_debug(bool(value))
 
 
-def test():  # pragma: no cover
-    from ._common import bytes2human
-    from ._compat import get_terminal_size
-
-    today_day = datetime.date.today()
-    # fmt: off
-    templ = "%-10s %5s %5s %7s %7s %5s %6s %6s %6s  %s"
-    attrs = ['pid', 'memory_percent', 'name', 'cmdline', 'cpu_times',
-             'create_time', 'memory_info', 'status', 'nice', 'username']
-    print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "NICE",  # NOQA
-                   "STATUS", "START", "TIME", "CMDLINE"))
-    # fmt: on
-    for p in process_iter(attrs, ad_value=None):
-        if p.info['create_time']:
-            ctime = datetime.datetime.fromtimestamp(p.info['create_time'])
-            if ctime.date() == today_day:
-                ctime = ctime.strftime("%H:%M")
-            else:
-                ctime = ctime.strftime("%b%d")
-        else:
-            ctime = ''
-        if p.info['cpu_times']:
-            cputime = time.strftime(
-                "%M:%S", time.localtime(sum(p.info['cpu_times']))
-            )
-        else:
-            cputime = ''
-
-        user = p.info['username'] or ''
-        if not user and POSIX:
-            try:
-                user = p.uids()[0]
-            except Error:
-                pass
-        if user and WINDOWS and '\\' in user:
-            user = user.split('\\')[1]
-        user = user[:9]
-        vms = (
-            bytes2human(p.info['memory_info'].vms)
-            if p.info['memory_info'] is not None
-            else ''
-        )
-        rss = (
-            bytes2human(p.info['memory_info'].rss)
-            if p.info['memory_info'] is not None
-            else ''
-        )
-        memp = (
-            round(p.info['memory_percent'], 1)
-            if p.info['memory_percent'] is not None
-            else ''
-        )
-        nice = int(p.info['nice']) if p.info['nice'] else ''
-        if p.info['cmdline']:
-            cmdline = ' '.join(p.info['cmdline'])
-        else:
-            cmdline = p.info['name']
-        status = p.info['status'][:5] if p.info['status'] else ''
-
-        line = templ % (
-            user[:10],
-            p.info['pid'],
-            memp,
-            vms,
-            rss,
-            nice,
-            status,
-            ctime,
-            cputime,
-            cmdline,
-        )
-        print(line[: get_terminal_size()[0]])  # NOQA
-
-
-del memoize_when_activated, division
-if sys.version_info[0] < 3:
-    del num, x  # noqa
-
-if __name__ == "__main__":
-    test()
+del memoize_when_activated

+ 84 - 128
contrib/python/psutil/py3/psutil/_common.py

@@ -2,17 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Common objects shared by __init__.py and _ps*.py modules."""
+"""Common objects shared by __init__.py and _ps*.py modules.
 
-# Note: this module is imported by setup.py so it should not import
-# psutil or third-party modules.
-
-from __future__ import division
-from __future__ import print_function
+Note: this module is imported by setup.py, so it should not import
+psutil or third-party modules.
+"""
 
 import collections
-import contextlib
-import errno
+import enum
 import functools
 import os
 import socket
@@ -36,14 +33,6 @@ except ImportError:
     AF_UNIX = None
 
 
-# can't take it from _common.py as this script is imported by setup.py
-PY3 = sys.version_info[0] >= 3
-if PY3:
-    import enum
-else:
-    enum = None
-
-
 PSUTIL_DEBUG = bool(os.getenv('PSUTIL_DEBUG'))
 _DEFAULT = object()
 
@@ -57,7 +46,7 @@ __all__ = [
     'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN',
     'CONN_NONE', 'CONN_SYN_RECV', 'CONN_SYN_SENT', 'CONN_TIME_WAIT',
     # net constants
-    'NIC_DUPLEX_FULL', 'NIC_DUPLEX_HALF', 'NIC_DUPLEX_UNKNOWN',
+    'NIC_DUPLEX_FULL', 'NIC_DUPLEX_HALF', 'NIC_DUPLEX_UNKNOWN',  # noqa: F822
     # process status constants
     'STATUS_DEAD', 'STATUS_DISK_SLEEP', 'STATUS_IDLE', 'STATUS_LOCKED',
     'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED',
@@ -134,42 +123,29 @@ CONN_LISTEN = "LISTEN"
 CONN_CLOSING = "CLOSING"
 CONN_NONE = "NONE"
 
+
 # net_if_stats()
-if enum is None:
+class NicDuplex(enum.IntEnum):
     NIC_DUPLEX_FULL = 2
     NIC_DUPLEX_HALF = 1
     NIC_DUPLEX_UNKNOWN = 0
-else:
 
-    class NicDuplex(enum.IntEnum):
-        NIC_DUPLEX_FULL = 2
-        NIC_DUPLEX_HALF = 1
-        NIC_DUPLEX_UNKNOWN = 0
 
-    globals().update(NicDuplex.__members__)
+globals().update(NicDuplex.__members__)
+
 
 # sensors_battery()
-if enum is None:
+class BatteryTime(enum.IntEnum):
     POWER_TIME_UNKNOWN = -1
     POWER_TIME_UNLIMITED = -2
-else:
 
-    class BatteryTime(enum.IntEnum):
-        POWER_TIME_UNKNOWN = -1
-        POWER_TIME_UNLIMITED = -2
 
-    globals().update(BatteryTime.__members__)
+globals().update(BatteryTime.__members__)
 
 # --- others
 
 ENCODING = sys.getfilesystemencoding()
-if not PY3:
-    ENCODING_ERRS = "replace"
-else:
-    try:
-        ENCODING_ERRS = sys.getfilesystemencodeerrors()  # py 3.6
-    except AttributeError:
-        ENCODING_ERRS = "surrogateescape" if POSIX else "replace"
+ENCODING_ERRS = sys.getfilesystemencodeerrors()
 
 
 # ===================================================================
@@ -273,7 +249,7 @@ if AF_INET6 is not None:
         "udp6": ([AF_INET6], [SOCK_DGRAM]),
     })
 
-if AF_UNIX is not None:
+if AF_UNIX is not None and not SUNOS:
     conn_tmap.update({"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM])})
 
 
@@ -293,9 +269,7 @@ class Error(Exception):
         info = collections.OrderedDict()
         for name in attrs:
             value = getattr(self, name, None)
-            if value:  # noqa
-                info[name] = value
-            elif name == "pid" and value == 0:
+            if value or (name == "pid" and value == 0):
                 info[name] = value
         return info
 
@@ -303,8 +277,8 @@ class Error(Exception):
         # invoked on `raise Error`
         info = self._infodict(("pid", "ppid", "name"))
         if info:
-            details = "(%s)" % ", ".join(
-                ["%s=%r" % (k, v) for k, v in info.items()]
+            details = "({})".format(
+                ", ".join([f"{k}={v!r}" for k, v in info.items()])
             )
         else:
             details = None
@@ -313,8 +287,8 @@ class Error(Exception):
     def __repr__(self):
         # invoked on `repr(Error)`
         info = self._infodict(("pid", "ppid", "name", "seconds", "msg"))
-        details = ", ".join(["%s=%r" % (k, v) for k, v in info.items()])
-        return "psutil.%s(%s)" % (self.__class__.__name__, details)
+        details = ", ".join([f"{k}={v!r}" for k, v in info.items()])
+        return f"psutil.{self.__class__.__name__}({details})"
 
 
 class NoSuchProcess(Error):
@@ -380,7 +354,7 @@ class TimeoutExpired(Error):
         self.seconds = seconds
         self.pid = pid
         self.name = name
-        self.msg = "timeout after %s seconds" % seconds
+        self.msg = f"timeout after {seconds} seconds"
 
     def __reduce__(self):
         return (self.__class__, (self.seconds, self.pid, self.name))
@@ -391,26 +365,6 @@ class TimeoutExpired(Error):
 # ===================================================================
 
 
-# This should be in _compat.py rather than here, but does not work well
-# with setup.py importing this module via a sys.path trick.
-if PY3:
-    if isinstance(__builtins__, dict):  # cpython
-        exec_ = __builtins__["exec"]
-    else:  # pypy
-        exec_ = getattr(__builtins__, "exec")  # noqa
-
-    exec_("""def raise_from(value, from_value):
-    try:
-        raise value from from_value
-    finally:
-        value = None
-    """)
-else:
-
-    def raise_from(value, from_value):
-        raise value
-
-
 def usage_percent(used, total, round_=None):
     """Calculate percentage usage of 'used' against 'total'."""
     try:
@@ -456,7 +410,7 @@ def memoize(fun):
             try:
                 ret = cache[key] = fun(*args, **kwargs)
             except Exception as err:  # noqa: BLE001
-                raise raise_from(err, None)
+                raise err from None
             return ret
 
     def cache_clear():
@@ -505,14 +459,14 @@ def memoize_when_activated(fun):
             try:
                 return fun(self)
             except Exception as err:  # noqa: BLE001
-                raise raise_from(err, None)
+                raise err from None
         except KeyError:
             # case 3: we entered oneshot() ctx but there's no cache
             # for this entry yet
             try:
                 ret = fun(self)
             except Exception as err:  # noqa: BLE001
-                raise raise_from(err, None)
+                raise err from None
             try:
                 self._cache[fun] = ret
             except AttributeError:
@@ -546,9 +500,9 @@ def isfile_strict(path):
     """
     try:
         st = os.stat(path)
-    except OSError as err:
-        if err.errno in {errno.EPERM, errno.EACCES}:
-            raise
+    except PermissionError:
+        raise
+    except OSError:
         return False
     else:
         return stat.S_ISREG(st.st_mode)
@@ -561,9 +515,9 @@ def path_exists_strict(path):
     """
     try:
         os.stat(path)
-    except OSError as err:
-        if err.errno in {errno.EPERM, errno.EACCES}:
-            raise
+    except PermissionError:
+        raise
+    except OSError:
         return False
     else:
         return True
@@ -575,11 +529,10 @@ def supports_ipv6():
     if not socket.has_ipv6 or AF_INET6 is None:
         return False
     try:
-        sock = socket.socket(AF_INET6, socket.SOCK_STREAM)
-        with contextlib.closing(sock):
+        with socket.socket(AF_INET6, socket.SOCK_STREAM) as sock:
             sock.bind(("::1", 0))
         return True
-    except socket.error:
+    except OSError:
         return False
 
 
@@ -615,26 +568,20 @@ def sockfam_to_enum(num):
     """Convert a numeric socket family value to an IntEnum member.
     If it's not a known member, return the numeric value itself.
     """
-    if enum is None:
+    try:
+        return socket.AddressFamily(num)
+    except ValueError:
         return num
-    else:  # pragma: no cover
-        try:
-            return socket.AddressFamily(num)
-        except ValueError:
-            return num
 
 
 def socktype_to_enum(num):
     """Convert a numeric socket type value to an IntEnum member.
     If it's not a known member, return the numeric value itself.
     """
-    if enum is None:
+    try:
+        return socket.SocketKind(num)
+    except ValueError:
         return num
-    else:  # pragma: no cover
-        try:
-            return socket.SocketKind(num)
-        except ValueError:
-            return num
 
 
 def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):
@@ -656,15 +603,37 @@ def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):
         return sconn(fd, fam, type_, laddr, raddr, status, pid)
 
 
+def broadcast_addr(addr):
+    """Given the address ntuple returned by ``net_if_addrs()``
+    calculates the broadcast address.
+    """
+    import ipaddress
+
+    if not addr.address or not addr.netmask:
+        return None
+    if addr.family == socket.AF_INET:
+        return str(
+            ipaddress.IPv4Network(
+                f"{addr.address}/{addr.netmask}", strict=False
+            ).broadcast_address
+        )
+    if addr.family == socket.AF_INET6:
+        return str(
+            ipaddress.IPv6Network(
+                f"{addr.address}/{addr.netmask}", strict=False
+            ).broadcast_address
+        )
+
+
 def deprecated_method(replacement):
     """A decorator which can be used to mark a method as deprecated
     'replcement' is the method name which will be called instead.
     """
 
     def outer(fun):
-        msg = "%s() is deprecated and will be removed; use %s() instead" % (
-            fun.__name__,
-            replacement,
+        msg = (
+            f"{fun.__name__}() is deprecated and will be removed; use"
+            f" {replacement}() instead"
         )
         if fun.__doc__ is None:
             fun.__doc__ = msg
@@ -789,8 +758,6 @@ wrap_numbers.cache_info = _wn.cache_info
 # is 8K. We use a bigger buffer (32K) in order to have more consistent
 # results when reading /proc pseudo files on Linux, see:
 # https://github.com/giampaolo/psutil/issues/2050
-# On Python 2 this also speeds up the reading of big files:
-# (namely /proc/{pid}/smaps and /proc/net/*):
 # https://github.com/giampaolo/psutil/issues/708
 FILE_READ_BUFFER_SIZE = 32 * 1024
 
@@ -800,17 +767,13 @@ def open_binary(fname):
 
 
 def open_text(fname):
-    """On Python 3 opens a file in text mode by using fs encoding and
-    a proper en/decoding errors handler.
-    On Python 2 this is just an alias for open(name, 'rt').
+    """Open a file in text mode by using the proper FS encoding and
+    en/decoding error handlers.
     """
-    if not PY3:
-        return open(fname, buffering=FILE_READ_BUFFER_SIZE)
-
     # See:
     # https://github.com/giampaolo/psutil/issues/675
     # https://github.com/giampaolo/psutil/pull/733
-    fobj = open(
+    fobj = open(  # noqa: SIM115
         fname,
         buffering=FILE_READ_BUFFER_SIZE,
         encoding=ENCODING,
@@ -842,7 +805,7 @@ def cat(fname, fallback=_DEFAULT, _open=open_text):
         try:
             with _open(fname) as f:
                 return f.read()
-        except (IOError, OSError):
+        except OSError:
             return fallback
 
 
@@ -852,7 +815,7 @@ def bcat(fname, fallback=_DEFAULT):
 
 
 def bytes2human(n, format="%(value).1f%(symbol)s"):
-    """Used by various scripts. See: http://goo.gl/zeJZl.
+    """Used by various scripts. See: https://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/?in=user-4178764.
 
     >>> bytes2human(10000)
     '9.8K'
@@ -875,15 +838,8 @@ def get_procfs_path():
     return sys.modules['psutil'].PROCFS_PATH
 
 
-if PY3:
-
-    def decode(s):
-        return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
-
-else:
-
-    def decode(s):
-        return s
+def decode(s):
+    return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
 
 
 # =====================================================================
@@ -927,13 +883,12 @@ def hilite(s, color=None, bold=False):  # pragma: no cover
     try:
         color = colors[color]
     except KeyError:
-        raise ValueError(
-            "invalid color %r; choose between %s" % (list(colors.keys()))
-        )
+        msg = f"invalid color {color!r}; choose amongst {list(colors.keys())}"
+        raise ValueError(msg) from None
     attr.append(color)
     if bold:
         attr.append('1')
-    return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
+    return f"\x1b[{';'.join(attr)}m{s}\x1b[0m"
 
 
 def print_color(
@@ -941,9 +896,9 @@ def print_color(
 ):  # pragma: no cover
     """Print a colorized version of string."""
     if not term_supports_colors():
-        print(s, file=file)  # NOQA
+        print(s, file=file)
     elif POSIX:
-        print(hilite(s, color, bold), file=file)  # NOQA
+        print(hilite(s, color, bold), file=file)
     else:
         import ctypes
 
@@ -958,10 +913,11 @@ def print_color(
         try:
             color = colors[color]
         except KeyError:
-            raise ValueError(
-                "invalid color %r; choose between %r"
-                % (color, list(colors.keys()))
+            msg = (
+                f"invalid color {color!r}; choose between"
+                f" {list(colors.keys())!r}"
             )
+            raise ValueError(msg) from None
         if bold and color <= 7:
             color += 8
 
@@ -970,7 +926,7 @@ def print_color(
         handle = GetStdHandle(handle_id)
         SetConsoleTextAttribute(handle, color)
         try:
-            print(s, file=file)  # NOQA
+            print(s, file=file)
         finally:
             SetConsoleTextAttribute(handle, DEFAULT_COLOR)
 
@@ -984,11 +940,11 @@ def debug(msg):
             inspect.currentframe().f_back
         )
         if isinstance(msg, Exception):
-            if isinstance(msg, (OSError, IOError, EnvironmentError)):
+            if isinstance(msg, OSError):
                 # ...because str(exc) may contain info about the file name
-                msg = "ignoring %s" % msg
+                msg = f"ignoring {msg}"
             else:
-                msg = "ignoring %r" % msg
-        print(  # noqa
-            "psutil-debug [%s:%s]> %s" % (fname, lineno, msg), file=sys.stderr
+                msg = f"ignoring {msg!r}"
+        print(  # noqa: T201
+            f"psutil-debug [{fname}:{lineno}]> {msg}", file=sys.stderr
         )

+ 0 - 477
contrib/python/psutil/py3/psutil/_compat.py

@@ -1,477 +0,0 @@
-# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Module which provides compatibility with older Python versions.
-This is more future-compatible rather than the opposite (prefer latest
-Python 3 way of doing things).
-"""
-
-import collections
-import contextlib
-import errno
-import functools
-import os
-import sys
-import types
-
-
-# fmt: off
-__all__ = [
-    # constants
-    "PY3",
-    # builtins
-    "long", "range", "super", "unicode", "basestring",
-    # literals
-    "b",
-    # collections module
-    "lru_cache",
-    # shutil module
-    "which", "get_terminal_size",
-    # contextlib module
-    "redirect_stderr",
-    # python 3 exceptions
-    "FileNotFoundError", "PermissionError", "ProcessLookupError",
-    "InterruptedError", "ChildProcessError", "FileExistsError",
-]
-# fmt: on
-
-
-PY3 = sys.version_info[0] >= 3
-_SENTINEL = object()
-
-if PY3:
-    long = int
-    xrange = range
-    unicode = str
-    basestring = str
-    range = range
-
-    def b(s):
-        return s.encode("latin-1")
-
-else:
-    long = long
-    range = xrange
-    unicode = unicode
-    basestring = basestring
-
-    def b(s):
-        return s
-
-
-# --- builtins
-
-
-# Python 3 super().
-# Taken from "future" package.
-# Credit: Ryan Kelly
-if PY3:
-    super = super
-else:
-    _builtin_super = super
-
-    def super(type_=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1):
-        """Like Python 3 builtin super(). If called without any arguments
-        it attempts to infer them at runtime.
-        """
-        if type_ is _SENTINEL:
-            f = sys._getframe(framedepth)
-            try:
-                # Get the function's first positional argument.
-                type_or_obj = f.f_locals[f.f_code.co_varnames[0]]
-            except (IndexError, KeyError):
-                msg = 'super() used in a function with no args'
-                raise RuntimeError(msg)
-            try:
-                # Get the MRO so we can crawl it.
-                mro = type_or_obj.__mro__
-            except (AttributeError, RuntimeError):
-                try:
-                    mro = type_or_obj.__class__.__mro__
-                except AttributeError:
-                    msg = 'super() used in a non-newstyle class'
-                    raise RuntimeError(msg)
-            for type_ in mro:
-                #  Find the class that owns the currently-executing method.
-                for meth in type_.__dict__.values():
-                    # Drill down through any wrappers to the underlying func.
-                    # This handles e.g. classmethod() and staticmethod().
-                    try:
-                        while not isinstance(meth, types.FunctionType):
-                            if isinstance(meth, property):
-                                # Calling __get__ on the property will invoke
-                                # user code which might throw exceptions or
-                                # have side effects
-                                meth = meth.fget
-                            else:
-                                try:
-                                    meth = meth.__func__
-                                except AttributeError:
-                                    meth = meth.__get__(type_or_obj, type_)
-                    except (AttributeError, TypeError):
-                        continue
-                    if meth.func_code is f.f_code:
-                        break  # found
-                else:
-                    # Not found. Move onto the next class in MRO.
-                    continue
-                break  # found
-            else:
-                msg = 'super() called outside a method'
-                raise RuntimeError(msg)
-
-        # Dispatch to builtin super().
-        if type_or_obj is not _SENTINEL:
-            return _builtin_super(type_, type_or_obj)
-        return _builtin_super(type_)
-
-
-# --- 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
-    import platform
-
-    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__(  # noqa
-                            *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', _SENTINEL) == errno.ENOENT
-
-    @_instance_checking_exception(EnvironmentError)
-    def ProcessLookupError(inst):
-        return getattr(inst, 'errno', _SENTINEL) == errno.ESRCH
-
-    @_instance_checking_exception(EnvironmentError)
-    def PermissionError(inst):
-        return getattr(inst, 'errno', _SENTINEL) in {errno.EACCES, errno.EPERM}
-
-    @_instance_checking_exception(EnvironmentError)
-    def InterruptedError(inst):
-        return getattr(inst, 'errno', _SENTINEL) == errno.EINTR
-
-    @_instance_checking_exception(EnvironmentError)
-    def ChildProcessError(inst):
-        return getattr(inst, 'errno', _SENTINEL) == errno.ECHILD
-
-    @_instance_checking_exception(EnvironmentError)
-    def FileExistsError(inst):
-        return getattr(inst, 'errno', _SENTINEL) == errno.EEXIST
-
-    if platform.python_implementation() != "CPython":
-        try:
-            raise OSError(errno.EEXIST, "perm")
-        except FileExistsError:
-            pass
-        except OSError:
-            msg = (
-                "broken or incompatible Python implementation, see: "
-                "https://github.com/giampaolo/psutil/issues/1659"
-            )
-            raise RuntimeError(msg)
-
-
-# --- stdlib additions
-
-
-# py 3.2 functools.lru_cache
-# Taken from: http://code.activestate.com/recipes/578078
-# Credit: Raymond Hettinger
-try:
-    from functools import lru_cache
-except ImportError:
-    try:
-        from threading import RLock
-    except ImportError:
-        from dummy_threading import RLock
-
-    _CacheInfo = collections.namedtuple(
-        "CacheInfo", ["hits", "misses", "maxsize", "currsize"]
-    )
-
-    class _HashedSeq(list):  # noqa: FURB189
-        __slots__ = ('hashvalue',)
-
-        def __init__(self, tup, hash=hash):
-            self[:] = tup
-            self.hashvalue = hash(tup)
-
-        def __hash__(self):
-            return self.hashvalue
-
-    def _make_key(
-        args,
-        kwds,
-        typed,
-        kwd_mark=(_SENTINEL,),
-        fasttypes=set((int, str, frozenset, type(None))),  # noqa
-        sorted=sorted,
-        tuple=tuple,
-        type=type,
-        len=len,
-    ):
-        key = args
-        if kwds:
-            sorted_items = sorted(kwds.items())
-            key += kwd_mark
-            for item in sorted_items:
-                key += item
-        if typed:
-            key += tuple(type(v) for v in args)
-            if kwds:
-                key += tuple(type(v) for k, v in sorted_items)
-        elif len(key) == 1 and type(key[0]) in fasttypes:
-            return key[0]
-        return _HashedSeq(key)
-
-    def lru_cache(maxsize=100, typed=False):
-        """Least-recently-used cache decorator, see:
-        http://docs.python.org/3/library/functools.html#functools.lru_cache.
-        """
-
-        def decorating_function(user_function):
-            cache = {}
-            stats = [0, 0]
-            HITS, MISSES = 0, 1
-            make_key = _make_key
-            cache_get = cache.get
-            _len = len
-            lock = RLock()
-            root = []
-            root[:] = [root, root, None, None]
-            nonlocal_root = [root]
-            PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
-            if maxsize == 0:
-
-                def wrapper(*args, **kwds):
-                    result = user_function(*args, **kwds)
-                    stats[MISSES] += 1
-                    return result
-
-            elif maxsize is None:
-
-                def wrapper(*args, **kwds):
-                    key = make_key(args, kwds, typed)
-                    result = cache_get(key, root)
-                    if result is not root:
-                        stats[HITS] += 1
-                        return result
-                    result = user_function(*args, **kwds)
-                    cache[key] = result
-                    stats[MISSES] += 1
-                    return result
-
-            else:
-
-                def wrapper(*args, **kwds):
-                    if kwds or typed:
-                        key = make_key(args, kwds, typed)
-                    else:
-                        key = args
-                    lock.acquire()
-                    try:
-                        link = cache_get(key)
-                        if link is not None:
-                            (root,) = nonlocal_root
-                            link_prev, link_next, key, result = link
-                            link_prev[NEXT] = link_next
-                            link_next[PREV] = link_prev
-                            last = root[PREV]
-                            last[NEXT] = root[PREV] = link
-                            link[PREV] = last
-                            link[NEXT] = root
-                            stats[HITS] += 1
-                            return result
-                    finally:
-                        lock.release()
-                    result = user_function(*args, **kwds)
-                    lock.acquire()
-                    try:
-                        (root,) = nonlocal_root
-                        if key in cache:
-                            pass
-                        elif _len(cache) >= maxsize:
-                            oldroot = root
-                            oldroot[KEY] = key
-                            oldroot[RESULT] = result
-                            root = nonlocal_root[0] = oldroot[NEXT]
-                            oldkey = root[KEY]
-                            root[KEY] = root[RESULT] = None
-                            del cache[oldkey]
-                            cache[key] = oldroot
-                        else:
-                            last = root[PREV]
-                            link = [last, root, key, result]
-                            last[NEXT] = root[PREV] = cache[key] = link
-                        stats[MISSES] += 1
-                    finally:
-                        lock.release()
-                    return result
-
-            def cache_info():
-                """Report cache statistics."""
-                lock.acquire()
-                try:
-                    return _CacheInfo(
-                        stats[HITS], stats[MISSES], maxsize, len(cache)
-                    )
-                finally:
-                    lock.release()
-
-            def cache_clear():
-                """Clear the cache and cache statistics."""
-                lock.acquire()
-                try:
-                    cache.clear()
-                    root = nonlocal_root[0]
-                    root[:] = [root, root, None, None]
-                    stats[:] = [0, 0]
-                finally:
-                    lock.release()
-
-            wrapper.__wrapped__ = user_function
-            wrapper.cache_info = cache_info
-            wrapper.cache_clear = cache_clear
-            return functools.update_wrapper(wrapper, user_function)
-
-        return decorating_function
-
-
-# python 3.3
-try:
-    from shutil import which
-except ImportError:
-
-    def which(cmd, mode=os.F_OK | os.X_OK, path=None):
-        """Given a command, mode, and a PATH string, return the path which
-        conforms to the given mode on the PATH, or None if there is no such
-        file.
-
-        `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
-        of os.environ.get("PATH"), or can be overridden with a custom search
-        path.
-        """
-
-        def _access_check(fn, mode):
-            return (
-                os.path.exists(fn)
-                and os.access(fn, mode)
-                and not os.path.isdir(fn)
-            )
-
-        if os.path.dirname(cmd):
-            if _access_check(cmd, mode):
-                return cmd
-            return None
-
-        if path is None:
-            path = os.environ.get("PATH", os.defpath)
-        if not path:
-            return None
-        path = path.split(os.pathsep)
-
-        if sys.platform == "win32":
-            if os.curdir not in path:
-                path.insert(0, os.curdir)
-
-            pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
-            if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
-                files = [cmd]
-            else:
-                files = [cmd + ext for ext in pathext]
-        else:
-            files = [cmd]
-
-        seen = set()
-        for dir in path:
-            normdir = os.path.normcase(dir)
-            if normdir not in seen:
-                seen.add(normdir)
-                for thefile in files:
-                    name = os.path.join(dir, thefile)
-                    if _access_check(name, mode):
-                        return name
-        return None
-
-
-# python 3.3
-try:
-    from shutil import get_terminal_size
-except ImportError:
-
-    def get_terminal_size(fallback=(80, 24)):
-        try:
-            import fcntl
-            import struct
-            import termios
-        except ImportError:
-            return fallback
-        else:
-            try:
-                # This should work on Linux.
-                res = struct.unpack(
-                    'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234')
-                )
-                return (res[1], res[0])
-            except Exception:  # noqa: BLE001
-                return fallback
-
-
-# python 3.3
-try:
-    from subprocess import TimeoutExpired as SubprocessTimeoutExpired
-except ImportError:
-
-    class SubprocessTimeoutExpired(Exception):
-        pass
-
-
-# python 3.5
-try:
-    from contextlib import redirect_stderr
-except ImportError:
-
-    @contextlib.contextmanager
-    def redirect_stderr(new_target):
-        original = sys.stderr
-        try:
-            sys.stderr = new_target
-            yield new_target
-        finally:
-            sys.stderr = original

+ 24 - 38
contrib/python/psutil/py3/psutil/_psaix.py

@@ -28,10 +28,6 @@ from ._common import conn_to_ntuple
 from ._common import get_procfs_path
 from ._common import memoize_when_activated
 from ._common import usage_percent
-from ._compat import PY3
-from ._compat import FileNotFoundError
-from ._compat import PermissionError
-from ._compat import ProcessLookupError
 
 
 __extra__all__ = ["PROCFS_PATH"]
@@ -148,12 +144,10 @@ def cpu_count_cores():
     cmd = ["lsdev", "-Cc", "processor"]
     p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     stdout, stderr = p.communicate()
-    if PY3:
-        stdout, stderr = (
-            x.decode(sys.stdout.encoding) for x in (stdout, stderr)
-        )
+    stdout, stderr = (x.decode(sys.stdout.encoding) for x in (stdout, stderr))
     if p.returncode != 0:
-        raise RuntimeError("%r command error\n%s" % (cmd, stderr))
+        msg = f"{cmd!r} command error\n{stderr}"
+        raise RuntimeError(msg)
     processors = stdout.strip().splitlines()
     return len(processors) or None
 
@@ -211,12 +205,6 @@ def net_connections(kind, _pid=-1):
     """Return socket connections.  If pid == -1 return system-wide
     connections (as opposed to connections opened by one process only).
     """
-    cmap = _common.conn_tmap
-    if kind not in cmap:
-        raise ValueError(
-            "invalid %r kind argument; choose between %s"
-            % (kind, ', '.join([repr(x) for x in cmap]))
-        )
     families, types = _common.conn_tmap[kind]
     rawlist = cext.net_connections(_pid)
     ret = []
@@ -243,7 +231,7 @@ def net_connections(kind, _pid=-1):
 def net_if_stats():
     """Get NIC stats (isup, duplex, speed, mtu)."""
     duplex_map = {"Full": NIC_DUPLEX_FULL, "Half": NIC_DUPLEX_HALF}
-    names = set([x[0] for x in net_if_addrs()])
+    names = {x[0] for x in net_if_addrs()}
     ret = {}
     for name in names:
         mtu = cext_posix.net_if_mtu(name)
@@ -260,10 +248,9 @@ def net_if_stats():
             stderr=subprocess.PIPE,
         )
         stdout, stderr = p.communicate()
-        if PY3:
-            stdout, stderr = (
-                x.decode(sys.stdout.encoding) for x in (stdout, stderr)
-            )
+        stdout, stderr = (
+            x.decode(sys.stdout.encoding) for x in (stdout, stderr)
+        )
         if p.returncode == 0:
             re_result = re.search(
                 r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout
@@ -330,18 +317,18 @@ def wrap_exceptions(fun):
 
     @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
+        pid, ppid, name = self.pid, self._ppid, self._name
         try:
             return fun(self, *args, **kwargs)
-        except (FileNotFoundError, ProcessLookupError):
+        except (FileNotFoundError, ProcessLookupError) as err:
             # 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)
+            if not pid_exists(pid):
+                raise NoSuchProcess(pid, name) from err
+            raise ZombieProcess(pid, name, ppid) from err
+        except PermissionError as err:
+            raise AccessDenied(pid, name) from err
 
     return wrapper
 
@@ -444,7 +431,7 @@ class Process:
             # is no longer there.
             if not retlist:
                 # will raise NSP if process is gone
-                os.stat('%s/%s' % (self._procfs_path, self.pid))
+                os.stat(f"{self._procfs_path}/{self.pid}")
             return retlist
 
     @wrap_exceptions
@@ -457,7 +444,7 @@ class Process:
         # is no longer there.
         if not ret:
             # will raise NSP if process is gone
-            os.stat('%s/%s' % (self._procfs_path, self.pid))
+            os.stat(f"{self._procfs_path}/{self.pid}")
         return ret
 
     @wrap_exceptions
@@ -503,10 +490,10 @@ class Process:
     def cwd(self):
         procfs_path = self._procfs_path
         try:
-            result = os.readlink("%s/%s/cwd" % (procfs_path, self.pid))
+            result = os.readlink(f"{procfs_path}/{self.pid}/cwd")
             return result.rstrip('/')
         except FileNotFoundError:
-            os.stat("%s/%s" % (procfs_path, self.pid))  # raise NSP or AD
+            os.stat(f"{procfs_path}/{self.pid}")  # raise NSP or AD
             return ""
 
     @wrap_exceptions
@@ -533,10 +520,9 @@ class Process:
             stderr=subprocess.PIPE,
         )
         stdout, stderr = p.communicate()
-        if PY3:
-            stdout, stderr = (
-                x.decode(sys.stdout.encoding) for x in (stdout, stderr)
-            )
+        stdout, stderr = (
+            x.decode(sys.stdout.encoding) for x in (stdout, stderr)
+        )
         if "no such process" in stderr.lower():
             raise NoSuchProcess(self.pid, self._name)
         procfiles = re.findall(r"(\d+): S_IFREG.*name:(.*)\n", stdout)
@@ -554,7 +540,7 @@ class Process:
     def num_fds(self):
         if self.pid == 0:  # no /proc/0/fd
             return 0
-        return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
+        return len(os.listdir(f"{self._procfs_path}/{self.pid}/fd"))
 
     @wrap_exceptions
     def num_ctx_switches(self):
@@ -570,10 +556,10 @@ class Process:
         def io_counters(self):
             try:
                 rc, wc, rb, wb = cext.proc_io_counters(self.pid)
-            except OSError:
+            except OSError as err:
                 # if process is terminated, proc_io_counters returns OSError
                 # instead of NSP
                 if not pid_exists(self.pid):
-                    raise NoSuchProcess(self.pid, self._name)
+                    raise NoSuchProcess(self.pid, self._name) from err
                 raise
             return _common.pio(rc, wc, rb, wb)

+ 44 - 58
contrib/python/psutil/py3/psutil/_psbsd.py

@@ -10,7 +10,7 @@ import functools
 import os
 from collections import defaultdict
 from collections import namedtuple
-from xml.etree import ElementTree  # noqa ICN001
+from xml.etree import ElementTree  # noqa: ICN001
 
 from . import _common
 from . import _psposix
@@ -28,10 +28,6 @@ from ._common import debug
 from ._common import memoize
 from ._common import memoize_when_activated
 from ._common import usage_percent
-from ._compat import FileNotFoundError
-from ._compat import PermissionError
-from ._compat import ProcessLookupError
-from ._compat import which
 
 
 __extra__all__ = []
@@ -196,8 +192,8 @@ def virtual_memory():
         # #2233), so zabbix seems to be wrong. Htop calculates it
         # differently, and the used value seem more realistic, so let's
         # match htop.
-        # https://github.com/htop-dev/htop/blob/e7f447b/netbsd/NetBSDProcessList.c#L162  # noqa
-        # https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/netbsd/memory.c#L135  # noqa
+        # https://github.com/htop-dev/htop/blob/e7f447b/netbsd/NetBSDProcessList.c#L162
+        # https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/netbsd/memory.c#L135
         used = active + wired
         avail = total - used
     else:
@@ -206,7 +202,7 @@ def virtual_memory():
         # * https://people.freebsd.org/~rse/dist/freebsd-memory
         # * https://www.cyberciti.biz/files/scripts/freebsd-memory.pl.txt
         # matches zabbix:
-        # * https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/freebsd/memory.c#L143  # noqa
+        # * https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/freebsd/memory.c#L143
         avail = inactive + cached + free
         used = active + wired + cached
 
@@ -439,14 +435,8 @@ def net_if_stats():
 
 def net_connections(kind):
     """System-wide network connections."""
-    if kind not in _common.conn_tmap:
-        raise ValueError(
-            "invalid %r kind argument; choose between %s"
-            % (kind, ', '.join([repr(x) for x in conn_tmap]))
-        )
     families, types = conn_tmap[kind]
     ret = set()
-
     if OPENBSD:
         rawlist = cext.net_connections(-1, families, types)
     elif NETBSD:
@@ -495,7 +485,7 @@ if FREEBSD:
                 current, high = cext.sensors_cpu_temperature(cpu)
                 if high <= 0:
                     high = None
-                name = "Core %s" % cpu
+                name = f"Core {cpu}"
                 ret["coretemp"].append(
                     _common.shwtemp(name, current, high, high)
                 )
@@ -600,21 +590,18 @@ def wrap_exceptions(fun):
 
     @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
+        pid, ppid, name = self.pid, self._ppid, self._name
         try:
             return fun(self, *args, **kwargs)
-        except ProcessLookupError:
-            if is_zombie(self.pid):
-                raise ZombieProcess(self.pid, self._name, self._ppid)
-            else:
-                raise NoSuchProcess(self.pid, self._name)
-        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
+        except ProcessLookupError as err:
+            if is_zombie(pid):
+                raise ZombieProcess(pid, name, ppid) from err
+            raise NoSuchProcess(pid, name) from err
+        except PermissionError as err:
+            raise AccessDenied(pid, name) from err
+        except OSError as err:
+            if pid == 0 and 0 in pids():
+                raise AccessDenied(pid, name) from err
             raise
 
     return wrapper
@@ -623,18 +610,19 @@ def wrap_exceptions(fun):
 @contextlib.contextmanager
 def wrap_exceptions_procfs(inst):
     """Same as above, for routines relying on reading /proc fs."""
+    pid, name, ppid = inst.pid, inst._name, inst._ppid
     try:
         yield
-    except (ProcessLookupError, FileNotFoundError):
+    except (ProcessLookupError, FileNotFoundError) as err:
         # 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 is_zombie(inst.pid):
-            raise ZombieProcess(inst.pid, inst._name, inst._ppid)
+            raise ZombieProcess(pid, name, ppid) from err
         else:
-            raise NoSuchProcess(inst.pid, inst._name)
-    except PermissionError:
-        raise AccessDenied(inst.pid, inst._name)
+            raise NoSuchProcess(pid, name) from err
+    except PermissionError as err:
+        raise AccessDenied(pid, name) from err
 
 
 class Process:
@@ -683,16 +671,18 @@ class Process:
                 # /proc/0 dir exists but /proc/0/exe doesn't
                 return ""
             with wrap_exceptions_procfs(self):
-                return os.readlink("/proc/%s/exe" % self.pid)
+                return os.readlink(f"/proc/{self.pid}/exe")
         else:
             # OpenBSD: exe cannot be determined; references:
             # https://chromium.googlesource.com/chromium/src/base/+/
             #     master/base_paths_posix.cc
             # We try our best guess by using which against the first
             # cmdline arg (may return None).
+            import shutil
+
             cmdline = self.cmdline()
             if cmdline:
-                return which(cmdline[0]) or ""
+                return shutil.which(cmdline[0]) or ""
             else:
                 return ""
 
@@ -709,15 +699,15 @@ class Process:
                 return cext.proc_cmdline(self.pid)
             except OSError as err:
                 if err.errno == errno.EINVAL:
+                    pid, name, ppid = self.pid, self._name, self._ppid
                     if is_zombie(self.pid):
-                        raise ZombieProcess(self.pid, self._name, self._ppid)
-                    elif not pid_exists(self.pid):
-                        raise NoSuchProcess(self.pid, self._name, self._ppid)
-                    else:
-                        # XXX: this happens with unicode tests. It means the C
-                        # routine is unable to decode invalid unicode chars.
-                        debug("ignoring %r and returning an empty list" % err)
-                        return []
+                        raise ZombieProcess(pid, name, ppid) from err
+                    if not pid_exists(self.pid):
+                        raise NoSuchProcess(pid, name, ppid) from err
+                    # XXX: this happens with unicode tests. It means the C
+                    # routine is unable to decode invalid unicode chars.
+                    debug(f"ignoring {err!r} and returning an empty list")
+                    return []
                 else:
                     raise
         else:
@@ -822,11 +812,6 @@ class Process:
 
     @wrap_exceptions
     def net_connections(self, kind='inet'):
-        if kind not in conn_tmap:
-            raise ValueError(
-                "invalid %r kind argument; choose between %s"
-                % (kind, ', '.join([repr(x) for x in conn_tmap]))
-            )
         families, types = conn_tmap[kind]
         ret = []
 
@@ -945,12 +930,11 @@ class Process:
             # Pre-emptively check if CPUs are valid because the C
             # function has a weird behavior in case of invalid CPUs,
             # see: https://github.com/giampaolo/psutil/issues/586
-            allcpus = tuple(range(len(per_cpu_times())))
+            allcpus = set(range(len(per_cpu_times())))
             for cpu in cpus:
                 if cpu not in allcpus:
-                    raise ValueError(
-                        "invalid CPU #%i (choose between %s)" % (cpu, allcpus)
-                    )
+                    msg = f"invalid CPU {cpu!r} (choose between {allcpus})"
+                    raise ValueError(msg)
             try:
                 cext.proc_cpu_affinity_set(self.pid, cpus)
             except OSError as err:
@@ -961,10 +945,11 @@ class Process:
                 if err.errno in {errno.EINVAL, errno.EDEADLK}:
                     for cpu in cpus:
                         if cpu not in allcpus:
-                            raise ValueError(
-                                "invalid CPU #%i (choose between %s)"
-                                % (cpu, allcpus)
+                            msg = (
+                                f"invalid CPU {cpu!r} (choose between"
+                                f" {allcpus})"
                             )
+                            raise ValueError(msg) from err
                 raise
 
         @wrap_exceptions
@@ -977,9 +962,10 @@ class Process:
                 return cext.proc_getrlimit(self.pid, resource)
             else:
                 if len(limits) != 2:
-                    raise ValueError(
-                        "second argument must be a (soft, hard) tuple, got %s"
-                        % repr(limits)
+                    msg = (
+                        "second argument must be a (soft, hard) tuple, got"
+                        f" {limits!r}"
                     )
+                    raise ValueError(msg)
                 soft, hard = limits
                 return cext.proc_setrlimit(self.pid, resource, soft, hard)

File diff suppressed because it is too large
+ 154 - 239
contrib/python/psutil/py3/psutil/_pslinux.py


+ 8 - 16
contrib/python/psutil/py3/psutil/_psosx.py

@@ -22,8 +22,6 @@ from ._common import isfile_strict
 from ._common import memoize_when_activated
 from ._common import parse_environ_block
 from ._common import usage_percent
-from ._compat import PermissionError
-from ._compat import ProcessLookupError
 
 
 __extra__all__ = []
@@ -114,8 +112,7 @@ def virtual_memory():
     """System virtual memory as a namedtuple."""
     total, active, inactive, wired, free, speculative = cext.virtual_mem()
     # This is how Zabbix calculate avail and used mem:
-    # https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/
-    #     osx/memory.c
+    # https://github.com/zabbix/zabbix/blob/master/src/libs/zbxsysinfo/osx/memory.c
     # Also see: https://github.com/giampaolo/psutil/issues/1277
     avail = inactive + free
     used = active + wired
@@ -345,15 +342,15 @@ def wrap_exceptions(fun):
 
     @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
+        pid, ppid, name = self.pid, self._ppid, self._name
         try:
             return fun(self, *args, **kwargs)
-        except ProcessLookupError:
-            if is_zombie(self.pid):
-                raise ZombieProcess(self.pid, self._name, self._ppid)
-            else:
-                raise NoSuchProcess(self.pid, self._name)
-        except PermissionError:
-            raise AccessDenied(self.pid, self._name)
+        except ProcessLookupError as err:
+            if is_zombie(pid):
+                raise ZombieProcess(pid, name, ppid) from err
+            raise NoSuchProcess(pid, name) from err
+        except PermissionError as err:
+            raise AccessDenied(pid, name) from err
 
     return wrapper
 
@@ -502,11 +499,6 @@ class Process:
 
     @wrap_exceptions
     def net_connections(self, kind='inet'):
-        if kind not in conn_tmap:
-            raise ValueError(
-                "invalid %r kind argument; choose between %s"
-                % (kind, ', '.join([repr(x) for x in conn_tmap]))
-            )
         families, types = conn_tmap[kind]
         rawlist = cext.proc_net_connections(self.pid, families, types)
         ret = []

Some files were not shown because too many files changed in this diff