Unverified Kaydet (Commit) 03e3c340 authored tarafından Ivan Levkivskyi's avatar Ivan Levkivskyi Kaydeden (comit) GitHub

bpo-31333: Re-implement ABCMeta in C (#5273)

This adds C versions of methods used by ABCMeta that
improve performance of various ABC operations.
üst 667b91a5
...@@ -853,6 +853,12 @@ Optimizations ...@@ -853,6 +853,12 @@ Optimizations
* Constant folding is moved from peephole optimizer to new AST optimizer. * Constant folding is moved from peephole optimizer to new AST optimizer.
(Contributed by Eugene Toder and INADA Naoki in :issue:`29469`) (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 Build and C API Changes
======================= =======================
......
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
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
"""Abstract Base Classes (ABCs) according to PEP 3119.""" """Abstract Base Classes (ABCs) according to PEP 3119."""
from _weakrefset import WeakSet
def abstractmethod(funcobj): def abstractmethod(funcobj):
"""A decorator indicating abstract methods. """A decorator indicating abstract methods.
...@@ -27,8 +25,7 @@ def abstractmethod(funcobj): ...@@ -27,8 +25,7 @@ def abstractmethod(funcobj):
class abstractclassmethod(classmethod): class abstractclassmethod(classmethod):
""" """A decorator indicating abstract classmethods.
A decorator indicating abstract classmethods.
Similar to abstractmethod. Similar to abstractmethod.
...@@ -51,8 +48,7 @@ class abstractclassmethod(classmethod): ...@@ -51,8 +48,7 @@ class abstractclassmethod(classmethod):
class abstractstaticmethod(staticmethod): class abstractstaticmethod(staticmethod):
""" """A decorator indicating abstract staticmethods.
A decorator indicating abstract staticmethods.
Similar to abstractmethod. Similar to abstractmethod.
...@@ -75,8 +71,7 @@ class abstractstaticmethod(staticmethod): ...@@ -75,8 +71,7 @@ class abstractstaticmethod(staticmethod):
class abstractproperty(property): class abstractproperty(property):
""" """A decorator indicating abstract properties.
A decorator indicating abstract properties.
Requires that the metaclass is ABCMeta or derived from it. A Requires that the metaclass is ABCMeta or derived from it. A
class that has a metaclass derived from ABCMeta cannot be class that has a metaclass derived from ABCMeta cannot be
...@@ -106,131 +101,66 @@ class abstractproperty(property): ...@@ -106,131 +101,66 @@ class abstractproperty(property):
__isabstractmethod__ = True __isabstractmethod__ = True
class ABCMeta(type): try:
from _abc import (get_cache_token, _abc_init, _abc_register,
"""Metaclass for defining Abstract Base Classes (ABCs). _abc_instancecheck, _abc_subclasscheck, _get_dump,
_reset_registry, _reset_caches)
Use this metaclass to create an ABC. An ABC can be subclassed except ImportError:
directly, and then acts as a mix-in class. You can also register from _py_abc import ABCMeta, get_cache_token
unrelated concrete classes (even built-in classes) and unrelated ABCMeta.__module__ = 'abc'
ABCs as 'virtual subclasses' -- these and their descendants will else:
be considered subclasses of the registering ABC by the built-in class ABCMeta(type):
issubclass() function, but the registering ABC won't show up in """Metaclass for defining Abstract Base Classes (ABCs).
their MRO (Method Resolution Order) nor will method
implementations defined by the registering ABC be callable (not Use this metaclass to create an ABC. An ABC can be subclassed
even via super()). 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
# A global counter that is incremented each time a class is issubclass() function, but the registering ABC won't show up in
# registered as a virtual subclass of anything. It forces the their MRO (Method Resolution Order) nor will method
# negative cache to be cleared before its next use. implementations defined by the registering ABC be callable (not
# Note: this counter is private. Use `abc.get_cache_token()` for even via super()).
# 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): def __new__(mcls, name, bases, namespace, **kwargs):
raise TypeError("Can only register classes") cls = super().__new__(mcls, name, bases, namespace, **kwargs)
if issubclass(subclass, cls): _abc_init(cls)
return subclass # Already a subclass return cls
# Subtle: test for cycles *after* testing for "already a subclass";
# this means we allow X.register(X) and interpret it as a no-op. def register(cls, subclass):
if issubclass(cls, subclass): """Register a virtual subclass of an ABC.
# This would create a cycle, which is bad for the algorithm below
raise RuntimeError("Refusing to create an inheritance cycle") Returns the subclass, to allow usage as a class decorator.
cls._abc_registry.add(subclass) """
ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache return _abc_register(cls, subclass)
return subclass
def __instancecheck__(cls, instance):
def _dump_registry(cls, file=None): """Override for isinstance(instance, cls)."""
"""Debug helper to print the ABC registry.""" return _abc_instancecheck(cls, instance)
print("Class: %s.%s" % (cls.__module__, cls.__qualname__), file=file)
print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file) def __subclasscheck__(cls, subclass):
for name in cls.__dict__: """Override for issubclass(subclass, cls)."""
if name.startswith("_abc_"): return _abc_subclasscheck(cls, subclass)
value = getattr(cls, name)
if isinstance(value, WeakSet): def _dump_registry(cls, file=None):
value = set(value) """Debug helper to print the ABC registry."""
print("%s: %r" % (name, value), file=file) print(f"Class: {cls.__module__}.{cls.__qualname__}", file=file)
print(f"Inv. counter: {get_cache_token()}", file=file)
def __instancecheck__(cls, instance): (_abc_registry, _abc_cache, _abc_negative_cache,
"""Override for isinstance(instance, cls).""" _abc_negative_cache_version) = _get_dump(cls)
# Inline the cache checking print(f"_abc_registry: {_abc_registry!r}", file=file)
subclass = instance.__class__ print(f"_abc_cache: {_abc_cache!r}", file=file)
if subclass in cls._abc_cache: print(f"_abc_negative_cache: {_abc_negative_cache!r}", file=file)
return True print(f"_abc_negative_cache_version: {_abc_negative_cache_version!r}",
subtype = type(instance) file=file)
if subtype is subclass:
if (cls._abc_negative_cache_version == def _abc_registry_clear(cls):
ABCMeta._abc_invalidation_counter and """Clear the registry (for debugging or testing)."""
subclass in cls._abc_negative_cache): _reset_registry(cls)
return False
# Fall back to the subclass check. def _abc_caches_clear(cls):
return cls.__subclasscheck__(subclass) """Clear the caches (for debugging or testing)."""
return any(cls.__subclasscheck__(c) for c in {subclass, subtype}) _reset_caches(cls)
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
class ABC(metaclass=ABCMeta): class ABC(metaclass=ABCMeta):
...@@ -238,13 +168,3 @@ class ABC(metaclass=ABCMeta): ...@@ -238,13 +168,3 @@ class ABC(metaclass=ABCMeta):
inheritance. inheritance.
""" """
__slots__ = () __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 ...@@ -5,6 +5,13 @@ import sys
import warnings import warnings
from inspect import isabstract from inspect import isabstract
from test import support 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): def dash_R(the_module, test, indirect_test, huntrleaks):
...@@ -36,7 +43,7 @@ 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): if not isabstract(abc):
continue continue
for obj in abc.__subclasses__() + [abc]: 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 # 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 # 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): ...@@ -113,7 +120,6 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
def dash_R_cleanup(fs, ps, pic, zdc, abcs): def dash_R_cleanup(fs, ps, pic, zdc, abcs):
import gc, copyreg import gc, copyreg
import collections.abc import collections.abc
from weakref import WeakSet
# Restore some original values. # Restore some original values.
warnings.filters[:] = fs warnings.filters[:] = fs
...@@ -137,9 +143,10 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs): ...@@ -137,9 +143,10 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs):
abs_classes = filter(isabstract, abs_classes) abs_classes = filter(isabstract, abs_classes)
for abc in abs_classes: for abc in abs_classes:
for obj in abc.__subclasses__() + [abc]: for obj in abc.__subclasses__() + [abc]:
obj._abc_registry = abcs.get(obj, WeakSet()).copy() for ref in abcs.get(obj, set()):
obj._abc_cache.clear() if ref() is not None:
obj._abc_negative_cache.clear() obj.register(ref())
obj._abc_caches_clear()
clear_caches() clear_caches()
......
# Copyright 2007 Google, Inc. All Rights Reserved. # Copyright 2007 Google, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement. # 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.""" """Unit tests for abc.py."""
import unittest import unittest
import abc import abc
import _py_abc
from inspect import isabstract 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):
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):
@abc.abstractproperty @abc.abstractproperty
def foo(self): return 3 def foo(self): pass
self.assertRaises(TypeError, C) self.assertTrue(foo.__isabstractmethod__)
class D(C): def bar(self): pass
@property self.assertFalse(hasattr(bar, "__isabstractmethod__"))
def foo(self): return super().foo
self.assertEqual(D().foo, 3) class C(metaclass=abc_ABCMeta):
self.assertFalse(getattr(D.foo, "__isabstractmethod__", False)) @abc.abstractproperty
def foo(self): return 3
def test_abstractclassmethod_basics(self): self.assertRaises(TypeError, C)
@abc.abstractclassmethod class D(C):
def foo(cls): pass @property
self.assertTrue(foo.__isabstractmethod__) def foo(self): return super().foo
@classmethod self.assertEqual(D().foo, 3)
def bar(cls): pass self.assertFalse(getattr(D.foo, "__isabstractmethod__", False))
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
def test_abstractclassmethod_basics(self):
class C(metaclass=abc.ABCMeta):
@abc.abstractclassmethod @abc.abstractclassmethod
def foo(cls): return cls.__name__ def foo(cls): pass
self.assertRaises(TypeError, C) self.assertTrue(foo.__isabstractmethod__)
class D(C):
@classmethod @classmethod
def foo(cls): return super().foo() def bar(cls): pass
self.assertEqual(D.foo(), 'D') self.assertFalse(getattr(bar, "__isabstractmethod__", False))
self.assertEqual(D().foo(), 'D')
class C(metaclass=abc_ABCMeta):
def test_abstractstaticmethod_basics(self): @abc.abstractclassmethod
@abc.abstractstaticmethod def foo(cls): return cls.__name__
def foo(): pass self.assertRaises(TypeError, C)
self.assertTrue(foo.__isabstractmethod__) class D(C):
@staticmethod @classmethod
def bar(): pass def foo(cls): return super().foo()
self.assertFalse(getattr(bar, "__isabstractmethod__", False)) self.assertEqual(D.foo(), 'D')
self.assertEqual(D().foo(), 'D')
class C(metaclass=abc.ABCMeta):
def test_abstractstaticmethod_basics(self):
@abc.abstractstaticmethod @abc.abstractstaticmethod
def foo(): return 3 def foo(): pass
self.assertRaises(TypeError, C) self.assertTrue(foo.__isabstractmethod__)
class D(C):
@staticmethod @staticmethod
def foo(): return 4 def bar(): pass
self.assertEqual(D.foo(), 4) self.assertFalse(getattr(bar, "__isabstractmethod__", False))
self.assertEqual(D().foo(), 4)
class TestABC(unittest.TestCase): 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_ABC_helper(self): def test_abstractmethod_basics(self):
# create an ABC using the helper class and perform basic checks
class C(abc.ABC):
@classmethod
@abc.abstractmethod @abc.abstractmethod
def foo(cls): return cls.__name__ def foo(self): pass
self.assertEqual(type(C), abc.ABCMeta) self.assertTrue(foo.__isabstractmethod__)
self.assertRaises(TypeError, C) def bar(self): pass
class D(C): self.assertFalse(hasattr(bar, "__isabstractmethod__"))
@classmethod
def foo(cls): return super().foo() def test_abstractproperty_basics(self):
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 @property
@abc.abstractmethod @abc.abstractmethod
def foo(self): return 3 def foo(self): pass
self.assertRaises(TypeError, C) self.assertTrue(foo.__isabstractmethod__)
class D(C): def bar(self): pass
@C.foo.getter self.assertFalse(getattr(bar, "__isabstractmethod__", False))
def foo(self): return super().foo
self.assertEqual(D().foo, 3) class C(metaclass=abc_ABCMeta):
@property
def test_abstractclassmethod_basics(self): @abc.abstractmethod
@classmethod def foo(self): return 3
@abc.abstractmethod self.assertRaises(TypeError, C)
def foo(cls): pass class D(C):
self.assertTrue(foo.__isabstractmethod__) @C.foo.getter
@classmethod def foo(self): return super().foo
def bar(cls): pass self.assertEqual(D().foo, 3)
self.assertFalse(getattr(bar, "__isabstractmethod__", False))
def test_abstractclassmethod_basics(self):
class C(metaclass=abc.ABCMeta):
@classmethod @classmethod
@abc.abstractmethod @abc.abstractmethod
def foo(cls): return cls.__name__ def foo(cls): pass
self.assertRaises(TypeError, C) self.assertTrue(foo.__isabstractmethod__)
class D(C):
@classmethod @classmethod
def foo(cls): return super().foo() def bar(cls): pass
self.assertEqual(D.foo(), 'D') self.assertFalse(getattr(bar, "__isabstractmethod__", False))
self.assertEqual(D().foo(), 'D')
class C(metaclass=abc_ABCMeta):
def test_abstractstaticmethod_basics(self): @classmethod
@staticmethod @abc.abstractmethod
@abc.abstractmethod def foo(cls): return cls.__name__
def foo(): pass self.assertRaises(TypeError, C)
self.assertTrue(foo.__isabstractmethod__) class D(C):
@staticmethod @classmethod
def bar(): pass def foo(cls): return super().foo()
self.assertFalse(getattr(bar, "__isabstractmethod__", False)) self.assertEqual(D.foo(), 'D')
self.assertEqual(D().foo(), 'D')
class C(metaclass=abc.ABCMeta):
def test_abstractstaticmethod_basics(self):
@staticmethod @staticmethod
@abc.abstractmethod @abc.abstractmethod
def foo(): return 3 def foo(): pass
self.assertRaises(TypeError, C) self.assertTrue(foo.__isabstractmethod__)
class D(C):
@staticmethod @staticmethod
def foo(): return 4 def bar(): pass
self.assertEqual(D.foo(), 4) self.assertFalse(getattr(bar, "__isabstractmethod__", False))
self.assertEqual(D().foo(), 4)
class C(metaclass=abc_ABCMeta):
def test_abstractmethod_integration(self): @staticmethod
for abstractthing in [abc.abstractmethod, abc.abstractproperty, @abc.abstractmethod
abc.abstractclassmethod, def foo(): return 3
abc.abstractstaticmethod]: self.assertRaises(TypeError, C)
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): class D(C):
def bar(self): pass # concrete override of concrete @staticmethod
self.assertEqual(D.__abstractmethods__, {"foo"}) def foo(): return 4
self.assertRaises(TypeError, D) # because foo is still abstract self.assertEqual(D.foo(), 4)
self.assertTrue(isabstract(D)) 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): class E(D):
def foo(self): pass @D.foo.setter
self.assertEqual(E.__abstractmethods__, set()) def foo(self, val): pass
E() # now foo is concrete, too self.assertEqual(E().foo, 3)
self.assertFalse(isabstract(E)) # check that the property's __isabstractmethod__ descriptor does the
class F(E): # right thing when presented with a value that fails truth testing:
@abstractthing class NotBool(object):
def bar(self): pass # abstract override of concrete def __bool__(self):
self.assertEqual(F.__abstractmethods__, {"bar"}) raise ValueError()
self.assertRaises(TypeError, F) # because bar is abstract now __len__ = __bool__
self.assertTrue(isabstract(F)) with self.assertRaises(ValueError):
class F(C):
def test_descriptors_with_abstractmethod(self): def bar(self):
class C(metaclass=abc.ABCMeta): pass
@property bar.__isabstractmethod__ = NotBool()
@abc.abstractmethod foo = property(bar)
def foo(self): return 3
@foo.setter
@abc.abstractmethod def test_customdescriptors_with_abstractmethod(self):
def foo(self, val): pass class Descriptor:
self.assertRaises(TypeError, C) def __init__(self, fget, fset=None):
class D(C): self._fget = fget
@C.foo.getter self._fset = fset
def foo(self): return super().foo def getter(self, callable):
self.assertRaises(TypeError, D) return Descriptor(callable, self._fget)
class E(D): def setter(self, callable):
@D.foo.setter return Descriptor(self._fget, callable)
def foo(self, val): pass @property
self.assertEqual(E().foo, 3) def __isabstractmethod__(self):
# check that the property's __isabstractmethod__ descriptor does the return (getattr(self._fget, '__isabstractmethod__', False)
# right thing when presented with a value that fails truth testing: or getattr(self._fset, '__isabstractmethod__', False))
class NotBool(object): class C(metaclass=abc_ABCMeta):
def __bool__(self): @Descriptor
raise ValueError() @abc.abstractmethod
__len__ = __bool__ def foo(self): return 3
with self.assertRaises(ValueError): @foo.setter
class F(C): @abc.abstractmethod
def bar(self): 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 pass
bar.__isabstractmethod__ = NotBool() self.assertEqual(A.__abstractmethods__, {"x"})
foo = property(bar) class meta(type, A):
def x(self):
return 1
def test_customdescriptors_with_abstractmethod(self): class C(metaclass=meta):
class Descriptor: pass
def __init__(self, fget, fset=None):
self._fget = fget def test_registration_basics(self):
self._fset = fset class A(metaclass=abc_ABCMeta):
def getter(self, callable): pass
return Descriptor(callable, self._fget) class B(object):
def setter(self, callable): pass
return Descriptor(self._fget, callable) b = B()
@property self.assertFalse(issubclass(B, A))
def __isabstractmethod__(self): self.assertFalse(issubclass(B, (A,)))
return (getattr(self._fget, '__isabstractmethod__', False) self.assertNotIsInstance(b, A)
or getattr(self._fset, '__isabstractmethod__', False)) self.assertNotIsInstance(b, (A,))
class C(metaclass=abc.ABCMeta): B1 = A.register(B)
@Descriptor self.assertTrue(issubclass(B, A))
@abc.abstractmethod self.assertTrue(issubclass(B, (A,)))
def foo(self): return 3 self.assertIsInstance(b, A)
@foo.setter self.assertIsInstance(b, (A,))
@abc.abstractmethod self.assertIs(B1, B)
def foo(self, val): pass class C(B):
self.assertRaises(TypeError, C) pass
class D(C): c = C()
@C.foo.getter self.assertTrue(issubclass(C, A))
def foo(self): return super().foo self.assertTrue(issubclass(C, (A,)))
self.assertRaises(TypeError, D) self.assertIsInstance(c, A)
class E(D): self.assertIsInstance(c, (A,))
@D.foo.setter
def foo(self, val): pass def test_register_as_class_deco(self):
self.assertFalse(E.foo.__isabstractmethod__) class A(metaclass=abc_ABCMeta):
pass
def test_metaclass_abc(self): @A.register
# Metaclasses can be ABCs, too. class B(object):
class A(metaclass=abc.ABCMeta): pass
@abc.abstractmethod b = B()
def x(self): 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_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_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_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_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):
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 pass
self.assertEqual(A.__abstractmethods__, {"x"}) self.assertEqual(saved_kwargs, dict(x=1, y=2, z=3))
class meta(type, A): return TestLegacyAPI, TestABC, TestABCWithInitSubclass
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_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_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_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_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__'))
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))
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__": if __name__ == "__main__":
unittest.main() unittest.main()
...@@ -761,8 +761,8 @@ class GenericTests(BaseTestCase): ...@@ -761,8 +761,8 @@ class GenericTests(BaseTestCase):
self.assertIsInstance(1, C) self.assertIsInstance(1, C)
C[int] C[int]
self.assertIsInstance(1, C) self.assertIsInstance(1, C)
C._abc_registry.clear() C._abc_registry_clear()
C._abc_cache.clear() # To keep refleak hunting mode clean C._abc_caches_clear() # To keep refleak hunting mode clean
def test_false_subclasses(self): def test_false_subclasses(self):
class MyMapping(MutableMapping[str, str]): pass class MyMapping(MutableMapping[str, str]): pass
......
``_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 ...@@ -114,6 +114,7 @@ _weakref _weakref.c # weak references
_functools _functoolsmodule.c # Tools for working with functions and callable objects _functools _functoolsmodule.c # Tools for working with functions and callable objects
_operator _operator.c # operator.add() and similar goodies _operator _operator.c # operator.add() and similar goodies
_collections _collectionsmodule.c # Container types _collections _collectionsmodule.c # Container types
_abc _abc.c # Abstract base classes
itertools itertoolsmodule.c # Functions creating iterators for efficient looping itertools itertoolsmodule.c # Functions creating iterators for efficient looping
atexit atexitmodule.c # Register functions to be run at interpreter-shutdown atexit atexitmodule.c # Register functions to be run at interpreter-shutdown
_signal signalmodule.c _signal signalmodule.c
......
/* 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);
}
/*[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 @@ ...@@ -5,6 +5,7 @@
#include "Python.h" #include "Python.h"
extern PyObject* PyInit__abc(void);
extern PyObject* PyInit_array(void); extern PyObject* PyInit_array(void);
extern PyObject* PyInit_audioop(void); extern PyObject* PyInit_audioop(void);
extern PyObject* PyInit_binascii(void); extern PyObject* PyInit_binascii(void);
...@@ -80,6 +81,7 @@ extern PyObject* PyInit__imp(void); ...@@ -80,6 +81,7 @@ extern PyObject* PyInit__imp(void);
struct _inittab _PyImport_Inittab[] = { struct _inittab _PyImport_Inittab[] = {
{"_abc", PyInit__abc},
{"array", PyInit_array}, {"array", PyInit_array},
{"_ast", PyInit__ast}, {"_ast", PyInit__ast},
{"audioop", PyInit_audioop}, {"audioop", PyInit_audioop},
......
...@@ -228,6 +228,7 @@ ...@@ -228,6 +228,7 @@
<ClInclude Include="$(zlibDir)\zutil.h" /> <ClInclude Include="$(zlibDir)\zutil.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\Modules\_abc.c" />
<ClCompile Include="..\Modules\_asynciomodule.c" /> <ClCompile Include="..\Modules\_asynciomodule.c" />
<ClCompile Include="..\Modules\_bisectmodule.c" /> <ClCompile Include="..\Modules\_bisectmodule.c" />
<ClCompile Include="..\Modules\_blake2\blake2module.c" /> <ClCompile Include="..\Modules\_blake2\blake2module.c" />
......
...@@ -473,6 +473,9 @@ ...@@ -473,6 +473,9 @@
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\Modules\_abc.c">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_bisectmodule.c"> <ClCompile Include="..\Modules\_bisectmodule.c">
<Filter>Modules</Filter> <Filter>Modules</Filter>
</ClCompile> </ClCompile>
......
...@@ -712,6 +712,8 @@ class PyBuildExt(build_ext): ...@@ -712,6 +712,8 @@ class PyBuildExt(build_ext):
exts.append( Extension('_opcode', ['_opcode.c']) ) exts.append( Extension('_opcode', ['_opcode.c']) )
# asyncio speedups # asyncio speedups
exts.append( Extension("_asyncio", ["_asynciomodule.c"]) ) exts.append( Extension("_asyncio", ["_asynciomodule.c"]) )
# _abc speedups
exts.append( Extension("_abc", ["_abc.c"]) )
# _queue module # _queue module
exts.append( Extension("_queue", ["_queuemodule.c"]) ) exts.append( Extension("_queue", ["_queuemodule.c"]) )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment