bpo-31333: Re-implement ABCMeta in C (#5273)
This adds C versions of methods used by ABCMeta that improve performance of various ABC operations.
This commit is contained in:
parent
667b91a5e2
commit
03e3c340a0
|
@ -853,6 +853,12 @@ Optimizations
|
|||
* Constant folding is moved from peephole optimizer to new AST optimizer.
|
||||
(Contributed by Eugene Toder and INADA Naoki in :issue:`29469`)
|
||||
|
||||
* Most functions and methods in :mod:`abc` have been rewrittent in C.
|
||||
This makes creation of abstract base classes, and calling :func:`isinstance`
|
||||
and :func:`issubclass` on them 1.5x faster. This also reduces Python
|
||||
start-up time by up to 10%. (Contributed by Ivan Levkivskyi and INADA Naoki
|
||||
in :issue:`31333`)
|
||||
|
||||
Build and C API Changes
|
||||
=======================
|
||||
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
from _weakrefset import WeakSet
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
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(f"Class: {cls.__module__}.{cls.__qualname__}", file=file)
|
||||
print(f"Inv. counter: {get_cache_token()}", file=file)
|
||||
for name in cls.__dict__:
|
||||
if name.startswith("_abc_"):
|
||||
value = getattr(cls, name)
|
||||
if isinstance(value, WeakSet):
|
||||
value = set(value)
|
||||
print(f"{name}: {value!r}", file=file)
|
||||
|
||||
def _abc_registry_clear(cls):
|
||||
"""Clear the registry (for debugging or testing)."""
|
||||
cls._abc_registry.clear()
|
||||
|
||||
def _abc_caches_clear(cls):
|
||||
"""Clear the caches (for debugging or testing)."""
|
||||
cls._abc_cache.clear()
|
||||
cls._abc_negative_cache.clear()
|
||||
|
||||
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
|
196
Lib/abc.py
196
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):
|
||||
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).
|
||||
|
||||
"""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.
|
||||
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 __new__(mcls, name, bases, namespace, **kwargs):
|
||||
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
|
||||
_abc_init(cls)
|
||||
return cls
|
||||
|
||||
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 register(cls, subclass):
|
||||
"""Register a virtual subclass of an ABC.
|
||||
|
||||
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})
|
||||
Returns the subclass, to allow usage as a class decorator.
|
||||
"""
|
||||
return _abc_register(cls, subclass)
|
||||
|
||||
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 __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
|
||||
|
|
|
@ -5,6 +5,13 @@ import sys
|
|||
import warnings
|
||||
from inspect import isabstract
|
||||
from test import support
|
||||
try:
|
||||
from _abc import _get_dump
|
||||
except ImportError:
|
||||
def _get_dump(cls):
|
||||
# For legacy Python version
|
||||
return (cls._abc_registry, cls._abc_cache,
|
||||
cls._abc_negative_cache, cls._abc_negative_cache_version)
|
||||
|
||||
|
||||
def dash_R(the_module, test, indirect_test, huntrleaks):
|
||||
|
@ -36,7 +43,7 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
|
|||
if not isabstract(abc):
|
||||
continue
|
||||
for obj in abc.__subclasses__() + [abc]:
|
||||
abcs[obj] = obj._abc_registry.copy()
|
||||
abcs[obj] = _get_dump(obj)[0]
|
||||
|
||||
# bpo-31217: Integer pool to get a single integer object for the same
|
||||
# value. The pool is used to prevent false alarm when checking for memory
|
||||
|
@ -113,7 +120,6 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
|
|||
def dash_R_cleanup(fs, ps, pic, zdc, abcs):
|
||||
import gc, copyreg
|
||||
import collections.abc
|
||||
from weakref import WeakSet
|
||||
|
||||
# Restore some original values.
|
||||
warnings.filters[:] = fs
|
||||
|
@ -137,9 +143,10 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs):
|
|||
abs_classes = filter(isabstract, abs_classes)
|
||||
for abc in abs_classes:
|
||||
for obj in abc.__subclasses__() + [abc]:
|
||||
obj._abc_registry = abcs.get(obj, WeakSet()).copy()
|
||||
obj._abc_cache.clear()
|
||||
obj._abc_negative_cache.clear()
|
||||
for ref in abcs.get(obj, set()):
|
||||
if ref() is not None:
|
||||
obj.register(ref())
|
||||
obj._abc_caches_clear()
|
||||
|
||||
clear_caches()
|
||||
|
||||
|
|
|
@ -1,422 +1,445 @@
|
|||
# Copyright 2007 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
# Note: each test is run with Python and C versions of ABCMeta. Except for
|
||||
# test_ABC_helper(), which assures that abc.ABC is an instance of abc.ABCMeta.
|
||||
|
||||
"""Unit tests for abc.py."""
|
||||
|
||||
import unittest
|
||||
|
||||
import abc
|
||||
import _py_abc
|
||||
from inspect import isabstract
|
||||
|
||||
def test_factory(abc_ABCMeta, abc_get_cache_token):
|
||||
class TestLegacyAPI(unittest.TestCase):
|
||||
|
||||
class TestLegacyAPI(unittest.TestCase):
|
||||
|
||||
def test_abstractproperty_basics(self):
|
||||
@abc.abstractproperty
|
||||
def foo(self): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
def bar(self): pass
|
||||
self.assertFalse(hasattr(bar, "__isabstractmethod__"))
|
||||
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
def test_abstractproperty_basics(self):
|
||||
@abc.abstractproperty
|
||||
def foo(self): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@property
|
||||
def foo(self): return super().foo
|
||||
self.assertEqual(D().foo, 3)
|
||||
self.assertFalse(getattr(D.foo, "__isabstractmethod__", False))
|
||||
def foo(self): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
def bar(self): pass
|
||||
self.assertFalse(hasattr(bar, "__isabstractmethod__"))
|
||||
|
||||
def test_abstractclassmethod_basics(self):
|
||||
@abc.abstractclassmethod
|
||||
def foo(cls): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@classmethod
|
||||
def bar(cls): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@abc.abstractclassmethod
|
||||
def foo(cls): return cls.__name__
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@classmethod
|
||||
def foo(cls): return super().foo()
|
||||
self.assertEqual(D.foo(), 'D')
|
||||
self.assertEqual(D().foo(), 'D')
|
||||
|
||||
def test_abstractstaticmethod_basics(self):
|
||||
@abc.abstractstaticmethod
|
||||
def foo(): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@staticmethod
|
||||
def bar(): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@abc.abstractstaticmethod
|
||||
def foo(): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@staticmethod
|
||||
def foo(): return 4
|
||||
self.assertEqual(D.foo(), 4)
|
||||
self.assertEqual(D().foo(), 4)
|
||||
|
||||
|
||||
class TestABC(unittest.TestCase):
|
||||
|
||||
def test_ABC_helper(self):
|
||||
# create an ABC using the helper class and perform basic checks
|
||||
class C(abc.ABC):
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def foo(cls): return cls.__name__
|
||||
self.assertEqual(type(C), abc.ABCMeta)
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@classmethod
|
||||
def foo(cls): return super().foo()
|
||||
self.assertEqual(D.foo(), 'D')
|
||||
|
||||
def test_abstractmethod_basics(self):
|
||||
@abc.abstractmethod
|
||||
def foo(self): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
def bar(self): pass
|
||||
self.assertFalse(hasattr(bar, "__isabstractmethod__"))
|
||||
|
||||
def test_abstractproperty_basics(self):
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def foo(self): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
def bar(self): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def foo(self): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@C.foo.getter
|
||||
def foo(self): return super().foo
|
||||
self.assertEqual(D().foo, 3)
|
||||
|
||||
def test_abstractclassmethod_basics(self):
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def foo(cls): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@classmethod
|
||||
def bar(cls): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def foo(cls): return cls.__name__
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@classmethod
|
||||
def foo(cls): return super().foo()
|
||||
self.assertEqual(D.foo(), 'D')
|
||||
self.assertEqual(D().foo(), 'D')
|
||||
|
||||
def test_abstractstaticmethod_basics(self):
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def foo(): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@staticmethod
|
||||
def bar(): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def foo(): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@staticmethod
|
||||
def foo(): return 4
|
||||
self.assertEqual(D.foo(), 4)
|
||||
self.assertEqual(D().foo(), 4)
|
||||
|
||||
def test_abstractmethod_integration(self):
|
||||
for abstractthing in [abc.abstractmethod, abc.abstractproperty,
|
||||
abc.abstractclassmethod,
|
||||
abc.abstractstaticmethod]:
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@abstractthing
|
||||
def foo(self): pass # abstract
|
||||
def bar(self): pass # concrete
|
||||
self.assertEqual(C.__abstractmethods__, {"foo"})
|
||||
self.assertRaises(TypeError, C) # because foo is abstract
|
||||
self.assertTrue(isabstract(C))
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@abc.abstractproperty
|
||||
def foo(self): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
def bar(self): pass # concrete override of concrete
|
||||
self.assertEqual(D.__abstractmethods__, {"foo"})
|
||||
self.assertRaises(TypeError, D) # because foo is still abstract
|
||||
self.assertTrue(isabstract(D))
|
||||
@property
|
||||
def foo(self): return super().foo
|
||||
self.assertEqual(D().foo, 3)
|
||||
self.assertFalse(getattr(D.foo, "__isabstractmethod__", False))
|
||||
|
||||
def test_abstractclassmethod_basics(self):
|
||||
@abc.abstractclassmethod
|
||||
def foo(cls): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@classmethod
|
||||
def bar(cls): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@abc.abstractclassmethod
|
||||
def foo(cls): return cls.__name__
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@classmethod
|
||||
def foo(cls): return super().foo()
|
||||
self.assertEqual(D.foo(), 'D')
|
||||
self.assertEqual(D().foo(), 'D')
|
||||
|
||||
def test_abstractstaticmethod_basics(self):
|
||||
@abc.abstractstaticmethod
|
||||
def foo(): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@staticmethod
|
||||
def bar(): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@abc.abstractstaticmethod
|
||||
def foo(): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@staticmethod
|
||||
def foo(): return 4
|
||||
self.assertEqual(D.foo(), 4)
|
||||
self.assertEqual(D().foo(), 4)
|
||||
|
||||
|
||||
class TestABC(unittest.TestCase):
|
||||
|
||||
def test_ABC_helper(self):
|
||||
# create an ABC using the helper class and perform basic checks
|
||||
class C(abc.ABC):
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def foo(cls): return cls.__name__
|
||||
self.assertEqual(type(C), abc.ABCMeta)
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@classmethod
|
||||
def foo(cls): return super().foo()
|
||||
self.assertEqual(D.foo(), 'D')
|
||||
|
||||
def test_abstractmethod_basics(self):
|
||||
@abc.abstractmethod
|
||||
def foo(self): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
def bar(self): pass
|
||||
self.assertFalse(hasattr(bar, "__isabstractmethod__"))
|
||||
|
||||
def test_abstractproperty_basics(self):
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def foo(self): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
def bar(self): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def foo(self): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@C.foo.getter
|
||||
def foo(self): return super().foo
|
||||
self.assertEqual(D().foo, 3)
|
||||
|
||||
def test_abstractclassmethod_basics(self):
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def foo(cls): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@classmethod
|
||||
def bar(cls): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def foo(cls): return cls.__name__
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@classmethod
|
||||
def foo(cls): return super().foo()
|
||||
self.assertEqual(D.foo(), 'D')
|
||||
self.assertEqual(D().foo(), 'D')
|
||||
|
||||
def test_abstractstaticmethod_basics(self):
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def foo(): pass
|
||||
self.assertTrue(foo.__isabstractmethod__)
|
||||
@staticmethod
|
||||
def bar(): pass
|
||||
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
|
||||
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def foo(): return 3
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@staticmethod
|
||||
def foo(): return 4
|
||||
self.assertEqual(D.foo(), 4)
|
||||
self.assertEqual(D().foo(), 4)
|
||||
|
||||
def test_abstractmethod_integration(self):
|
||||
for abstractthing in [abc.abstractmethod, abc.abstractproperty,
|
||||
abc.abstractclassmethod,
|
||||
abc.abstractstaticmethod]:
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@abstractthing
|
||||
def foo(self): pass # abstract
|
||||
def bar(self): pass # concrete
|
||||
self.assertEqual(C.__abstractmethods__, {"foo"})
|
||||
self.assertRaises(TypeError, C) # because foo is abstract
|
||||
self.assertTrue(isabstract(C))
|
||||
class D(C):
|
||||
def bar(self): pass # concrete override of concrete
|
||||
self.assertEqual(D.__abstractmethods__, {"foo"})
|
||||
self.assertRaises(TypeError, D) # because foo is still abstract
|
||||
self.assertTrue(isabstract(D))
|
||||
class E(D):
|
||||
def foo(self): pass
|
||||
self.assertEqual(E.__abstractmethods__, set())
|
||||
E() # now foo is concrete, too
|
||||
self.assertFalse(isabstract(E))
|
||||
class F(E):
|
||||
@abstractthing
|
||||
def bar(self): pass # abstract override of concrete
|
||||
self.assertEqual(F.__abstractmethods__, {"bar"})
|
||||
self.assertRaises(TypeError, F) # because bar is abstract now
|
||||
self.assertTrue(isabstract(F))
|
||||
|
||||
def test_descriptors_with_abstractmethod(self):
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def foo(self): return 3
|
||||
@foo.setter
|
||||
@abc.abstractmethod
|
||||
def foo(self, val): pass
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@C.foo.getter
|
||||
def foo(self): return super().foo
|
||||
self.assertRaises(TypeError, D)
|
||||
class E(D):
|
||||
def foo(self): pass
|
||||
self.assertEqual(E.__abstractmethods__, set())
|
||||
E() # now foo is concrete, too
|
||||
self.assertFalse(isabstract(E))
|
||||
class F(E):
|
||||
@abstractthing
|
||||
def bar(self): pass # abstract override of concrete
|
||||
self.assertEqual(F.__abstractmethods__, {"bar"})
|
||||
self.assertRaises(TypeError, F) # because bar is abstract now
|
||||
self.assertTrue(isabstract(F))
|
||||
@D.foo.setter
|
||||
def foo(self, val): pass
|
||||
self.assertEqual(E().foo, 3)
|
||||
# check that the property's __isabstractmethod__ descriptor does the
|
||||
# right thing when presented with a value that fails truth testing:
|
||||
class NotBool(object):
|
||||
def __bool__(self):
|
||||
raise ValueError()
|
||||
__len__ = __bool__
|
||||
with self.assertRaises(ValueError):
|
||||
class F(C):
|
||||
def bar(self):
|
||||
pass
|
||||
bar.__isabstractmethod__ = NotBool()
|
||||
foo = property(bar)
|
||||
|
||||
def test_descriptors_with_abstractmethod(self):
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def foo(self): return 3
|
||||
@foo.setter
|
||||
@abc.abstractmethod
|
||||
def foo(self, val): pass
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@C.foo.getter
|
||||
def foo(self): return super().foo
|
||||
self.assertRaises(TypeError, D)
|
||||
class E(D):
|
||||
@D.foo.setter
|
||||
def foo(self, val): pass
|
||||
self.assertEqual(E().foo, 3)
|
||||
# check that the property's __isabstractmethod__ descriptor does the
|
||||
# right thing when presented with a value that fails truth testing:
|
||||
class NotBool(object):
|
||||
def __bool__(self):
|
||||
raise ValueError()
|
||||
__len__ = __bool__
|
||||
with self.assertRaises(ValueError):
|
||||
class F(C):
|
||||
def bar(self):
|
||||
|
||||
def test_customdescriptors_with_abstractmethod(self):
|
||||
class Descriptor:
|
||||
def __init__(self, fget, fset=None):
|
||||
self._fget = fget
|
||||
self._fset = fset
|
||||
def getter(self, callable):
|
||||
return Descriptor(callable, self._fget)
|
||||
def setter(self, callable):
|
||||
return Descriptor(self._fget, callable)
|
||||
@property
|
||||
def __isabstractmethod__(self):
|
||||
return (getattr(self._fget, '__isabstractmethod__', False)
|
||||
or getattr(self._fset, '__isabstractmethod__', False))
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
@Descriptor
|
||||
@abc.abstractmethod
|
||||
def foo(self): return 3
|
||||
@foo.setter
|
||||
@abc.abstractmethod
|
||||
def foo(self, val): pass
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@C.foo.getter
|
||||
def foo(self): return super().foo
|
||||
self.assertRaises(TypeError, D)
|
||||
class E(D):
|
||||
@D.foo.setter
|
||||
def foo(self, val): pass
|
||||
self.assertFalse(E.foo.__isabstractmethod__)
|
||||
|
||||
def test_metaclass_abc(self):
|
||||
# Metaclasses can be ABCs, too.
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def x(self):
|
||||
pass
|
||||
bar.__isabstractmethod__ = NotBool()
|
||||
foo = property(bar)
|
||||
|
||||
|
||||
def test_customdescriptors_with_abstractmethod(self):
|
||||
class Descriptor:
|
||||
def __init__(self, fget, fset=None):
|
||||
self._fget = fget
|
||||
self._fset = fset
|
||||
def getter(self, callable):
|
||||
return Descriptor(callable, self._fget)
|
||||
def setter(self, callable):
|
||||
return Descriptor(self._fget, callable)
|
||||
@property
|
||||
def __isabstractmethod__(self):
|
||||
return (getattr(self._fget, '__isabstractmethod__', False)
|
||||
or getattr(self._fset, '__isabstractmethod__', False))
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
@Descriptor
|
||||
@abc.abstractmethod
|
||||
def foo(self): return 3
|
||||
@foo.setter
|
||||
@abc.abstractmethod
|
||||
def foo(self, val): pass
|
||||
self.assertRaises(TypeError, C)
|
||||
class D(C):
|
||||
@C.foo.getter
|
||||
def foo(self): return super().foo
|
||||
self.assertRaises(TypeError, D)
|
||||
class E(D):
|
||||
@D.foo.setter
|
||||
def foo(self, val): pass
|
||||
self.assertFalse(E.foo.__isabstractmethod__)
|
||||
|
||||
def test_metaclass_abc(self):
|
||||
# Metaclasses can be ABCs, too.
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def x(self):
|
||||
self.assertEqual(A.__abstractmethods__, {"x"})
|
||||
class meta(type, A):
|
||||
def x(self):
|
||||
return 1
|
||||
class C(metaclass=meta):
|
||||
pass
|
||||
self.assertEqual(A.__abstractmethods__, {"x"})
|
||||
class meta(type, A):
|
||||
def x(self):
|
||||
return 1
|
||||
class C(metaclass=meta):
|
||||
pass
|
||||
|
||||
def test_registration_basics(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
class B(object):
|
||||
pass
|
||||
b = B()
|
||||
self.assertFalse(issubclass(B, A))
|
||||
self.assertFalse(issubclass(B, (A,)))
|
||||
self.assertNotIsInstance(b, A)
|
||||
self.assertNotIsInstance(b, (A,))
|
||||
B1 = A.register(B)
|
||||
self.assertTrue(issubclass(B, A))
|
||||
self.assertTrue(issubclass(B, (A,)))
|
||||
self.assertIsInstance(b, A)
|
||||
self.assertIsInstance(b, (A,))
|
||||
self.assertIs(B1, B)
|
||||
class C(B):
|
||||
pass
|
||||
c = C()
|
||||
self.assertTrue(issubclass(C, A))
|
||||
self.assertTrue(issubclass(C, (A,)))
|
||||
self.assertIsInstance(c, A)
|
||||
self.assertIsInstance(c, (A,))
|
||||
def test_registration_basics(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
class B(object):
|
||||
pass
|
||||
b = B()
|
||||
self.assertFalse(issubclass(B, A))
|
||||
self.assertFalse(issubclass(B, (A,)))
|
||||
self.assertNotIsInstance(b, A)
|
||||
self.assertNotIsInstance(b, (A,))
|
||||
B1 = A.register(B)
|
||||
self.assertTrue(issubclass(B, A))
|
||||
self.assertTrue(issubclass(B, (A,)))
|
||||
self.assertIsInstance(b, A)
|
||||
self.assertIsInstance(b, (A,))
|
||||
self.assertIs(B1, B)
|
||||
class C(B):
|
||||
pass
|
||||
c = C()
|
||||
self.assertTrue(issubclass(C, A))
|
||||
self.assertTrue(issubclass(C, (A,)))
|
||||
self.assertIsInstance(c, A)
|
||||
self.assertIsInstance(c, (A,))
|
||||
|
||||
def test_register_as_class_deco(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
@A.register
|
||||
class B(object):
|
||||
pass
|
||||
b = B()
|
||||
self.assertTrue(issubclass(B, A))
|
||||
self.assertTrue(issubclass(B, (A,)))
|
||||
self.assertIsInstance(b, A)
|
||||
self.assertIsInstance(b, (A,))
|
||||
@A.register
|
||||
class C(B):
|
||||
pass
|
||||
c = C()
|
||||
self.assertTrue(issubclass(C, A))
|
||||
self.assertTrue(issubclass(C, (A,)))
|
||||
self.assertIsInstance(c, A)
|
||||
self.assertIsInstance(c, (A,))
|
||||
self.assertIs(C, A.register(C))
|
||||
def test_register_as_class_deco(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
@A.register
|
||||
class B(object):
|
||||
pass
|
||||
b = B()
|
||||
self.assertTrue(issubclass(B, A))
|
||||
self.assertTrue(issubclass(B, (A,)))
|
||||
self.assertIsInstance(b, A)
|
||||
self.assertIsInstance(b, (A,))
|
||||
@A.register
|
||||
class C(B):
|
||||
pass
|
||||
c = C()
|
||||
self.assertTrue(issubclass(C, A))
|
||||
self.assertTrue(issubclass(C, (A,)))
|
||||
self.assertIsInstance(c, A)
|
||||
self.assertIsInstance(c, (A,))
|
||||
self.assertIs(C, A.register(C))
|
||||
|
||||
def test_isinstance_invalidation(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
class B:
|
||||
pass
|
||||
b = B()
|
||||
self.assertFalse(isinstance(b, A))
|
||||
self.assertFalse(isinstance(b, (A,)))
|
||||
token_old = abc.get_cache_token()
|
||||
A.register(B)
|
||||
token_new = abc.get_cache_token()
|
||||
self.assertNotEqual(token_old, token_new)
|
||||
self.assertTrue(isinstance(b, A))
|
||||
self.assertTrue(isinstance(b, (A,)))
|
||||
def test_isinstance_invalidation(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
class B:
|
||||
pass
|
||||
b = B()
|
||||
self.assertFalse(isinstance(b, A))
|
||||
self.assertFalse(isinstance(b, (A,)))
|
||||
token_old = abc_get_cache_token()
|
||||
A.register(B)
|
||||
token_new = abc_get_cache_token()
|
||||
self.assertNotEqual(token_old, token_new)
|
||||
self.assertTrue(isinstance(b, A))
|
||||
self.assertTrue(isinstance(b, (A,)))
|
||||
|
||||
def test_registration_builtins(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
A.register(int)
|
||||
self.assertIsInstance(42, A)
|
||||
self.assertIsInstance(42, (A,))
|
||||
self.assertTrue(issubclass(int, A))
|
||||
self.assertTrue(issubclass(int, (A,)))
|
||||
class B(A):
|
||||
pass
|
||||
B.register(str)
|
||||
class C(str): pass
|
||||
self.assertIsInstance("", A)
|
||||
self.assertIsInstance("", (A,))
|
||||
self.assertTrue(issubclass(str, A))
|
||||
self.assertTrue(issubclass(str, (A,)))
|
||||
self.assertTrue(issubclass(C, A))
|
||||
self.assertTrue(issubclass(C, (A,)))
|
||||
def test_registration_builtins(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
A.register(int)
|
||||
self.assertIsInstance(42, A)
|
||||
self.assertIsInstance(42, (A,))
|
||||
self.assertTrue(issubclass(int, A))
|
||||
self.assertTrue(issubclass(int, (A,)))
|
||||
class B(A):
|
||||
pass
|
||||
B.register(str)
|
||||
class C(str): pass
|
||||
self.assertIsInstance("", A)
|
||||
self.assertIsInstance("", (A,))
|
||||
self.assertTrue(issubclass(str, A))
|
||||
self.assertTrue(issubclass(str, (A,)))
|
||||
self.assertTrue(issubclass(C, A))
|
||||
self.assertTrue(issubclass(C, (A,)))
|
||||
|
||||
def test_registration_edge_cases(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
A.register(A) # should pass silently
|
||||
class A1(A):
|
||||
pass
|
||||
self.assertRaises(RuntimeError, A1.register, A) # cycles not allowed
|
||||
class B(object):
|
||||
pass
|
||||
A1.register(B) # ok
|
||||
A1.register(B) # should pass silently
|
||||
class C(A):
|
||||
pass
|
||||
A.register(C) # should pass silently
|
||||
self.assertRaises(RuntimeError, C.register, A) # cycles not allowed
|
||||
C.register(B) # ok
|
||||
def test_registration_edge_cases(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
A.register(A) # should pass silently
|
||||
class A1(A):
|
||||
pass
|
||||
self.assertRaises(RuntimeError, A1.register, A) # cycles not allowed
|
||||
class B(object):
|
||||
pass
|
||||
A1.register(B) # ok
|
||||
A1.register(B) # should pass silently
|
||||
class C(A):
|
||||
pass
|
||||
A.register(C) # should pass silently
|
||||
self.assertRaises(RuntimeError, C.register, A) # cycles not allowed
|
||||
C.register(B) # ok
|
||||
|
||||
def test_register_non_class(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
self.assertRaisesRegex(TypeError, "Can only register classes",
|
||||
A.register, 4)
|
||||
def test_register_non_class(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
self.assertRaisesRegex(TypeError, "Can only register classes",
|
||||
A.register, 4)
|
||||
|
||||
def test_registration_transitiveness(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
self.assertTrue(issubclass(A, A))
|
||||
self.assertTrue(issubclass(A, (A,)))
|
||||
class B(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
self.assertFalse(issubclass(A, B))
|
||||
self.assertFalse(issubclass(A, (B,)))
|
||||
self.assertFalse(issubclass(B, A))
|
||||
self.assertFalse(issubclass(B, (A,)))
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
A.register(B)
|
||||
class B1(B):
|
||||
pass
|
||||
self.assertTrue(issubclass(B1, A))
|
||||
self.assertTrue(issubclass(B1, (A,)))
|
||||
class C1(C):
|
||||
pass
|
||||
B1.register(C1)
|
||||
self.assertFalse(issubclass(C, B))
|
||||
self.assertFalse(issubclass(C, (B,)))
|
||||
self.assertFalse(issubclass(C, B1))
|
||||
self.assertFalse(issubclass(C, (B1,)))
|
||||
self.assertTrue(issubclass(C1, A))
|
||||
self.assertTrue(issubclass(C1, (A,)))
|
||||
self.assertTrue(issubclass(C1, B))
|
||||
self.assertTrue(issubclass(C1, (B,)))
|
||||
self.assertTrue(issubclass(C1, B1))
|
||||
self.assertTrue(issubclass(C1, (B1,)))
|
||||
C1.register(int)
|
||||
class MyInt(int):
|
||||
pass
|
||||
self.assertTrue(issubclass(MyInt, A))
|
||||
self.assertTrue(issubclass(MyInt, (A,)))
|
||||
self.assertIsInstance(42, A)
|
||||
self.assertIsInstance(42, (A,))
|
||||
def test_registration_transitiveness(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
self.assertTrue(issubclass(A, A))
|
||||
self.assertTrue(issubclass(A, (A,)))
|
||||
class B(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
self.assertFalse(issubclass(A, B))
|
||||
self.assertFalse(issubclass(A, (B,)))
|
||||
self.assertFalse(issubclass(B, A))
|
||||
self.assertFalse(issubclass(B, (A,)))
|
||||
class C(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
A.register(B)
|
||||
class B1(B):
|
||||
pass
|
||||
self.assertTrue(issubclass(B1, A))
|
||||
self.assertTrue(issubclass(B1, (A,)))
|
||||
class C1(C):
|
||||
pass
|
||||
B1.register(C1)
|
||||
self.assertFalse(issubclass(C, B))
|
||||
self.assertFalse(issubclass(C, (B,)))
|
||||
self.assertFalse(issubclass(C, B1))
|
||||
self.assertFalse(issubclass(C, (B1,)))
|
||||
self.assertTrue(issubclass(C1, A))
|
||||
self.assertTrue(issubclass(C1, (A,)))
|
||||
self.assertTrue(issubclass(C1, B))
|
||||
self.assertTrue(issubclass(C1, (B,)))
|
||||
self.assertTrue(issubclass(C1, B1))
|
||||
self.assertTrue(issubclass(C1, (B1,)))
|
||||
C1.register(int)
|
||||
class MyInt(int):
|
||||
pass
|
||||
self.assertTrue(issubclass(MyInt, A))
|
||||
self.assertTrue(issubclass(MyInt, (A,)))
|
||||
self.assertIsInstance(42, A)
|
||||
self.assertIsInstance(42, (A,))
|
||||
|
||||
def test_all_new_methods_are_called(self):
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
pass
|
||||
class B(object):
|
||||
counter = 0
|
||||
def __new__(cls):
|
||||
B.counter += 1
|
||||
return super().__new__(cls)
|
||||
class C(A, B):
|
||||
pass
|
||||
self.assertEqual(B.counter, 0)
|
||||
C()
|
||||
self.assertEqual(B.counter, 1)
|
||||
def test_all_new_methods_are_called(self):
|
||||
class A(metaclass=abc_ABCMeta):
|
||||
pass
|
||||
class B(object):
|
||||
counter = 0
|
||||
def __new__(cls):
|
||||
B.counter += 1
|
||||
return super().__new__(cls)
|
||||
class C(A, B):
|
||||
pass
|
||||
self.assertEqual(B.counter, 0)
|
||||
C()
|
||||
self.assertEqual(B.counter, 1)
|
||||
|
||||
def test_ABC_has___slots__(self):
|
||||
self.assertTrue(hasattr(abc.ABC, '__slots__'))
|
||||
def test_ABC_has___slots__(self):
|
||||
self.assertTrue(hasattr(abc.ABC, '__slots__'))
|
||||
|
||||
def test_tricky_new_works(self):
|
||||
def with_metaclass(meta, *bases):
|
||||
class metaclass(type):
|
||||
def __new__(cls, name, this_bases, d):
|
||||
return meta(name, bases, d)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
class A: ...
|
||||
class B: ...
|
||||
class C(with_metaclass(abc_ABCMeta, A, B)):
|
||||
pass
|
||||
self.assertEqual(C.__class__, abc_ABCMeta)
|
||||
|
||||
|
||||
class TestABCWithInitSubclass(unittest.TestCase):
|
||||
def test_works_with_init_subclass(self):
|
||||
saved_kwargs = {}
|
||||
class ReceivesClassKwargs:
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__()
|
||||
saved_kwargs.update(kwargs)
|
||||
class Receiver(ReceivesClassKwargs, abc.ABC, x=1, y=2, z=3):
|
||||
pass
|
||||
self.assertEqual(saved_kwargs, dict(x=1, y=2, z=3))
|
||||
class TestABCWithInitSubclass(unittest.TestCase):
|
||||
def test_works_with_init_subclass(self):
|
||||
class abc_ABC(metaclass=abc_ABCMeta):
|
||||
__slots__ = ()
|
||||
saved_kwargs = {}
|
||||
class ReceivesClassKwargs:
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__()
|
||||
saved_kwargs.update(kwargs)
|
||||
class Receiver(ReceivesClassKwargs, abc_ABC, x=1, y=2, z=3):
|
||||
pass
|
||||
self.assertEqual(saved_kwargs, dict(x=1, y=2, z=3))
|
||||
return TestLegacyAPI, TestABC, TestABCWithInitSubclass
|
||||
|
||||
TestLegacyAPI_Py, TestABC_Py, TestABCWithInitSubclass_Py = test_factory(abc.ABCMeta,
|
||||
abc.get_cache_token)
|
||||
TestLegacyAPI_C, TestABC_C, TestABCWithInitSubclass_C = test_factory(_py_abc.ABCMeta,
|
||||
_py_abc.get_cache_token)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -761,8 +761,8 @@ class GenericTests(BaseTestCase):
|
|||
self.assertIsInstance(1, C)
|
||||
C[int]
|
||||
self.assertIsInstance(1, C)
|
||||
C._abc_registry.clear()
|
||||
C._abc_cache.clear() # To keep refleak hunting mode clean
|
||||
C._abc_registry_clear()
|
||||
C._abc_caches_clear() # To keep refleak hunting mode clean
|
||||
|
||||
def test_false_subclasses(self):
|
||||
class MyMapping(MutableMapping[str, str]): pass
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
``_abc`` module is added. It is a speedup module with C implementations for
|
||||
various functions and methods in ``abc``. Creating an ABC subclass and calling
|
||||
``isinstance`` or ``issubclass`` with an ABC subclass are up to 1.5x faster.
|
||||
In addition, this makes Python start-up up to 10% faster.
|
||||
|
||||
Note that the new implementation hides internal registry and caches, previously
|
||||
accessible via private attributes ``_abc_registry``, ``_abc_cache``, and
|
||||
``_abc_negative_cache``. There are three debugging helper methods that can be
|
||||
used instead ``_dump_registry``, ``_abc_registry_clear``, and
|
||||
``_abc_caches_clear``.
|
|
@ -114,6 +114,7 @@ _weakref _weakref.c # weak references
|
|||
_functools _functoolsmodule.c # Tools for working with functions and callable objects
|
||||
_operator _operator.c # operator.add() and similar goodies
|
||||
_collections _collectionsmodule.c # Container types
|
||||
_abc _abc.c # Abstract base classes
|
||||
itertools itertoolsmodule.c # Functions creating iterators for efficient looping
|
||||
atexit atexitmodule.c # Register functions to be run at interpreter-shutdown
|
||||
_signal signalmodule.c
|
||||
|
|
|
@ -0,0 +1,822 @@
|
|||
/* ABCMeta implementation */
|
||||
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "clinic/_abc.c.h"
|
||||
|
||||
/*[clinic input]
|
||||
module _abc
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=964f5328e1aefcda]*/
|
||||
|
||||
PyDoc_STRVAR(_abc__doc__,
|
||||
"Module contains faster C implementation of abc.ABCMeta");
|
||||
|
||||
_Py_IDENTIFIER(__abstractmethods__);
|
||||
_Py_IDENTIFIER(__class__);
|
||||
_Py_IDENTIFIER(__dict__);
|
||||
_Py_IDENTIFIER(__bases__);
|
||||
_Py_IDENTIFIER(_abc_impl);
|
||||
_Py_IDENTIFIER(__subclasscheck__);
|
||||
_Py_IDENTIFIER(__subclasshook__);
|
||||
|
||||
/* 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. */
|
||||
static unsigned long long abc_invalidation_counter = 0;
|
||||
|
||||
/* This object stores internal state for ABCs.
|
||||
Note that we can use normal sets for caches,
|
||||
since they are never iterated over. */
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *_abc_registry;
|
||||
PyObject *_abc_cache; /* Normal set of weak references. */
|
||||
PyObject *_abc_negative_cache; /* Normal set of weak references. */
|
||||
unsigned long long _abc_negative_cache_version;
|
||||
} _abc_data;
|
||||
|
||||
static void
|
||||
abc_data_dealloc(_abc_data *self)
|
||||
{
|
||||
Py_XDECREF(self->_abc_registry);
|
||||
Py_XDECREF(self->_abc_cache);
|
||||
Py_XDECREF(self->_abc_negative_cache);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
abc_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
_abc_data *self = (_abc_data *) type->tp_alloc(type, 0);
|
||||
if (self == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->_abc_registry = NULL;
|
||||
self->_abc_cache = NULL;
|
||||
self->_abc_negative_cache = NULL;
|
||||
self->_abc_negative_cache_version = abc_invalidation_counter;
|
||||
return (PyObject *) self;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(abc_data_doc,
|
||||
"Internal state held by ABC machinery.");
|
||||
|
||||
static PyTypeObject _abc_data_type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
"_abc_data", /*tp_name*/
|
||||
sizeof(_abc_data), /*tp_size*/
|
||||
.tp_dealloc = (destructor)abc_data_dealloc,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_alloc = PyType_GenericAlloc,
|
||||
.tp_new = abc_data_new,
|
||||
};
|
||||
|
||||
static _abc_data *
|
||||
_get_impl(PyObject *self)
|
||||
{
|
||||
PyObject *impl = _PyObject_GetAttrId(self, &PyId__abc_impl);
|
||||
if (impl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (Py_TYPE(impl) != &_abc_data_type) {
|
||||
PyErr_SetString(PyExc_TypeError, "_abc_impl is set to a wrong type");
|
||||
Py_DECREF(impl);
|
||||
return NULL;
|
||||
}
|
||||
return (_abc_data *)impl;
|
||||
}
|
||||
|
||||
static int
|
||||
_in_weak_set(PyObject *set, PyObject *obj)
|
||||
{
|
||||
if (set == NULL || PySet_GET_SIZE(set) == 0) {
|
||||
return 0;
|
||||
}
|
||||
PyObject *ref = PyWeakref_NewRef(obj, NULL);
|
||||
if (ref == NULL) {
|
||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int res = PySet_Contains(set, ref);
|
||||
Py_DECREF(ref);
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_destroy(PyObject *setweakref, PyObject *objweakref)
|
||||
{
|
||||
PyObject *set;
|
||||
set = PyWeakref_GET_OBJECT(setweakref);
|
||||
if (set == Py_None) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
Py_INCREF(set);
|
||||
if (PySet_Discard(set, objweakref) < 0) {
|
||||
Py_DECREF(set);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(set);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef _destroy_def = {
|
||||
"_destroy", (PyCFunction) _destroy, METH_O
|
||||
};
|
||||
|
||||
static int
|
||||
_add_to_weak_set(PyObject **pset, PyObject *obj)
|
||||
{
|
||||
if (*pset == NULL) {
|
||||
*pset = PySet_New(NULL);
|
||||
if (*pset == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *set = *pset;
|
||||
PyObject *ref, *wr;
|
||||
PyObject *destroy_cb;
|
||||
wr = PyWeakref_NewRef(set, NULL);
|
||||
if (wr == NULL) {
|
||||
return -1;
|
||||
}
|
||||
destroy_cb = PyCFunction_NewEx(&_destroy_def, wr, NULL);
|
||||
if (destroy_cb == NULL) {
|
||||
Py_DECREF(wr);
|
||||
return -1;
|
||||
}
|
||||
ref = PyWeakref_NewRef(obj, destroy_cb);
|
||||
Py_DECREF(destroy_cb);
|
||||
if (ref == NULL) {
|
||||
Py_DECREF(wr);
|
||||
return -1;
|
||||
}
|
||||
int ret = PySet_Add(set, ref);
|
||||
Py_DECREF(wr);
|
||||
Py_DECREF(ref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_abc._reset_registry
|
||||
|
||||
self: object
|
||||
/
|
||||
|
||||
Internal ABC helper to reset registry of a given class.
|
||||
|
||||
Should be only used by refleak.py
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc__reset_registry(PyObject *module, PyObject *self)
|
||||
/*[clinic end generated code: output=92d591a43566cc10 input=12a0b7eb339ac35c]*/
|
||||
{
|
||||
_abc_data *impl = _get_impl(self);
|
||||
if (impl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (impl->_abc_registry != NULL && PySet_Clear(impl->_abc_registry) < 0) {
|
||||
Py_DECREF(impl);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(impl);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_abc._reset_caches
|
||||
|
||||
self: object
|
||||
/
|
||||
|
||||
Internal ABC helper to reset both caches of a given class.
|
||||
|
||||
Should be only used by refleak.py
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc__reset_caches(PyObject *module, PyObject *self)
|
||||
/*[clinic end generated code: output=f296f0d5c513f80c input=c0ac616fd8acfb6f]*/
|
||||
{
|
||||
_abc_data *impl = _get_impl(self);
|
||||
if (impl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (impl->_abc_cache != NULL && PySet_Clear(impl->_abc_cache) < 0) {
|
||||
Py_DECREF(impl);
|
||||
return NULL;
|
||||
}
|
||||
/* also the second cache */
|
||||
if (impl->_abc_negative_cache != NULL &&
|
||||
PySet_Clear(impl->_abc_negative_cache) < 0) {
|
||||
Py_DECREF(impl);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(impl);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_abc._get_dump
|
||||
|
||||
self: object
|
||||
/
|
||||
|
||||
Internal ABC helper for cache and registry debugging.
|
||||
|
||||
Return shallow copies of registry, of both caches, and
|
||||
negative cache version. Don't call this function directly,
|
||||
instead use ABC._dump_registry() for a nice repr.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc__get_dump(PyObject *module, PyObject *self)
|
||||
/*[clinic end generated code: output=9d9569a8e2c1c443 input=2c5deb1bfe9e3c79]*/
|
||||
{
|
||||
_abc_data *impl = _get_impl(self);
|
||||
if (impl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *res = Py_BuildValue("NNNK",
|
||||
PySet_New(impl->_abc_registry),
|
||||
PySet_New(impl->_abc_cache),
|
||||
PySet_New(impl->_abc_negative_cache),
|
||||
impl->_abc_negative_cache_version);
|
||||
Py_DECREF(impl);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Compute set of abstract method names.
|
||||
static int
|
||||
compute_abstract_methods(PyObject *self)
|
||||
{
|
||||
int ret = -1;
|
||||
PyObject *abstracts = PyFrozenSet_New(NULL);
|
||||
if (abstracts == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *ns = NULL, *items = NULL, *bases = NULL; // Py_XDECREF()ed on error.
|
||||
|
||||
/* Stage 1: direct abstract methods. */
|
||||
ns = _PyObject_GetAttrId(self, &PyId___dict__);
|
||||
if (!ns) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// We can't use PyDict_Next(ns) even when ns is dict because
|
||||
// _PyObject_IsAbstract() can mutate ns.
|
||||
items = PyMapping_Items(ns);
|
||||
if (!items) {
|
||||
goto error;
|
||||
}
|
||||
assert(PyList_Check(items));
|
||||
for (Py_ssize_t pos = 0; pos < PyList_GET_SIZE(items); pos++) {
|
||||
PyObject *it = PySequence_Fast(
|
||||
PyList_GET_ITEM(items, pos),
|
||||
"items() returned non-iterable");
|
||||
if (!it) {
|
||||
goto error;
|
||||
}
|
||||
if (PySequence_Fast_GET_SIZE(it) != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"items() returned item which size is not 2");
|
||||
Py_DECREF(it);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// borrowed
|
||||
PyObject *key = PySequence_Fast_GET_ITEM(it, 0);
|
||||
PyObject *value = PySequence_Fast_GET_ITEM(it, 1);
|
||||
// items or it may be cleared while accessing __abstractmethod__
|
||||
// So we need to keep strong reference for key
|
||||
Py_INCREF(key);
|
||||
int is_abstract = _PyObject_IsAbstract(value);
|
||||
if (is_abstract < 0 ||
|
||||
(is_abstract && PySet_Add(abstracts, key) < 0)) {
|
||||
Py_DECREF(it);
|
||||
Py_DECREF(key);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(it);
|
||||
}
|
||||
|
||||
/* Stage 2: inherited abstract methods. */
|
||||
bases = _PyObject_GetAttrId(self, &PyId___bases__);
|
||||
if (!bases) {
|
||||
goto error;
|
||||
}
|
||||
if (!PyTuple_Check(bases)) {
|
||||
PyErr_SetString(PyExc_TypeError, "__bases__ is not tuple");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (Py_ssize_t pos = 0; pos < PyTuple_GET_SIZE(bases); pos++) {
|
||||
PyObject *item = PyTuple_GET_ITEM(bases, pos); // borrowed
|
||||
PyObject *base_abstracts, *iter;
|
||||
|
||||
if (_PyObject_LookupAttrId(item, &PyId___abstractmethods__,
|
||||
&base_abstracts) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (base_abstracts == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (!(iter = PyObject_GetIter(base_abstracts))) {
|
||||
Py_DECREF(base_abstracts);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(base_abstracts);
|
||||
PyObject *key, *value;
|
||||
while ((key = PyIter_Next(iter))) {
|
||||
if (_PyObject_LookupAttr(self, key, &value) < 0) {
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(iter);
|
||||
goto error;
|
||||
}
|
||||
if (value == NULL) {
|
||||
Py_DECREF(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
int is_abstract = _PyObject_IsAbstract(value);
|
||||
Py_DECREF(value);
|
||||
if (is_abstract < 0 ||
|
||||
(is_abstract && PySet_Add(abstracts, key) < 0))
|
||||
{
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(iter);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(key);
|
||||
}
|
||||
Py_DECREF(iter);
|
||||
if (PyErr_Occurred()) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (_PyObject_SetAttrId(self, &PyId___abstractmethods__, abstracts) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
Py_DECREF(abstracts);
|
||||
Py_XDECREF(ns);
|
||||
Py_XDECREF(items);
|
||||
Py_XDECREF(bases);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_abc._abc_init
|
||||
|
||||
self: object
|
||||
/
|
||||
|
||||
Internal ABC helper for class set-up. Should be never used outside abc module.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_init(PyObject *module, PyObject *self)
|
||||
/*[clinic end generated code: output=594757375714cda1 input=8d7fe470ff77f029]*/
|
||||
{
|
||||
PyObject *data;
|
||||
if (compute_abstract_methods(self) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set up inheritance registry. */
|
||||
data = abc_data_new(&_abc_data_type, NULL, NULL);
|
||||
if (data == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyObject_SetAttrId(self, &PyId__abc_impl, data) < 0) {
|
||||
Py_DECREF(data);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(data);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_abc._abc_register
|
||||
|
||||
self: object
|
||||
subclass: object
|
||||
/
|
||||
|
||||
Internal ABC helper for subclasss registration. Should be never used outside abc module.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass)
|
||||
/*[clinic end generated code: output=7851e7668c963524 input=ca589f8c3080e67f]*/
|
||||
{
|
||||
if (!PyType_Check(subclass)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Can only register classes");
|
||||
return NULL;
|
||||
}
|
||||
int result = PyObject_IsSubclass(subclass, self);
|
||||
if (result > 0) {
|
||||
Py_INCREF(subclass);
|
||||
return subclass; /* Already a subclass. */
|
||||
}
|
||||
if (result < 0) {
|
||||
return NULL;
|
||||
}
|
||||
/* Subtle: test for cycles *after* testing for "already a subclass";
|
||||
this means we allow X.register(X) and interpret it as a no-op. */
|
||||
result = PyObject_IsSubclass(self, subclass);
|
||||
if (result > 0) {
|
||||
/* This would create a cycle, which is bad for the algorithm below. */
|
||||
PyErr_SetString(PyExc_RuntimeError, "Refusing to create an inheritance cycle");
|
||||
return NULL;
|
||||
}
|
||||
if (result < 0) {
|
||||
return NULL;
|
||||
}
|
||||
_abc_data *impl = _get_impl(self);
|
||||
if (impl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (_add_to_weak_set(&impl->_abc_registry, subclass) < 0) {
|
||||
Py_DECREF(impl);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(impl);
|
||||
|
||||
/* Invalidate negative cache */
|
||||
abc_invalidation_counter++;
|
||||
|
||||
Py_INCREF(subclass);
|
||||
return subclass;
|
||||
}
|
||||
|
||||
|
||||
/*[clinic input]
|
||||
_abc._abc_instancecheck
|
||||
|
||||
self: object
|
||||
instance: object
|
||||
/
|
||||
|
||||
Internal ABC helper for instance checks. Should be never used outside abc module.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
|
||||
PyObject *instance)
|
||||
/*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/
|
||||
{
|
||||
PyObject *subtype, *result = NULL, *subclass = NULL;
|
||||
_abc_data *impl = _get_impl(self);
|
||||
if (impl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
subclass = _PyObject_GetAttrId(instance, &PyId___class__);
|
||||
if (subclass == NULL) {
|
||||
Py_DECREF(impl);
|
||||
return NULL;
|
||||
}
|
||||
/* Inline the cache checking. */
|
||||
int incache = _in_weak_set(impl->_abc_cache, subclass);
|
||||
if (incache < 0) {
|
||||
goto end;
|
||||
}
|
||||
if (incache > 0) {
|
||||
result = Py_True;
|
||||
Py_INCREF(result);
|
||||
goto end;
|
||||
}
|
||||
subtype = (PyObject *)Py_TYPE(instance);
|
||||
if (subtype == subclass) {
|
||||
if (impl->_abc_negative_cache_version == abc_invalidation_counter) {
|
||||
incache = _in_weak_set(impl->_abc_negative_cache, subclass);
|
||||
if (incache < 0) {
|
||||
goto end;
|
||||
}
|
||||
if (incache > 0) {
|
||||
result = Py_False;
|
||||
Py_INCREF(result);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
/* Fall back to the subclass check. */
|
||||
result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
|
||||
subclass, NULL);
|
||||
goto end;
|
||||
}
|
||||
result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
|
||||
subclass, NULL);
|
||||
if (result == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (PyObject_IsTrue(result)) {
|
||||
case -1:
|
||||
Py_DECREF(result);
|
||||
result = NULL;
|
||||
break;
|
||||
case 0:
|
||||
Py_DECREF(result);
|
||||
result = _PyObject_CallMethodIdObjArgs(self, &PyId___subclasscheck__,
|
||||
subtype, NULL);
|
||||
break;
|
||||
case 1: // Nothing to do.
|
||||
break;
|
||||
default:
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
|
||||
end:
|
||||
Py_XDECREF(impl);
|
||||
Py_XDECREF(subclass);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Return -1 when exception occured.
|
||||
// Return 1 when result is set.
|
||||
// Return 0 otherwise.
|
||||
static int subclasscheck_check_registry(_abc_data *impl, PyObject *subclass,
|
||||
PyObject **result);
|
||||
|
||||
/*[clinic input]
|
||||
_abc._abc_subclasscheck
|
||||
|
||||
self: object
|
||||
subclass: object
|
||||
/
|
||||
|
||||
Internal ABC helper for subclasss checks. Should be never used outside abc module.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
|
||||
PyObject *subclass)
|
||||
/*[clinic end generated code: output=b56c9e4a530e3894 input=1d947243409d10b8]*/
|
||||
{
|
||||
PyObject *ok, *mro, *subclasses = NULL, *result = NULL;
|
||||
Py_ssize_t pos;
|
||||
int incache;
|
||||
_abc_data *impl = _get_impl(self);
|
||||
if (impl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 1. Check cache. */
|
||||
incache = _in_weak_set(impl->_abc_cache, subclass);
|
||||
if (incache < 0) {
|
||||
goto end;
|
||||
}
|
||||
if (incache > 0) {
|
||||
result = Py_True;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* 2. Check negative cache; may have to invalidate. */
|
||||
if (impl->_abc_negative_cache_version < abc_invalidation_counter) {
|
||||
/* Invalidate the negative cache. */
|
||||
if (impl->_abc_negative_cache != NULL &&
|
||||
PySet_Clear(impl->_abc_negative_cache) < 0)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
impl->_abc_negative_cache_version = abc_invalidation_counter;
|
||||
}
|
||||
else {
|
||||
incache = _in_weak_set(impl->_abc_negative_cache, subclass);
|
||||
if (incache < 0) {
|
||||
goto end;
|
||||
}
|
||||
if (incache > 0) {
|
||||
result = Py_False;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. Check the subclass hook. */
|
||||
ok = _PyObject_CallMethodIdObjArgs((PyObject *)self, &PyId___subclasshook__,
|
||||
subclass, NULL);
|
||||
if (ok == NULL) {
|
||||
goto end;
|
||||
}
|
||||
if (ok == Py_True) {
|
||||
Py_DECREF(ok);
|
||||
if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) {
|
||||
goto end;
|
||||
}
|
||||
result = Py_True;
|
||||
goto end;
|
||||
}
|
||||
if (ok == Py_False) {
|
||||
Py_DECREF(ok);
|
||||
if (_add_to_weak_set(&impl->_abc_negative_cache, subclass) < 0) {
|
||||
goto end;
|
||||
}
|
||||
result = Py_False;
|
||||
goto end;
|
||||
}
|
||||
if (ok != Py_NotImplemented) {
|
||||
Py_DECREF(ok);
|
||||
PyErr_SetString(PyExc_AssertionError, "__subclasshook__ must return either"
|
||||
" False, True, or NotImplemented");
|
||||
goto end;
|
||||
}
|
||||
Py_DECREF(ok);
|
||||
|
||||
/* 4. Check if it's a direct subclass. */
|
||||
mro = ((PyTypeObject *)subclass)->tp_mro;
|
||||
assert(PyTuple_Check(mro));
|
||||
for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) {
|
||||
PyObject *mro_item = PyTuple_GET_ITEM(mro, pos);
|
||||
if (mro_item == NULL) {
|
||||
goto end;
|
||||
}
|
||||
if ((PyObject *)self == mro_item) {
|
||||
if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) {
|
||||
goto end;
|
||||
}
|
||||
result = Py_True;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5. Check if it's a subclass of a registered class (recursive). */
|
||||
if (subclasscheck_check_registry(impl, subclass, &result)) {
|
||||
// Exception occured or result is set.
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* 6. Check if it's a subclass of a subclass (recursive). */
|
||||
subclasses = PyObject_CallMethod(self, "__subclasses__", NULL);
|
||||
if (!PyList_Check(subclasses)) {
|
||||
PyErr_SetString(PyExc_TypeError, "__subclasses__() must return a list");
|
||||
goto end;
|
||||
}
|
||||
for (pos = 0; pos < PyList_GET_SIZE(subclasses); pos++) {
|
||||
PyObject *scls = PyList_GET_ITEM(subclasses, pos);
|
||||
Py_INCREF(scls);
|
||||
int r = PyObject_IsSubclass(subclass, scls);
|
||||
Py_DECREF(scls);
|
||||
if (r > 0) {
|
||||
if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) {
|
||||
goto end;
|
||||
}
|
||||
result = Py_True;
|
||||
goto end;
|
||||
}
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* No dice; update negative cache. */
|
||||
if (_add_to_weak_set(&impl->_abc_negative_cache, subclass) < 0) {
|
||||
goto end;
|
||||
}
|
||||
result = Py_False;
|
||||
|
||||
end:
|
||||
Py_XDECREF(impl);
|
||||
Py_XDECREF(subclasses);
|
||||
Py_XINCREF(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
subclasscheck_check_registry(_abc_data *impl, PyObject *subclass,
|
||||
PyObject **result)
|
||||
{
|
||||
// Fast path: check subclass is in weakref directly.
|
||||
int ret = _in_weak_set(impl->_abc_registry, subclass);
|
||||
if (ret < 0) {
|
||||
*result = NULL;
|
||||
return -1;
|
||||
}
|
||||
if (ret > 0) {
|
||||
*result = Py_True;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (impl->_abc_registry == NULL) {
|
||||
return 0;
|
||||
}
|
||||
Py_ssize_t registry_size = PySet_Size(impl->_abc_registry);
|
||||
if (registry_size == 0) {
|
||||
return 0;
|
||||
}
|
||||
// Weakref callback may remove entry from set.
|
||||
// So we take snapshot of registry first.
|
||||
PyObject **copy = PyMem_Malloc(sizeof(PyObject*) * registry_size);
|
||||
PyObject *key;
|
||||
Py_ssize_t pos = 0;
|
||||
Py_hash_t hash;
|
||||
Py_ssize_t i = 0;
|
||||
|
||||
while (_PySet_NextEntry(impl->_abc_registry, &pos, &key, &hash)) {
|
||||
Py_INCREF(key);
|
||||
copy[i++] = key;
|
||||
}
|
||||
assert(i == registry_size);
|
||||
|
||||
for (i = 0; i < registry_size; i++) {
|
||||
PyObject *rkey = PyWeakref_GetObject(copy[i]);
|
||||
if (rkey == NULL) {
|
||||
// Someone inject non-weakref type in the registry.
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if (rkey == Py_None) {
|
||||
continue;
|
||||
}
|
||||
Py_INCREF(rkey);
|
||||
int r = PyObject_IsSubclass(subclass, rkey);
|
||||
Py_DECREF(rkey);
|
||||
if (r < 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if (r > 0) {
|
||||
if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
*result = Py_True;
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < registry_size; i++) {
|
||||
Py_DECREF(copy[i]);
|
||||
}
|
||||
PyMem_Free(copy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_abc.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.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_abc_get_cache_token_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=c7d87841e033dacc input=70413d1c423ad9f9]*/
|
||||
{
|
||||
return PyLong_FromUnsignedLongLong(abc_invalidation_counter);
|
||||
}
|
||||
|
||||
static struct PyMethodDef module_functions[] = {
|
||||
_ABC_GET_CACHE_TOKEN_METHODDEF
|
||||
_ABC__ABC_INIT_METHODDEF
|
||||
_ABC__RESET_REGISTRY_METHODDEF
|
||||
_ABC__RESET_CACHES_METHODDEF
|
||||
_ABC__GET_DUMP_METHODDEF
|
||||
_ABC__ABC_REGISTER_METHODDEF
|
||||
_ABC__ABC_INSTANCECHECK_METHODDEF
|
||||
_ABC__ABC_SUBCLASSCHECK_METHODDEF
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static struct PyModuleDef _abcmodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_abc",
|
||||
_abc__doc__,
|
||||
-1,
|
||||
module_functions,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__abc(void)
|
||||
{
|
||||
if (PyType_Ready(&_abc_data_type) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
_abc_data_type.tp_doc = abc_data_doc;
|
||||
|
||||
return PyModule_Create(&_abcmodule);
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/*[clinic input]
|
||||
preserve
|
||||
[clinic start generated code]*/
|
||||
|
||||
PyDoc_STRVAR(_abc__reset_registry__doc__,
|
||||
"_reset_registry($module, self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Internal ABC helper to reset registry of a given class.\n"
|
||||
"\n"
|
||||
"Should be only used by refleak.py");
|
||||
|
||||
#define _ABC__RESET_REGISTRY_METHODDEF \
|
||||
{"_reset_registry", (PyCFunction)_abc__reset_registry, METH_O, _abc__reset_registry__doc__},
|
||||
|
||||
PyDoc_STRVAR(_abc__reset_caches__doc__,
|
||||
"_reset_caches($module, self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Internal ABC helper to reset both caches of a given class.\n"
|
||||
"\n"
|
||||
"Should be only used by refleak.py");
|
||||
|
||||
#define _ABC__RESET_CACHES_METHODDEF \
|
||||
{"_reset_caches", (PyCFunction)_abc__reset_caches, METH_O, _abc__reset_caches__doc__},
|
||||
|
||||
PyDoc_STRVAR(_abc__get_dump__doc__,
|
||||
"_get_dump($module, self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Internal ABC helper for cache and registry debugging.\n"
|
||||
"\n"
|
||||
"Return shallow copies of registry, of both caches, and\n"
|
||||
"negative cache version. Don\'t call this function directly,\n"
|
||||
"instead use ABC._dump_registry() for a nice repr.");
|
||||
|
||||
#define _ABC__GET_DUMP_METHODDEF \
|
||||
{"_get_dump", (PyCFunction)_abc__get_dump, METH_O, _abc__get_dump__doc__},
|
||||
|
||||
PyDoc_STRVAR(_abc__abc_init__doc__,
|
||||
"_abc_init($module, self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Internal ABC helper for class set-up. Should be never used outside abc module.");
|
||||
|
||||
#define _ABC__ABC_INIT_METHODDEF \
|
||||
{"_abc_init", (PyCFunction)_abc__abc_init, METH_O, _abc__abc_init__doc__},
|
||||
|
||||
PyDoc_STRVAR(_abc__abc_register__doc__,
|
||||
"_abc_register($module, self, subclass, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Internal ABC helper for subclasss registration. Should be never used outside abc module.");
|
||||
|
||||
#define _ABC__ABC_REGISTER_METHODDEF \
|
||||
{"_abc_register", (PyCFunction)_abc__abc_register, METH_FASTCALL, _abc__abc_register__doc__},
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass);
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_register(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject *self;
|
||||
PyObject *subclass;
|
||||
|
||||
if (!_PyArg_UnpackStack(args, nargs, "_abc_register",
|
||||
2, 2,
|
||||
&self, &subclass)) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = _abc__abc_register_impl(module, self, subclass);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_abc__abc_instancecheck__doc__,
|
||||
"_abc_instancecheck($module, self, instance, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Internal ABC helper for instance checks. Should be never used outside abc module.");
|
||||
|
||||
#define _ABC__ABC_INSTANCECHECK_METHODDEF \
|
||||
{"_abc_instancecheck", (PyCFunction)_abc__abc_instancecheck, METH_FASTCALL, _abc__abc_instancecheck__doc__},
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
|
||||
PyObject *instance);
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_instancecheck(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject *self;
|
||||
PyObject *instance;
|
||||
|
||||
if (!_PyArg_UnpackStack(args, nargs, "_abc_instancecheck",
|
||||
2, 2,
|
||||
&self, &instance)) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = _abc__abc_instancecheck_impl(module, self, instance);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_abc__abc_subclasscheck__doc__,
|
||||
"_abc_subclasscheck($module, self, subclass, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Internal ABC helper for subclasss checks. Should be never used outside abc module.");
|
||||
|
||||
#define _ABC__ABC_SUBCLASSCHECK_METHODDEF \
|
||||
{"_abc_subclasscheck", (PyCFunction)_abc__abc_subclasscheck, METH_FASTCALL, _abc__abc_subclasscheck__doc__},
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
|
||||
PyObject *subclass);
|
||||
|
||||
static PyObject *
|
||||
_abc__abc_subclasscheck(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject *self;
|
||||
PyObject *subclass;
|
||||
|
||||
if (!_PyArg_UnpackStack(args, nargs, "_abc_subclasscheck",
|
||||
2, 2,
|
||||
&self, &subclass)) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = _abc__abc_subclasscheck_impl(module, self, subclass);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_abc_get_cache_token__doc__,
|
||||
"get_cache_token($module, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Returns the current ABC cache token.\n"
|
||||
"\n"
|
||||
"The token is an opaque object (supporting equality testing) identifying the\n"
|
||||
"current version of the ABC cache for virtual subclasses. The token changes\n"
|
||||
"with every call to register() on any ABC.");
|
||||
|
||||
#define _ABC_GET_CACHE_TOKEN_METHODDEF \
|
||||
{"get_cache_token", (PyCFunction)_abc_get_cache_token, METH_NOARGS, _abc_get_cache_token__doc__},
|
||||
|
||||
static PyObject *
|
||||
_abc_get_cache_token_impl(PyObject *module);
|
||||
|
||||
static PyObject *
|
||||
_abc_get_cache_token(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return _abc_get_cache_token_impl(module);
|
||||
}
|
||||
/*[clinic end generated code: output=9d6f861a8f45bc6f input=a9049054013a1b77]*/
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "Python.h"
|
||||
|
||||
extern PyObject* PyInit__abc(void);
|
||||
extern PyObject* PyInit_array(void);
|
||||
extern PyObject* PyInit_audioop(void);
|
||||
extern PyObject* PyInit_binascii(void);
|
||||
|
@ -80,6 +81,7 @@ extern PyObject* PyInit__imp(void);
|
|||
|
||||
struct _inittab _PyImport_Inittab[] = {
|
||||
|
||||
{"_abc", PyInit__abc},
|
||||
{"array", PyInit_array},
|
||||
{"_ast", PyInit__ast},
|
||||
{"audioop", PyInit_audioop},
|
||||
|
|
|
@ -228,6 +228,7 @@
|
|||
<ClInclude Include="$(zlibDir)\zutil.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Modules\_abc.c" />
|
||||
<ClCompile Include="..\Modules\_asynciomodule.c" />
|
||||
<ClCompile Include="..\Modules\_bisectmodule.c" />
|
||||
<ClCompile Include="..\Modules\_blake2\blake2module.c" />
|
||||
|
|
|
@ -473,6 +473,9 @@
|
|||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Modules\_abc.c">
|
||||
<Filter>Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_bisectmodule.c">
|
||||
<Filter>Modules</Filter>
|
||||
</ClCompile>
|
||||
|
|
2
setup.py
2
setup.py
|
@ -712,6 +712,8 @@ class PyBuildExt(build_ext):
|
|||
exts.append( Extension('_opcode', ['_opcode.c']) )
|
||||
# asyncio speedups
|
||||
exts.append( Extension("_asyncio", ["_asynciomodule.c"]) )
|
||||
# _abc speedups
|
||||
exts.append( Extension("_abc", ["_abc.c"]) )
|
||||
# _queue module
|
||||
exts.append( Extension("_queue", ["_queuemodule.c"]) )
|
||||
|
||||
|
|
Loading…
Reference in New Issue