Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(207045)

Delta Between Two Patch Sets: Lib/abc.py

Issue 11610: Improving property to accept abstract methods
Left Patch Set: Created 8 years, 6 months ago
Right Patch Set: Created 8 years ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « Include/object.h ('k') | Lib/numbers.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # Copyright 2007 Google, Inc. All Rights Reserved. 1 # Copyright 2007 Google, Inc. All Rights Reserved.
2 # Licensed to PSF under a Contributor Agreement. 2 # Licensed to PSF under a Contributor Agreement.
3 3
4 """Abstract Base Classes (ABCs) according to PEP 3119.""" 4 """Abstract Base Classes (ABCs) according to PEP 3119."""
5 5
6 from _weakrefset import WeakSet 6 from _weakrefset import WeakSet
7 7
8 def abstractmethod(funcobj): 8 def abstractmethod(funcobj):
9 """A decorator indicating abstract methods. 9 """A decorator indicating abstract methods.
10 10
11 Requires that the metaclass is ABCMeta or derived from it. A 11 Requires that the metaclass is ABCMeta or derived from it. A
12 class that has a metaclass derived from ABCMeta cannot be 12 class that has a metaclass derived from ABCMeta cannot be
13 instantiated unless all of its abstract methods are overridden. 13 instantiated unless all of its abstract methods are overridden.
14 abstractmethod can be used to specify abstract methods for 14 The abstract methods can be called using any of the normal
15 properties and descriptors. The abstract methods can be called 15 'super' call mechanisms.
16 using any of the normal 'super' call mechanisms.
17 16
18 Usage: 17 Usage:
19 18
20 class C(metaclass=ABCMeta): 19 class C(metaclass=ABCMeta):
21 @abstractmethod 20 @abstractmethod
22 def my_abstract_method(self, ...): 21 def my_abstract_method(self, ...):
23 ... 22 ...
24
25 @property
26 @abstractmethod
27 def my_abstract_property(self):
28 ...
29 @my_abstract_property.setter
30 @abstractmethod
31 def my_abstract_property(self, val):
32 ...
33
34 @abstractmethod
35 def _get_x(self):
36 ...
37 @abstractmethod
38 def _set_x(self, val):
39 ...
40 x = property(_get_x, _set_x)
41
42 """ 23 """
43 funcobj.__isabstractmethod__ = True 24 funcobj.__isabstractmethod__ = True
44 return funcobj 25 return funcobj
45 26
46 27
47 class abstractclassmethod(classmethod): 28 class abstractclassmethod(classmethod):
48 """A decorator indicating abstract classmethods. 29 """
30 A decorator indicating abstract classmethods.
49 31
50 Similar to abstractmethod. 32 Similar to abstractmethod.
51 33
52 Usage: 34 Usage:
53 35
54 class C(metaclass=ABCMeta): 36 class C(metaclass=ABCMeta):
55 @abstractclassmethod 37 @abstractclassmethod
56 def my_abstract_classmethod(cls, ...): 38 def my_abstract_classmethod(cls, ...):
57 ... 39 ...
40
41 'abstractclassmethod' is deprecated. Use 'classmethod' with
42 'abstractmethod' instead.
58 """ 43 """
59 44
60 __isabstractmethod__ = True 45 __isabstractmethod__ = True
61 46
62 def __init__(self, callable): 47 def __init__(self, callable):
63 callable.__isabstractmethod__ = True 48 callable.__isabstractmethod__ = True
64 super().__init__(callable) 49 super().__init__(callable)
65 50
66 51
67 class abstractstaticmethod(staticmethod): 52 class abstractstaticmethod(staticmethod):
68 """A decorator indicating abstract staticmethods. 53 """
54 A decorator indicating abstract staticmethods.
69 55
70 Similar to abstractmethod. 56 Similar to abstractmethod.
71 57
72 Usage: 58 Usage:
73 59
74 class C(metaclass=ABCMeta): 60 class C(metaclass=ABCMeta):
75 @abstractstaticmethod 61 @abstractstaticmethod
76 def my_abstract_staticmethod(...): 62 def my_abstract_staticmethod(...):
77 ... 63 ...
64
65 'abstractstaticmethod' is deprecated. Use 'staticmethod' with
66 'abstractmethod' instead.
78 """ 67 """
79 68
80 __isabstractmethod__ = True 69 __isabstractmethod__ = True
81 70
82 def __init__(self, callable): 71 def __init__(self, callable):
83 callable.__isabstractmethod__ = True 72 callable.__isabstractmethod__ = True
84 super().__init__(callable) 73 super().__init__(callable)
85 74
86 75
87 class abstractproperty(property): 76 class abstractproperty(property):
88 """ 77 """
89 .. deprecated:: 3.3
90 Use :class:`property` and :func:`abstractmethod` instead.
91
92 A decorator indicating abstract properties. 78 A decorator indicating abstract properties.
93 79
94 Requires that the metaclass is ABCMeta or derived from it. A 80 Requires that the metaclass is ABCMeta or derived from it. A
95 class that has a metaclass derived from ABCMeta cannot be 81 class that has a metaclass derived from ABCMeta cannot be
96 instantiated unless all of its abstract properties are overridden. 82 instantiated unless all of its abstract properties are overridden.
97 The abstract properties can be called using any of the normal 83 The abstract properties can be called using any of the normal
98 'super' call mechanisms. 84 'super' call mechanisms.
99 85
100 Usage: 86 Usage:
101 87
102 class C(metaclass=ABCMeta): 88 class C(metaclass=ABCMeta):
103 @abstractproperty 89 @abstractproperty
104 def my_abstract_property(self): 90 def my_abstract_property(self):
105 ... 91 ...
106 92
107 This defines a read-only property; you can also define a read-write 93 This defines a read-only property; you can also define a read-write
108 abstract property using the 'long' form of property declaration: 94 abstract property using the 'long' form of property declaration:
109 95
110 class C(metaclass=ABCMeta): 96 class C(metaclass=ABCMeta):
111 def getx(self): ... 97 def getx(self): ...
112 def setx(self, value): ... 98 def setx(self, value): ...
113 x = abstractproperty(getx, setx) 99 x = abstractproperty(getx, setx)
114 100
115 """ 101 'abstractproperty' is deprecated. Use 'property' with 'abstractmethod'
102 instead.
103 """
104
116 __isabstractmethod__ = True 105 __isabstractmethod__ = True
117
118 def __init__(self, *args, **kwargs):
119 import warnings
120 warnings.warn("abstractproperty is deprecated", DeprecationWarning, 2)
121 super().__init__(*args, **kwargs)
122 106
123 107
124 class ABCMeta(type): 108 class ABCMeta(type):
125 109
126 """Metaclass for defining Abstract Base Classes (ABCs). 110 """Metaclass for defining Abstract Base Classes (ABCs).
127 111
128 Use this metaclass to create an ABC. An ABC can be subclassed 112 Use this metaclass to create an ABC. An ABC can be subclassed
129 directly, and then acts as a mix-in class. You can also register 113 directly, and then acts as a mix-in class. You can also register
130 unrelated concrete classes (even built-in classes) and unrelated 114 unrelated concrete classes (even built-in classes) and unrelated
131 ABCs as 'virtual subclasses' -- these and their descendants will 115 ABCs as 'virtual subclasses' -- these and their descendants will
132 be considered subclasses of the registering ABC by the built-in 116 be considered subclasses of the registering ABC by the built-in
133 issubclass() function, but the registering ABC won't show up in 117 issubclass() function, but the registering ABC won't show up in
134 their MRO (Method Resolution Order) nor will method 118 their MRO (Method Resolution Order) nor will method
135 implementations defined by the registering ABC be callable (not 119 implementations defined by the registering ABC be callable (not
136 even via super()). 120 even via super()).
137 121
138 """ 122 """
139 123
140 # A global counter that is incremented each time a class is 124 # A global counter that is incremented each time a class is
141 # registered as a virtual subclass of anything. It forces the 125 # registered as a virtual subclass of anything. It forces the
142 # negative cache to be cleared before its next use. 126 # negative cache to be cleared before its next use.
143 _abc_invalidation_counter = 0 127 _abc_invalidation_counter = 0
144 128
145 def __new__(mcls, name, bases, namespace): 129 def __new__(mcls, name, bases, namespace):
146 cls = super().__new__(mcls, name, bases, namespace) 130 cls = super().__new__(mcls, name, bases, namespace)
147
148 # Compute set of abstract method names 131 # Compute set of abstract method names
149 def is_descriptor(value): 132 abstracts = {name
150 return (hasattr(value, '__get__') or hasattr(value, '__set__') 133 for name, value in namespace.items()
151 or hasattr(value, '__delete__')) 134 if getattr(value, "__isabstractmethod__", False)}
152 def is_abstract(value):
153 return getattr(value, "__isabstractmethod__", False)
154 def get_abstract_names_for_item(item):
155 name, value = item
156 if is_abstract(value):
157 return [name]
158 elif is_descriptor(value):
159 return ['{}.{}'.format(name, attr) for attr in dir(value)
160 if is_abstract(getattr(value, attr))]
161 return []
162
163 abstract_names = []
164 for item in namespace.items():
165 abstract_names.extend(get_abstract_names_for_item(item))
166
167 for base in bases: 135 for base in bases:
168 for name in getattr(base, "__abstractmethods__", ()): 136 for name in getattr(base, "__abstractmethods__", set()):
169 descr_name, is_descr, attr = name.rpartition('.') 137 value = getattr(cls, name, None)
170 if is_descr: 138 if getattr(value, "__isabstractmethod__", False):
171 # base class identified a descriptor abstract method: 139 abstracts.add(name)
172 descr = getattr(cls, descr_name, None) 140 cls.__abstractmethods__ = frozenset(abstracts)
173 val = getattr(descr, attr, None)
174 else:
175 val = getattr(cls, name, None)
176 if val is None or is_abstract(val):
177 abstract_names.append(name)
178
179 cls.__abstractmethods__ = frozenset(abstract_names)
180
181 # Set up inheritance registry 141 # Set up inheritance registry
182 cls._abc_registry = WeakSet() 142 cls._abc_registry = WeakSet()
183 cls._abc_cache = WeakSet() 143 cls._abc_cache = WeakSet()
184 cls._abc_negative_cache = WeakSet() 144 cls._abc_negative_cache = WeakSet()
185 cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter 145 cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
186 return cls 146 return cls
187 147
188 def register(cls, subclass): 148 def register(cls, subclass):
189 """Register a virtual subclass of an ABC. 149 """Register a virtual subclass of an ABC.
190 150
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 cls._abc_cache.add(subclass) 219 cls._abc_cache.add(subclass)
260 return True 220 return True
261 # Check if it's a subclass of a subclass (recursive) 221 # Check if it's a subclass of a subclass (recursive)
262 for scls in cls.__subclasses__(): 222 for scls in cls.__subclasses__():
263 if issubclass(subclass, scls): 223 if issubclass(subclass, scls):
264 cls._abc_cache.add(subclass) 224 cls._abc_cache.add(subclass)
265 return True 225 return True
266 # No dice; update negative cache 226 # No dice; update negative cache
267 cls._abc_negative_cache.add(subclass) 227 cls._abc_negative_cache.add(subclass)
268 return False 228 return False
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+