Show More
Commit Description:
Merge pull request #17 from nattee/master...
Commit Description:
Merge pull request #17 from nattee/master
upgrade to current working snapshot
References:
File last commit:
Show/Diff file:
Action:
lib/assets/Lib/abc.py
| 228 lines
| 8.1 KiB
| text/x-python
| PythonLexer
|
r584 | # Copyright 2007 Google, Inc. All Rights Reserved. | |||
# Licensed to PSF under a Contributor Agreement. | ||||
"""Abstract Base Classes (ABCs) according to PEP 3119.""" | ||||
from _weakrefset import WeakSet | ||||
def abstractmethod(funcobj): | ||||
"""A decorator indicating abstract methods. | ||||
Requires that the metaclass is ABCMeta or derived from it. A | ||||
class that has a metaclass derived from ABCMeta cannot be | ||||
instantiated unless all of its abstract methods are overridden. | ||||
The abstract methods can be called using any of the normal | ||||
'super' call mechanisms. | ||||
Usage: | ||||
class C(metaclass=ABCMeta): | ||||
@abstractmethod | ||||
def my_abstract_method(self, ...): | ||||
... | ||||
""" | ||||
funcobj.__isabstractmethod__ = True | ||||
return funcobj | ||||
class abstractclassmethod(classmethod): | ||||
""" | ||||
A decorator indicating abstract classmethods. | ||||
Similar to abstractmethod. | ||||
Usage: | ||||
class C(metaclass=ABCMeta): | ||||
@abstractclassmethod | ||||
def my_abstract_classmethod(cls, ...): | ||||
... | ||||
'abstractclassmethod' is deprecated. Use 'classmethod' with | ||||
'abstractmethod' instead. | ||||
""" | ||||
__isabstractmethod__ = True | ||||
def __init__(self, callable): | ||||
callable.__isabstractmethod__ = True | ||||
super().__init__(callable) | ||||
class abstractstaticmethod(staticmethod): | ||||
""" | ||||
A decorator indicating abstract staticmethods. | ||||
Similar to abstractmethod. | ||||
Usage: | ||||
class C(metaclass=ABCMeta): | ||||
@abstractstaticmethod | ||||
def my_abstract_staticmethod(...): | ||||
... | ||||
'abstractstaticmethod' is deprecated. Use 'staticmethod' with | ||||
'abstractmethod' instead. | ||||
""" | ||||
__isabstractmethod__ = True | ||||
def __init__(self, callable): | ||||
callable.__isabstractmethod__ = True | ||||
super().__init__(callable) | ||||
class abstractproperty(property): | ||||
""" | ||||
A decorator indicating abstract properties. | ||||
Requires that the metaclass is ABCMeta or derived from it. A | ||||
class that has a metaclass derived from ABCMeta cannot be | ||||
instantiated unless all of its abstract properties are overridden. | ||||
The abstract properties can be called using any of the normal | ||||
'super' call mechanisms. | ||||
Usage: | ||||
class C(metaclass=ABCMeta): | ||||
@abstractproperty | ||||
def my_abstract_property(self): | ||||
... | ||||
This defines a read-only property; you can also define a read-write | ||||
abstract property using the 'long' form of property declaration: | ||||
class C(metaclass=ABCMeta): | ||||
def getx(self): ... | ||||
def setx(self, value): ... | ||||
x = abstractproperty(getx, setx) | ||||
'abstractproperty' is deprecated. Use 'property' with 'abstractmethod' | ||||
instead. | ||||
""" | ||||
__isabstractmethod__ = True | ||||
class ABCMeta(type): | ||||
"""Metaclass for defining Abstract Base Classes (ABCs). | ||||
Use this metaclass to create an ABC. An ABC can be subclassed | ||||
directly, and then acts as a mix-in class. You can also register | ||||
unrelated concrete classes (even built-in classes) and unrelated | ||||
ABCs as 'virtual subclasses' -- these and their descendants will | ||||
be considered subclasses of the registering ABC by the built-in | ||||
issubclass() function, but the registering ABC won't show up in | ||||
their MRO (Method Resolution Order) nor will method | ||||
implementations defined by the registering ABC be callable (not | ||||
even via super()). | ||||
""" | ||||
# A global counter that is incremented each time a class is | ||||
# registered as a virtual subclass of anything. It forces the | ||||
# negative cache to be cleared before its next use. | ||||
_abc_invalidation_counter = 0 | ||||
def __new__(mcls, name, bases, namespace): | ||||
cls = super().__new__(mcls, name, bases, namespace) | ||||
# 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("Class: %s.%s" % (cls.__module__, cls.__name__), file=file) | ||||
print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file) | ||||
for name in sorted(cls.__dict__.keys()): | ||||
if name.startswith("_abc_"): | ||||
value = getattr(cls, name) | ||||
print("%s: %r" % (name, value), file=file) | ||||
def __instancecheck__(cls, instance): | ||||
"""Override for isinstance(instance, cls).""" | ||||
# Inline the cache checking | ||||
subclass = instance.__class__ | ||||
if subclass in cls._abc_cache: | ||||
return True | ||||
subtype = type(instance) | ||||
if subtype is subclass: | ||||
if (cls._abc_negative_cache_version == | ||||
ABCMeta._abc_invalidation_counter and | ||||
subclass in cls._abc_negative_cache): | ||||
return False | ||||
# Fall back to the subclass check. | ||||
return cls.__subclasscheck__(subclass) | ||||
return any(cls.__subclasscheck__(c) for c in {subclass, subtype}) | ||||
def __subclasscheck__(cls, subclass): | ||||
"""Override for issubclass(subclass, cls).""" | ||||
# Check cache | ||||
if subclass in cls._abc_cache: | ||||
return True | ||||
# Check negative cache; may have to invalidate | ||||
if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter: | ||||
# Invalidate the negative cache | ||||
cls._abc_negative_cache = WeakSet() | ||||
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter | ||||
elif subclass in cls._abc_negative_cache: | ||||
return False | ||||
# Check the subclass hook | ||||
ok = cls.__subclasshook__(subclass) | ||||
if ok is not NotImplemented: | ||||
assert isinstance(ok, bool) | ||||
if ok: | ||||
cls._abc_cache.add(subclass) | ||||
else: | ||||
cls._abc_negative_cache.add(subclass) | ||||
return ok | ||||
# Check if it's a direct subclass | ||||
if cls in getattr(subclass, '__mro__', ()): | ||||
cls._abc_cache.add(subclass) | ||||
return True | ||||
# Check if it's a subclass of a registered class (recursive) | ||||
for rcls in cls._abc_registry: | ||||
if issubclass(subclass, rcls): | ||||
cls._abc_cache.add(subclass) | ||||
return True | ||||
# Check if it's a subclass of a subclass (recursive) | ||||
for scls in cls.__subclasses__(): | ||||
if issubclass(subclass, scls): | ||||
cls._abc_cache.add(subclass) | ||||
return True | ||||
# No dice; update negative cache | ||||
cls._abc_negative_cache.add(subclass) | ||||
return False | ||||