Skip to content

Commit 8518bdc

Browse files
committed
Somehow this needed adding.
1 parent f5b4685 commit 8518bdc

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

Lib/abc.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Copyright 2007 Google, Inc. All Rights Reserved.
2+
# Licensed to PSF under a Contributor Agreement.
3+
4+
"""Abstract Base Classes (ABCs) according to PEP 3119."""
5+
6+
7+
def abstractmethod(funcobj):
8+
"""A decorator indicating abstract methods.
9+
10+
Requires that the metaclass is ABCMeta or derived from it. A
11+
class that has a metaclass derived from ABCMeta cannot be
12+
instantiated unless all of its abstract methods are overridden.
13+
The abstract methods can be called using any of the the normal
14+
'super' call mechanisms.
15+
16+
Usage:
17+
18+
class C(metaclass=ABCMeta):
19+
@abstractmethod
20+
def my_abstract_method(self, ...):
21+
...
22+
"""
23+
funcobj.__isabstractmethod__ = True
24+
return funcobj
25+
26+
27+
class _Abstract(object):
28+
29+
"""Helper class inserted into the bases by ABCMeta (using _fix_bases()).
30+
31+
You should never need to explicitly subclass this class.
32+
33+
There should never be a base class between _Abstract and object.
34+
"""
35+
36+
def __new__(cls, *args, **kwds):
37+
am = cls.__dict__.get("__abstractmethods__")
38+
if am:
39+
raise TypeError("Can't instantiate abstract class %s "
40+
"with abstract methods %s" %
41+
(cls.__name__, ", ".join(sorted(am))))
42+
if (args or kwds) and cls.__init__ is object.__init__:
43+
raise TypeError("Can't pass arguments to __new__ "
44+
"without overriding __init__")
45+
return object.__new__(cls)
46+
47+
@classmethod
48+
def __subclasshook__(cls, subclass):
49+
"""Abstract classes can override this to customize issubclass().
50+
51+
This is invoked early on by __subclasscheck__() below. It
52+
should return True, False or NotImplemented. If it returns
53+
NotImplemented, the normal algorithm is used. Otherwise, it
54+
overrides the normal algorithm (and the outcome is cached).
55+
"""
56+
return NotImplemented
57+
58+
59+
def _fix_bases(bases):
60+
"""Helper method that inserts _Abstract in the bases if needed."""
61+
for base in bases:
62+
if issubclass(base, _Abstract):
63+
# _Abstract is already a base (maybe indirectly)
64+
return bases
65+
if object in bases:
66+
# Replace object with _Abstract
67+
return tuple([_Abstract if base is object else base
68+
for base in bases])
69+
# Append _Abstract to the end
70+
return bases + (_Abstract,)
71+
72+
73+
class ABCMeta(type):
74+
75+
"""Metaclass for defining Abstract Base Classes (ABCs).
76+
77+
Use this metaclass to create an ABC. An ABC can be subclassed
78+
directly, and then acts as a mix-in class. You can also register
79+
unrelated concrete classes (even built-in classes) and unrelated
80+
ABCs as 'virtual subclasses' -- these and their descendants will
81+
be considered subclasses of the registering ABC by the built-in
82+
issubclass() function, but the registering ABC won't show up in
83+
their MRO (Method Resolution Order) nor will method
84+
implementations defined by the registering ABC be callable (not
85+
even via super()).
86+
87+
"""
88+
89+
# A global counter that is incremented each time a class is
90+
# registered as a virtual subclass of anything. It forces the
91+
# negative cache to be cleared before its next use.
92+
__invalidation_counter = 0
93+
94+
def __new__(mcls, name, bases, namespace):
95+
bases = _fix_bases(bases)
96+
cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)
97+
# Compute set of abstract method names
98+
abstracts = {name
99+
for name, value in namespace.items()
100+
if getattr(value, "__isabstractmethod__", False)}
101+
for base in bases:
102+
for name in getattr(base, "__abstractmethods__", set()):
103+
value = getattr(cls, name, None)
104+
if getattr(value, "__isabstractmethod__", False):
105+
abstracts.add(name)
106+
cls.__abstractmethods__ = abstracts
107+
# Set up inheritance registry
108+
cls.__registry = set()
109+
cls.__cache = set()
110+
cls.__negative_cache = set()
111+
cls.__negative_cache_version = ABCMeta.__invalidation_counter
112+
return cls
113+
114+
def register(cls, subclass):
115+
"""Register a virtual subclass of an ABC."""
116+
if not isinstance(cls, type):
117+
raise TypeError("Can only register classes")
118+
if issubclass(subclass, cls):
119+
return # Already a subclass
120+
# Subtle: test for cycles *after* testing for "already a subclass";
121+
# this means we allow X.register(X) and interpret it as a no-op.
122+
if issubclass(cls, subclass):
123+
# This would create a cycle, which is bad for the algorithm below
124+
raise RuntimeError("Refusing to create an inheritance cycle")
125+
cls.__registry.add(subclass)
126+
ABCMeta.__invalidation_counter += 1 # Invalidate negative cache
127+
128+
def _dump_registry(cls, file=None):
129+
"""Debug helper to print the ABC registry."""
130+
print("Class: %s.%s" % (cls.__module__, cls.__name__), file=file)
131+
print("Inv.counter: %s" % ABCMeta.__invalidation_counter, file=file)
132+
for name in sorted(cls.__dict__.keys()):
133+
if name.startswith("__abc_"):
134+
value = getattr(cls, name)
135+
print("%s: %r" % (name, value), file=file)
136+
137+
def __instancecheck__(cls, instance):
138+
"""Override for isinstance(instance, cls)."""
139+
return any(cls.__subclasscheck__(c)
140+
for c in {instance.__class__, type(instance)})
141+
142+
def __subclasscheck__(cls, subclass):
143+
"""Override for issubclass(subclass, cls)."""
144+
# Check cache
145+
if subclass in cls.__cache:
146+
return True
147+
# Check negative cache; may have to invalidate
148+
if cls.__negative_cache_version < ABCMeta.__invalidation_counter:
149+
# Invalidate the negative cache
150+
cls.__negative_cache_version = ABCMeta.__invalidation_counter
151+
cls.__negative_cache = set()
152+
elif subclass in cls.__negative_cache:
153+
return False
154+
# Check the subclass hook
155+
ok = cls.__subclasshook__(subclass)
156+
if ok is not NotImplemented:
157+
assert isinstance(ok, bool)
158+
if ok:
159+
cls.__cache.add(subclass)
160+
else:
161+
cls.__negative_cache.add(subclass)
162+
return ok
163+
# Check if it's a direct subclass
164+
if cls in subclass.__mro__:
165+
cls.__cache.add(subclass)
166+
return True
167+
# Check if it's a subclass of a registered class (recursive)
168+
for rcls in cls.__registry:
169+
if issubclass(subclass, rcls):
170+
cls.__registry.add(subclass)
171+
return True
172+
# Check if it's a subclass of a subclass (recursive)
173+
for scls in cls.__subclasses__():
174+
if issubclass(subclass, scls):
175+
cls.__registry.add(subclass)
176+
return True
177+
# No dice; update negative cache
178+
cls.__negative_cache.add(subclass)
179+
return False

0 commit comments

Comments
 (0)