summaryrefslogtreecommitdiffstats
path: root/Lib/abc.py
diff options
context:
space:
mode:
authorIvan Levkivskyi <levkivskyi@gmail.com>2018-02-18 12:41:58 (GMT)
committerGitHub <noreply@github.com>2018-02-18 12:41:58 (GMT)
commit03e3c340a0156891a036d6dbdb9e348108826255 (patch)
tree5b897f150d3855868c7cf3c2fb1c7c6a313d4e31 /Lib/abc.py
parent667b91a5e210e20946ad41f1796c544a1becf1b6 (diff)
downloadcpython-03e3c340a0156891a036d6dbdb9e348108826255.zip
cpython-03e3c340a0156891a036d6dbdb9e348108826255.tar.gz
cpython-03e3c340a0156891a036d6dbdb9e348108826255.tar.bz2
bpo-31333: Re-implement ABCMeta in C (#5273)
This adds C versions of methods used by ABCMeta that improve performance of various ABC operations.
Diffstat (limited to 'Lib/abc.py')
-rw-r--r--Lib/abc.py204
1 files changed, 62 insertions, 142 deletions
diff --git a/Lib/abc.py b/Lib/abc.py
index 9bdc36d..7094141 100644
--- a/Lib/abc.py
+++ b/Lib/abc.py
@@ -3,8 +3,6 @@
"""Abstract Base Classes (ABCs) according to PEP 3119."""
-from _weakrefset import WeakSet
-
def abstractmethod(funcobj):
"""A decorator indicating abstract methods.
@@ -27,8 +25,7 @@ def abstractmethod(funcobj):
class abstractclassmethod(classmethod):
- """
- A decorator indicating abstract classmethods.
+ """A decorator indicating abstract classmethods.
Similar to abstractmethod.
@@ -51,8 +48,7 @@ class abstractclassmethod(classmethod):
class abstractstaticmethod(staticmethod):
- """
- A decorator indicating abstract staticmethods.
+ """A decorator indicating abstract staticmethods.
Similar to abstractmethod.
@@ -75,8 +71,7 @@ class abstractstaticmethod(staticmethod):
class abstractproperty(property):
- """
- A decorator indicating abstract properties.
+ """A decorator indicating abstract properties.
Requires that the metaclass is ABCMeta or derived from it. A
class that has a metaclass derived from ABCMeta cannot be
@@ -106,131 +101,66 @@ class abstractproperty(property):
__isabstractmethod__ = True
-class ABCMeta(type):
-
- """Metaclass for defining Abstract Base Classes (ABCs).
-
- Use this metaclass to create an ABC. An ABC can be subclassed
- directly, and then acts as a mix-in class. You can also register
- unrelated concrete classes (even built-in classes) and unrelated
- ABCs as 'virtual subclasses' -- these and their descendants will
- be considered subclasses of the registering ABC by the built-in
- issubclass() function, but the registering ABC won't show up in
- their MRO (Method Resolution Order) nor will method
- implementations defined by the registering ABC be callable (not
- even via super()).
-
- """
-
- # A global counter that is incremented each time a class is
- # registered as a virtual subclass of anything. It forces the
- # negative cache to be cleared before its next use.
- # Note: this counter is private. Use `abc.get_cache_token()` for
- # external code.
- _abc_invalidation_counter = 0
-
- def __new__(mcls, name, bases, namespace, **kwargs):
- cls = super().__new__(mcls, name, bases, namespace, **kwargs)
- # Compute set of abstract method names
- abstracts = {name
- for name, value in namespace.items()
- if getattr(value, "__isabstractmethod__", False)}
- for base in bases:
- for name in getattr(base, "__abstractmethods__", set()):
- value = getattr(cls, name, None)
- if getattr(value, "__isabstractmethod__", False):
- abstracts.add(name)
- cls.__abstractmethods__ = frozenset(abstracts)
- # Set up inheritance registry
- cls._abc_registry = WeakSet()
- cls._abc_cache = WeakSet()
- cls._abc_negative_cache = WeakSet()
- cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
- return cls
-
- def register(cls, subclass):
- """Register a virtual subclass of an ABC.
-
- Returns the subclass, to allow usage as a class decorator.
+try:
+ from _abc import (get_cache_token, _abc_init, _abc_register,
+ _abc_instancecheck, _abc_subclasscheck, _get_dump,
+ _reset_registry, _reset_caches)
+except ImportError:
+ from _py_abc import ABCMeta, get_cache_token
+ ABCMeta.__module__ = 'abc'
+else:
+ class ABCMeta(type):
+ """Metaclass for defining Abstract Base Classes (ABCs).
+
+ Use this metaclass to create an ABC. An ABC can be subclassed
+ directly, and then acts as a mix-in class. You can also register
+ unrelated concrete classes (even built-in classes) and unrelated
+ ABCs as 'virtual subclasses' -- these and their descendants will
+ be considered subclasses of the registering ABC by the built-in
+ issubclass() function, but the registering ABC won't show up in
+ their MRO (Method Resolution Order) nor will method
+ implementations defined by the registering ABC be callable (not
+ even via super()).
"""
- if not isinstance(subclass, type):
- raise TypeError("Can only register classes")
- if issubclass(subclass, cls):
- return subclass # Already a subclass
- # Subtle: test for cycles *after* testing for "already a subclass";
- # this means we allow X.register(X) and interpret it as a no-op.
- if issubclass(cls, subclass):
- # This would create a cycle, which is bad for the algorithm below
- raise RuntimeError("Refusing to create an inheritance cycle")
- cls._abc_registry.add(subclass)
- ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
- return subclass
-
- def _dump_registry(cls, file=None):
- """Debug helper to print the ABC registry."""
- print("Class: %s.%s" % (cls.__module__, cls.__qualname__), file=file)
- print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file)
- for name in cls.__dict__:
- if name.startswith("_abc_"):
- value = getattr(cls, name)
- if isinstance(value, WeakSet):
- value = set(value)
- print("%s: %r" % (name, value), file=file)
-
- def __instancecheck__(cls, instance):
- """Override for isinstance(instance, cls)."""
- # Inline the cache checking
- subclass = instance.__class__
- if subclass in cls._abc_cache:
- return True
- subtype = type(instance)
- if subtype is subclass:
- if (cls._abc_negative_cache_version ==
- ABCMeta._abc_invalidation_counter and
- subclass in cls._abc_negative_cache):
- return False
- # Fall back to the subclass check.
- return cls.__subclasscheck__(subclass)
- return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
-
- def __subclasscheck__(cls, subclass):
- """Override for issubclass(subclass, cls)."""
- # Check cache
- if subclass in cls._abc_cache:
- return True
- # Check negative cache; may have to invalidate
- if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
- # Invalidate the negative cache
- cls._abc_negative_cache = WeakSet()
- cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
- elif subclass in cls._abc_negative_cache:
- return False
- # Check the subclass hook
- ok = cls.__subclasshook__(subclass)
- if ok is not NotImplemented:
- assert isinstance(ok, bool)
- if ok:
- cls._abc_cache.add(subclass)
- else:
- cls._abc_negative_cache.add(subclass)
- return ok
- # Check if it's a direct subclass
- if cls in getattr(subclass, '__mro__', ()):
- cls._abc_cache.add(subclass)
- return True
- # Check if it's a subclass of a registered class (recursive)
- for rcls in cls._abc_registry:
- if issubclass(subclass, rcls):
- cls._abc_cache.add(subclass)
- return True
- # Check if it's a subclass of a subclass (recursive)
- for scls in cls.__subclasses__():
- if issubclass(subclass, scls):
- cls._abc_cache.add(subclass)
- return True
- # No dice; update negative cache
- cls._abc_negative_cache.add(subclass)
- return False
+ def __new__(mcls, name, bases, namespace, **kwargs):
+ cls = super().__new__(mcls, name, bases, namespace, **kwargs)
+ _abc_init(cls)
+ return cls
+
+ def register(cls, subclass):
+ """Register a virtual subclass of an ABC.
+
+ Returns the subclass, to allow usage as a class decorator.
+ """
+ return _abc_register(cls, subclass)
+
+ def __instancecheck__(cls, instance):
+ """Override for isinstance(instance, cls)."""
+ return _abc_instancecheck(cls, instance)
+
+ def __subclasscheck__(cls, subclass):
+ """Override for issubclass(subclass, cls)."""
+ return _abc_subclasscheck(cls, subclass)
+
+ def _dump_registry(cls, file=None):
+ """Debug helper to print the ABC registry."""
+ print(f"Class: {cls.__module__}.{cls.__qualname__}", file=file)
+ print(f"Inv. counter: {get_cache_token()}", file=file)
+ (_abc_registry, _abc_cache, _abc_negative_cache,
+ _abc_negative_cache_version) = _get_dump(cls)
+ print(f"_abc_registry: {_abc_registry!r}", file=file)
+ print(f"_abc_cache: {_abc_cache!r}", file=file)
+ print(f"_abc_negative_cache: {_abc_negative_cache!r}", file=file)
+ print(f"_abc_negative_cache_version: {_abc_negative_cache_version!r}",
+ file=file)
+
+ def _abc_registry_clear(cls):
+ """Clear the registry (for debugging or testing)."""
+ _reset_registry(cls)
+
+ def _abc_caches_clear(cls):
+ """Clear the caches (for debugging or testing)."""
+ _reset_caches(cls)
class ABC(metaclass=ABCMeta):
@@ -238,13 +168,3 @@ class ABC(metaclass=ABCMeta):
inheritance.
"""
__slots__ = ()
-
-
-def get_cache_token():
- """Returns the current ABC cache token.
-
- The token is an opaque object (supporting equality testing) identifying the
- current version of the ABC cache for virtual subclasses. The token changes
- with every call to ``register()`` on any ABC.
- """
- return ABCMeta._abc_invalidation_counter