Browse Source
bpo-31333: Re-implement ABCMeta in C (#5273)
bpo-31333: Re-implement ABCMeta in C (#5273)
This adds C versions of methods used by ABCMeta that improve performance of various ABC operations.pull/5735/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1640 additions and 536 deletions
-
6Doc/whatsnew/3.7.rst
-
145Lib/_py_abc.py
-
204Lib/abc.py
-
17Lib/test/libregrtest/refleak.py
-
797Lib/test/test_abc.py
-
4Lib/test/test_typing.py
-
10Misc/NEWS.d/next/Library/2018-02-15-08-18-52.bpo-31333.4fF-gM.rst
-
1Modules/Setup.dist
-
822Modules/_abc.c
-
162Modules/clinic/_abc.c.h
-
2PC/config.c
-
1PCbuild/pythoncore.vcxproj
-
3PCbuild/pythoncore.vcxproj.filters
-
2setup.py
@ -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 |
|||
@ -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 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): |
|||
def foo(self): pass |
|||
self.assertTrue(foo.__isabstractmethod__) |
|||
def bar(self): pass |
|||
self.assertFalse(hasattr(bar, "__isabstractmethod__")) |
|||
|
|||
class C(metaclass=abc_ABCMeta): |
|||
@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 test_abstractclassmethod_basics(self): |
|||
@abc.abstractclassmethod |
|||
def foo(cls): return cls.__name__ |
|||
self.assertRaises(TypeError, C) |
|||
class D(C): |
|||
def foo(cls): pass |
|||
self.assertTrue(foo.__isabstractmethod__) |
|||
@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): |
|||
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(): return 3 |
|||
self.assertRaises(TypeError, C) |
|||
class D(C): |
|||
def foo(): pass |
|||
self.assertTrue(foo.__isabstractmethod__) |
|||
@staticmethod |
|||
def foo(): return 4 |
|||
self.assertEqual(D.foo(), 4) |
|||
self.assertEqual(D().foo(), 4) |
|||
|
|||
def bar(): pass |
|||
self.assertFalse(getattr(bar, "__isabstractmethod__", False)) |
|||
|
|||
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): |
|||
# create an ABC using the helper class and perform basic checks |
|||
class C(abc.ABC): |
|||
@classmethod |
|||
def test_abstractmethod_basics(self): |
|||
@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): |
|||
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): 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): |
|||
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): return cls.__name__ |
|||
self.assertRaises(TypeError, C) |
|||
class D(C): |
|||
def foo(cls): pass |
|||
self.assertTrue(foo.__isabstractmethod__) |
|||
@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): |
|||
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(): return 3 |
|||
self.assertRaises(TypeError, C) |
|||
class D(C): |
|||
def foo(): pass |
|||
self.assertTrue(foo.__isabstractmethod__) |
|||
@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)) |
|||
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): |
|||
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)) |
|||
@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)) |
|||
|
|||
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): |
|||
@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_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 |
|||
|
|||
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__')) |
|||
|
|||
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 |
|||
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_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)) |
|||
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() |
|||
@ -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``. |
|||
@ -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]*/ |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue