diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2020-04-24 03:57:09 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-24 12:57:09 +0200 |
commit | 6242f7411b882d525e5d267de4bcda1079934ea2 (patch) | |
tree | ae4dc31674e3d7c26699feb1c8544e308afe90bf | |
parent | bdb6e7b52d625097ef82742e9647532d3ec7eac4 (diff) | |
download | psutil-6242f7411b882d525e5d267de4bcda1079934ea2.tar.gz |
Backport python 3 super() (#1733)
-rw-r--r-- | psutil/_compat.py | 121 | ||||
-rw-r--r-- | psutil/_pslinux.py | 2 | ||||
-rw-r--r-- | psutil/_pswindows.py | 4 | ||||
-rw-r--r-- | psutil/tests/__init__.py | 6 | ||||
-rwxr-xr-x | psutil/tests/test_contracts.py | 3 | ||||
-rwxr-xr-x | psutil/tests/test_memory_leaks.py | 5 | ||||
-rwxr-xr-x | psutil/tests/test_misc.py | 5 |
7 files changed, 112 insertions, 34 deletions
diff --git a/psutil/_compat.py b/psutil/_compat.py index 64a5761e..145fb71d 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -2,7 +2,10 @@ # 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.""" +"""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 @@ -10,19 +13,33 @@ import errno import functools import os import sys +import types + +__all__ = [ + # constants + "PY3", + # builtins + "long", "range", "super", "unicode", "basestring", + # literals + "u", "b", + # collections module + "lru_cache", "redirect_stderr", + # shutil module + "which", "get_terminal_size", + # python 3 exceptions + "FileNotFoundError", "PermissionError", "ProcessLookupError", + "InterruptedError", "ChildProcessError", "FileExistsError"] -__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b", - "lru_cache", "which", "get_terminal_size", "redirect_stderr", - "FileNotFoundError", "PermissionError", "ProcessLookupError", - "InterruptedError", "ChildProcessError", "FileExistsError"] PY3 = sys.version_info[0] == 3 +_SENTINEL = object() if PY3: long = int xrange = range unicode = str basestring = str + range = range def u(s): return s @@ -31,7 +48,7 @@ if PY3: return s.encode("latin-1") else: long = long - xrange = xrange + range = xrange unicode = unicode basestring = basestring @@ -42,6 +59,70 @@ else: 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): + raise RuntimeError('super() used in a function with no args') + 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: + raise RuntimeError('super() used in a non-newstyle class') + 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: + raise RuntimeError('super() called outside a method') + + # Dispatch to builtin super(). + if type_or_obj is not _SENTINEL: + return _builtin_super(type_, type_or_obj) + return _builtin_super(type_) + + # --- exceptions @@ -57,9 +138,7 @@ else: # src/future/types/exceptions/pep3151.py import platform - _singleton = object() - - def instance_checking_exception(base_exception=Exception): + def _instance_checking_exception(base_exception=Exception): def wrapped(instance_checker): class TemporaryClass(base_exception): @@ -86,30 +165,30 @@ else: return wrapped - @instance_checking_exception(EnvironmentError) + @_instance_checking_exception(EnvironmentError) def FileNotFoundError(inst): - return getattr(inst, 'errno', _singleton) == errno.ENOENT + return getattr(inst, 'errno', _SENTINEL) == errno.ENOENT - @instance_checking_exception(EnvironmentError) + @_instance_checking_exception(EnvironmentError) def ProcessLookupError(inst): - return getattr(inst, 'errno', _singleton) == errno.ESRCH + return getattr(inst, 'errno', _SENTINEL) == errno.ESRCH - @instance_checking_exception(EnvironmentError) + @_instance_checking_exception(EnvironmentError) def PermissionError(inst): - return getattr(inst, 'errno', _singleton) in ( + return getattr(inst, 'errno', _SENTINEL) in ( errno.EACCES, errno.EPERM) - @instance_checking_exception(EnvironmentError) + @_instance_checking_exception(EnvironmentError) def InterruptedError(inst): - return getattr(inst, 'errno', _singleton) == errno.EINTR + return getattr(inst, 'errno', _SENTINEL) == errno.EINTR - @instance_checking_exception(EnvironmentError) + @_instance_checking_exception(EnvironmentError) def ChildProcessError(inst): - return getattr(inst, 'errno', _singleton) == errno.ECHILD + return getattr(inst, 'errno', _SENTINEL) == errno.ECHILD - @instance_checking_exception(EnvironmentError) + @_instance_checking_exception(EnvironmentError) def FileExistsError(inst): - return getattr(inst, 'errno', _singleton) == errno.EEXIST + return getattr(inst, 'errno', _SENTINEL) == errno.EEXIST if platform.python_implementation() != "CPython": try: diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index aca5fd7d..3e3caace 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -846,7 +846,7 @@ class Connections: # old version - let's keep it, just in case... # ip = ip.decode('hex') # return socket.inet_ntop(socket.AF_INET6, - # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) + # ''.join(ip[i:i+4][::-1] for i in range(0, 16, 4))) ip = base64.b16decode(ip) try: # see: https://github.com/giampaolo/psutil/issues/201 diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 99d5d714..c1707db7 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -30,8 +30,8 @@ from ._common import usage_percent from ._compat import long from ._compat import lru_cache from ._compat import PY3 +from ._compat import range from ._compat import unicode -from ._compat import xrange from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS from ._psutil_windows import HIGH_PRIORITY_CLASS @@ -1060,7 +1060,7 @@ class Process(object): @wrap_exceptions def cpu_affinity_get(self): def from_bitmask(x): - return [i for i in xrange(64) if (1 << i) & x] + return [i for i in range(64) if (1 << i) & x] bitmask = cext.proc_cpu_affinity_get(self.pid) return from_bitmask(bitmask) diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index f3358806..d7dd42b2 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -41,16 +41,16 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._common import bytes2human -from psutil._common import supports_ipv6 from psutil._common import print_color +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 range from psutil._compat import u from psutil._compat import unicode from psutil._compat import which -from psutil._compat import xrange if sys.version_info < (2, 7): import unittest2 as unittest # requires "pip install unittest2" @@ -869,7 +869,7 @@ class TestMemoryLeak(unittest.TestCase): return (diff, ncalls) def _call_ntimes(self, fun, times): - return self._itercall(fun, xrange(times))[0] + return self._itercall(fun, range(times))[0] def _call_for(self, fun, secs): def iterator(secs): diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index 312f17d9..51ac3609 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -27,6 +27,7 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._compat import long +from psutil._compat import range from psutil.tests import create_sockets from psutil.tests import enum from psutil.tests import get_kernel_version @@ -620,7 +621,7 @@ class TestFetchAllProcesses(unittest.TestCase): def cpu_affinity(self, ret, proc): self.assertIsInstance(ret, list) assert ret != [], ret - cpus = range(psutil.cpu_count()) + cpus = list(range(psutil.cpu_count())) for n in ret: self.assertIsInstance(n, int) self.assertIn(n, cpus) diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py index b0b4af1b..3d37f35e 100755 --- a/psutil/tests/test_memory_leaks.py +++ b/psutil/tests/test_memory_leaks.py @@ -29,6 +29,7 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._compat import ProcessLookupError +from psutil._compat import super from psutil.tests import CIRRUS from psutil.tests import create_sockets from psutil.tests import get_test_subprocess @@ -283,7 +284,7 @@ class TestTerminatedProcessLeaks(TestProcessObjectLeaks): @classmethod def setUpClass(cls): - super(TestTerminatedProcessLeaks, cls).setUpClass() + super().setUpClass() p = get_test_subprocess() cls.proc = psutil.Process(p.pid) cls.proc.kill() @@ -291,7 +292,7 @@ class TestTerminatedProcessLeaks(TestProcessObjectLeaks): @classmethod def tearDownClass(cls): - super(TestTerminatedProcessLeaks, cls).tearDownClass() + super().tearDownClass() reap_children() def _call(self, fun): diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index ca0a0433..74a0c427 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -42,7 +42,6 @@ from psutil.tests import reload_module from psutil.tests import ROOT_DIR from psutil.tests import SCRIPTS_DIR from psutil.tests import sh -from psutil.tests import TOX from psutil.tests import TRAVIS from psutil.tests import unittest import psutil @@ -624,9 +623,7 @@ class TestWrapNumbers(unittest.TestCase): # =================================================================== -@unittest.skipIf(TOX, "can't test on TOX") -# See: https://travis-ci.org/giampaolo/psutil/jobs/295224806 -@unittest.skipIf(TRAVIS and not os.path.exists(SCRIPTS_DIR), +@unittest.skipIf(not os.path.exists(SCRIPTS_DIR), "can't locate scripts directory") class TestScripts(unittest.TestCase): """Tests for scripts in the "scripts" directory.""" |