summaryrefslogtreecommitdiff
path: root/psutil/_compat.py
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-04-24 03:57:09 -0700
committerGitHub <noreply@github.com>2020-04-24 12:57:09 +0200
commit6242f7411b882d525e5d267de4bcda1079934ea2 (patch)
treeae4dc31674e3d7c26699feb1c8544e308afe90bf /psutil/_compat.py
parentbdb6e7b52d625097ef82742e9647532d3ec7eac4 (diff)
downloadpsutil-6242f7411b882d525e5d267de4bcda1079934ea2.tar.gz
Backport python 3 super() (#1733)
Diffstat (limited to 'psutil/_compat.py')
-rw-r--r--psutil/_compat.py121
1 files changed, 100 insertions, 21 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: