mirror of
https://github.com/python/cpython.git
synced 2024-11-25 10:54:51 +08:00
9e7f1d2e96
svn+ssh://pythondev@svn.python.org/python/trunk ........ r61098 | jeffrey.yasskin | 2008-02-28 05:45:36 +0100 (Thu, 28 Feb 2008) | 7 lines Move abc._Abstract into object by adding a new flag Py_TPFLAGS_IS_ABSTRACT, which forbids constructing types that have it set. The effect is to speed ./python.exe -m timeit -s 'import abc' -s 'class Foo(object): __metaclass__ = abc.ABCMeta' 'Foo()' up from 2.5us to 0.201us. This fixes issue 1762. ........ r61099 | jeffrey.yasskin | 2008-02-28 06:53:18 +0100 (Thu, 28 Feb 2008) | 3 lines Speed test_socketserver up from 28.739s to 0.226s, simplify the logic, and make sure all tests run even if some fail. ........ r61100 | jeffrey.yasskin | 2008-02-28 07:09:19 +0100 (Thu, 28 Feb 2008) | 21 lines Thread.start() used sleep(0.000001) to make sure it didn't return before the new thread had started. At least on my MacBook Pro, that wound up sleeping for a full 10ms (probably 1 jiffy). By using an Event instead, we can be absolutely certain that the thread has started, and return more quickly (217us). Before: $ ./python.exe -m timeit -s 'from threading import Thread' 't = Thread(); t.start(); t.join()' 100 loops, best of 3: 10.3 msec per loop $ ./python.exe -m timeit -s 'from threading import Thread; t = Thread()' 't.isAlive()' 1000000 loops, best of 3: 0.47 usec per loop After: $ ./python.exe -m timeit -s 'from threading import Thread' 't = Thread(); t.start(); t.join()' 1000 loops, best of 3: 217 usec per loop $ ./python.exe -m timeit -s 'from threading import Thread; t = Thread()' 't.isAlive()' 1000000 loops, best of 3: 0.86 usec per loop To be fair, the 10ms isn't CPU time, and other threads including the spawned one get to run during it. There are also some slightly more complicated ways to get back the .4us in isAlive() if we want. ........ r61101 | raymond.hettinger | 2008-02-28 10:23:48 +0100 (Thu, 28 Feb 2008) | 1 line Add repeat keyword argument to itertools.product(). ........ r61102 | christian.heimes | 2008-02-28 12:18:49 +0100 (Thu, 28 Feb 2008) | 1 line The empty tuple is usually a singleton with a much higher refcnt than 1 ........
172 lines
6.6 KiB
Python
172 lines
6.6 KiB
Python
# 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 the normal
|
|
'super' call mechanisms.
|
|
|
|
Usage:
|
|
|
|
class C(metaclass=ABCMeta):
|
|
@abstractmethod
|
|
def my_abstract_method(self, ...):
|
|
...
|
|
"""
|
|
funcobj.__isabstractmethod__ = True
|
|
return funcobj
|
|
|
|
|
|
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 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)
|
|
"""
|
|
__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."""
|
|
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")
|
|
cls._abc_registry.add(subclass)
|
|
ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
|
|
|
|
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_registry.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_registry.add(subclass)
|
|
return True
|
|
# No dice; update negative cache
|
|
cls._abc_negative_cache.add(subclass)
|
|
return False
|