| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  | # Copyright 2007 Google, Inc. All Rights Reserved. | 
					
						
							|  |  |  | # Licensed to PSF under a Contributor Agreement. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """Abstract Base Classes (ABCs) according to PEP 3119.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-23 06:26:46 +00:00
										 |  |  | from weakref import WeakSet | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 the normal | 
					
						
							|  |  |  |     'super' call mechanisms. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Usage: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class C(metaclass=ABCMeta): | 
					
						
							|  |  |  |             @abstractmethod | 
					
						
							|  |  |  |             def my_abstract_method(self, ...): | 
					
						
							|  |  |  |                 ... | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     funcobj.__isabstractmethod__ = True | 
					
						
							|  |  |  |     return funcobj | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-01 17:32:28 +00:00
										 |  |  | 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. | 
					
						
							| 
									
										
										
										
											2007-08-01 17:52:23 +00:00
										 |  |  |     The abstract properties can be called using any of the the normal | 
					
						
							|  |  |  |     'super' call mechanisms. | 
					
						
							| 
									
										
										
										
											2007-08-01 17:32:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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) | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     __isabstractmethod__ = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  | class _Abstract(object): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Helper class inserted into the bases by ABCMeta (using _fix_bases()).
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     You should never need to explicitly subclass this class. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __new__(cls, *args, **kwds): | 
					
						
							|  |  |  |         am = cls.__dict__.get("__abstractmethods__") | 
					
						
							|  |  |  |         if am: | 
					
						
							|  |  |  |             raise TypeError("Can't instantiate abstract class %s " | 
					
						
							|  |  |  |                             "with abstract methods %s" % | 
					
						
							|  |  |  |                             (cls.__name__, ", ".join(sorted(am)))) | 
					
						
							|  |  |  |         if (args or kwds) and cls.__init__ is object.__init__: | 
					
						
							|  |  |  |             raise TypeError("Can't pass arguments to __new__ " | 
					
						
							|  |  |  |                             "without overriding __init__") | 
					
						
							| 
									
										
										
										
											2007-09-11 20:42:30 +00:00
										 |  |  |         return super().__new__(cls) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def __subclasshook__(cls, subclass): | 
					
						
							|  |  |  |         """Abstract classes can override this to customize issubclass().
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This is invoked early on by __subclasscheck__() below.  It | 
					
						
							|  |  |  |         should return True, False or NotImplemented.  If it returns | 
					
						
							|  |  |  |         NotImplemented, the normal algorithm is used.  Otherwise, it | 
					
						
							|  |  |  |         overrides the normal algorithm (and the outcome is cached). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _fix_bases(bases): | 
					
						
							|  |  |  |     """Helper method that inserts _Abstract in the bases if needed.""" | 
					
						
							|  |  |  |     for base in bases: | 
					
						
							|  |  |  |         if issubclass(base, _Abstract): | 
					
						
							|  |  |  |             # _Abstract is already a base (maybe indirectly) | 
					
						
							|  |  |  |             return bases | 
					
						
							|  |  |  |     if object in bases: | 
					
						
							|  |  |  |         # Replace object with _Abstract | 
					
						
							|  |  |  |         return tuple([_Abstract if base is object else base | 
					
						
							|  |  |  |                       for base in bases]) | 
					
						
							|  |  |  |     # Append _Abstract to the end | 
					
						
							|  |  |  |     return bases + (_Abstract,) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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. | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |     _abc_invalidation_counter = 0 | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __new__(mcls, name, bases, namespace): | 
					
						
							|  |  |  |         bases = _fix_bases(bases) | 
					
						
							| 
									
										
										
										
											2007-06-14 03:27:55 +00:00
										 |  |  |         cls = super().__new__(mcls, name, bases, namespace) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |         # 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__ = abstracts | 
					
						
							|  |  |  |         # Set up inheritance registry | 
					
						
							| 
									
										
										
										
											2007-10-23 06:26:46 +00:00
										 |  |  |         cls._abc_registry = WeakSet() | 
					
						
							|  |  |  |         cls._abc_cache = WeakSet() | 
					
						
							|  |  |  |         cls._abc_negative_cache = WeakSet() | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |         cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |         return cls | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def register(cls, subclass): | 
					
						
							|  |  |  |         """Register a virtual subclass of an ABC.""" | 
					
						
							|  |  |  |         if not isinstance(cls, type): | 
					
						
							|  |  |  |             raise TypeError("Can only register classes") | 
					
						
							|  |  |  |         if issubclass(subclass, cls): | 
					
						
							|  |  |  |             return  # 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") | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |         cls._abc_registry.add(subclass) | 
					
						
							|  |  |  |         ABCMeta._abc_invalidation_counter += 1  # Invalidate negative cache | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _dump_registry(cls, file=None): | 
					
						
							|  |  |  |         """Debug helper to print the ABC registry.""" | 
					
						
							|  |  |  |         print("Class: %s.%s" % (cls.__module__, cls.__name__), file=file) | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |         print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |         for name in sorted(cls.__dict__.keys()): | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |             if name.startswith("_abc_"): | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |                 value = getattr(cls, name) | 
					
						
							|  |  |  |                 print("%s: %r" % (name, value), file=file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __instancecheck__(cls, instance): | 
					
						
							|  |  |  |         """Override for isinstance(instance, cls).""" | 
					
						
							|  |  |  |         return any(cls.__subclasscheck__(c) | 
					
						
							|  |  |  |                    for c in {instance.__class__, type(instance)}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __subclasscheck__(cls, subclass): | 
					
						
							|  |  |  |         """Override for issubclass(subclass, cls).""" | 
					
						
							|  |  |  |         # Check cache | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |         if subclass in cls._abc_cache: | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |             return True | 
					
						
							|  |  |  |         # Check negative cache; may have to invalidate | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |         if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter: | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |             # Invalidate the negative cache | 
					
						
							| 
									
										
										
										
											2007-10-23 06:26:46 +00:00
										 |  |  |             cls._abc_negative_cache = WeakSet() | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |             cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter | 
					
						
							|  |  |  |         elif subclass in cls._abc_negative_cache: | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |             return False | 
					
						
							|  |  |  |         # Check the subclass hook | 
					
						
							|  |  |  |         ok = cls.__subclasshook__(subclass) | 
					
						
							|  |  |  |         if ok is not NotImplemented: | 
					
						
							|  |  |  |             assert isinstance(ok, bool) | 
					
						
							|  |  |  |             if ok: | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |                 cls._abc_cache.add(subclass) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |                 cls._abc_negative_cache.add(subclass) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |             return ok | 
					
						
							|  |  |  |         # Check if it's a direct subclass | 
					
						
							|  |  |  |         if cls in subclass.__mro__: | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |             cls._abc_cache.add(subclass) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |             return True | 
					
						
							|  |  |  |         # Check if it's a subclass of a registered class (recursive) | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |         for rcls in cls._abc_registry: | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |             if issubclass(subclass, rcls): | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |                 cls._abc_registry.add(subclass) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |                 return True | 
					
						
							|  |  |  |         # Check if it's a subclass of a subclass (recursive) | 
					
						
							|  |  |  |         for scls in cls.__subclasses__(): | 
					
						
							|  |  |  |             if issubclass(subclass, scls): | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |                 cls._abc_registry.add(subclass) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |                 return True | 
					
						
							|  |  |  |         # No dice; update negative cache | 
					
						
							| 
									
										
										
										
											2007-08-20 19:29:24 +00:00
										 |  |  |         cls._abc_negative_cache.add(subclass) | 
					
						
							| 
									
										
										
										
											2007-06-14 00:03:37 +00:00
										 |  |  |         return False |