9
9
from abc import ABCMeta , abstractmethod
10
10
import sys
11
11
12
- __all__ = ["Awaitable" , "Coroutine" , "AsyncIterable" , "AsyncIterator" ,
13
- "Hashable" , "Iterable" , "Iterator" , "Generator" ,
14
- "Sized" , "Container" , "Callable" ,
12
+ __all__ = ["Awaitable" , "Coroutine" ,
13
+ "AsyncIterable" , "AsyncIterator" , "AsyncGenerator" ,
14
+ "Hashable" , "Iterable" , "Iterator" , "Generator" , "Reversible" ,
15
+ "Sized" , "Container" , "Callable" , "Collection" ,
15
16
"Set" , "MutableSet" ,
16
17
"Mapping" , "MutableMapping" ,
17
18
"MappingView" , "KeysView" , "ItemsView" , "ValuesView" ,
@@ -59,10 +60,27 @@ async def _coro(): pass
59
60
coroutine = type (_coro )
60
61
_coro .close () # Prevent ResourceWarning
61
62
del _coro
63
+ ## asynchronous generator ##
64
+ async def _ag (): yield
65
+ _ag = _ag ()
66
+ async_generator = type (_ag )
67
+ del _ag
62
68
63
69
64
70
### ONE-TRICK PONIES ###
65
71
72
+ def _check_methods (C , * methods ):
73
+ mro = C .__mro__
74
+ for method in methods :
75
+ for B in mro :
76
+ if method in B .__dict__ :
77
+ if B .__dict__ [method ] is None :
78
+ return NotImplemented
79
+ break
80
+ else :
81
+ return NotImplemented
82
+ return True
83
+
66
84
class Hashable (metaclass = ABCMeta ):
67
85
68
86
__slots__ = ()
@@ -74,11 +92,7 @@ def __hash__(self):
74
92
@classmethod
75
93
def __subclasshook__ (cls , C ):
76
94
if cls is Hashable :
77
- for B in C .__mro__ :
78
- if "__hash__" in B .__dict__ :
79
- if B .__dict__ ["__hash__" ]:
80
- return True
81
- break
95
+ return _check_methods (C , "__hash__" )
82
96
return NotImplemented
83
97
84
98
@@ -93,11 +107,7 @@ def __await__(self):
93
107
@classmethod
94
108
def __subclasshook__ (cls , C ):
95
109
if cls is Awaitable :
96
- for B in C .__mro__ :
97
- if "__await__" in B .__dict__ :
98
- if B .__dict__ ["__await__" ]:
99
- return True
100
- break
110
+ return _check_methods (C , "__await__" )
101
111
return NotImplemented
102
112
103
113
@@ -138,14 +148,7 @@ def close(self):
138
148
@classmethod
139
149
def __subclasshook__ (cls , C ):
140
150
if cls is Coroutine :
141
- mro = C .__mro__
142
- for method in ('__await__' , 'send' , 'throw' , 'close' ):
143
- for base in mro :
144
- if method in base .__dict__ :
145
- break
146
- else :
147
- return NotImplemented
148
- return True
151
+ return _check_methods (C , '__await__' , 'send' , 'throw' , 'close' )
149
152
return NotImplemented
150
153
151
154
@@ -163,8 +166,7 @@ def __aiter__(self):
163
166
@classmethod
164
167
def __subclasshook__ (cls , C ):
165
168
if cls is AsyncIterable :
166
- if any ("__aiter__" in B .__dict__ for B in C .__mro__ ):
167
- return True
169
+ return _check_methods (C , "__aiter__" )
168
170
return NotImplemented
169
171
170
172
@@ -183,12 +185,61 @@ def __aiter__(self):
183
185
@classmethod
184
186
def __subclasshook__ (cls , C ):
185
187
if cls is AsyncIterator :
186
- if (any ("__anext__" in B .__dict__ for B in C .__mro__ ) and
187
- any ("__aiter__" in B .__dict__ for B in C .__mro__ )):
188
- return True
188
+ return _check_methods (C , "__anext__" , "__aiter__" )
189
189
return NotImplemented
190
190
191
191
192
+ class AsyncGenerator (AsyncIterator ):
193
+
194
+ __slots__ = ()
195
+
196
+ async def __anext__ (self ):
197
+ """Return the next item from the asynchronous generator.
198
+ When exhausted, raise StopAsyncIteration.
199
+ """
200
+ return await self .asend (None )
201
+
202
+ @abstractmethod
203
+ async def asend (self , value ):
204
+ """Send a value into the asynchronous generator.
205
+ Return next yielded value or raise StopAsyncIteration.
206
+ """
207
+ raise StopAsyncIteration
208
+
209
+ @abstractmethod
210
+ async def athrow (self , typ , val = None , tb = None ):
211
+ """Raise an exception in the asynchronous generator.
212
+ Return next yielded value or raise StopAsyncIteration.
213
+ """
214
+ if val is None :
215
+ if tb is None :
216
+ raise typ
217
+ val = typ ()
218
+ if tb is not None :
219
+ val = val .with_traceback (tb )
220
+ raise val
221
+
222
+ async def aclose (self ):
223
+ """Raise GeneratorExit inside coroutine.
224
+ """
225
+ try :
226
+ await self .athrow (GeneratorExit )
227
+ except (GeneratorExit , StopAsyncIteration ):
228
+ pass
229
+ else :
230
+ raise RuntimeError ("asynchronous generator ignored GeneratorExit" )
231
+
232
+ @classmethod
233
+ def __subclasshook__ (cls , C ):
234
+ if cls is AsyncGenerator :
235
+ return _check_methods (C , '__aiter__' , '__anext__' ,
236
+ 'asend' , 'athrow' , 'aclose' )
237
+ return NotImplemented
238
+
239
+
240
+ AsyncGenerator .register (async_generator )
241
+
242
+
192
243
class Iterable (metaclass = ABCMeta ):
193
244
194
245
__slots__ = ()
@@ -201,8 +252,7 @@ def __iter__(self):
201
252
@classmethod
202
253
def __subclasshook__ (cls , C ):
203
254
if cls is Iterable :
204
- if any ("__iter__" in B .__dict__ for B in C .__mro__ ):
205
- return True
255
+ return _check_methods (C , "__iter__" )
206
256
return NotImplemented
207
257
208
258
@@ -221,9 +271,7 @@ def __iter__(self):
221
271
@classmethod
222
272
def __subclasshook__ (cls , C ):
223
273
if cls is Iterator :
224
- if (any ("__next__" in B .__dict__ for B in C .__mro__ ) and
225
- any ("__iter__" in B .__dict__ for B in C .__mro__ )):
226
- return True
274
+ return _check_methods (C , '__iter__' , '__next__' )
227
275
return NotImplemented
228
276
229
277
Iterator .register (bytes_iterator )
@@ -242,6 +290,22 @@ def __subclasshook__(cls, C):
242
290
Iterator .register (zip_iterator )
243
291
244
292
293
+ class Reversible (Iterable ):
294
+
295
+ __slots__ = ()
296
+
297
+ @abstractmethod
298
+ def __reversed__ (self ):
299
+ while False :
300
+ yield None
301
+
302
+ @classmethod
303
+ def __subclasshook__ (cls , C ):
304
+ if cls is Reversible :
305
+ return _check_methods (C , "__reversed__" , "__iter__" )
306
+ return NotImplemented
307
+
308
+
245
309
class Generator (Iterator ):
246
310
247
311
__slots__ = ()
@@ -285,17 +349,10 @@ def close(self):
285
349
@classmethod
286
350
def __subclasshook__ (cls , C ):
287
351
if cls is Generator :
288
- mro = C .__mro__
289
- for method in ('__iter__' , '__next__' , 'send' , 'throw' , 'close' ):
290
- for base in mro :
291
- if method in base .__dict__ :
292
- break
293
- else :
294
- return NotImplemented
295
- return True
352
+ return _check_methods (C , '__iter__' , '__next__' ,
353
+ 'send' , 'throw' , 'close' )
296
354
return NotImplemented
297
355
298
-
299
356
Generator .register (generator )
300
357
301
358
@@ -310,8 +367,7 @@ def __len__(self):
310
367
@classmethod
311
368
def __subclasshook__ (cls , C ):
312
369
if cls is Sized :
313
- if any ("__len__" in B .__dict__ for B in C .__mro__ ):
314
- return True
370
+ return _check_methods (C , "__len__" )
315
371
return NotImplemented
316
372
317
373
@@ -326,10 +382,18 @@ def __contains__(self, x):
326
382
@classmethod
327
383
def __subclasshook__ (cls , C ):
328
384
if cls is Container :
329
- if any ("__contains__" in B .__dict__ for B in C .__mro__ ):
330
- return True
385
+ return _check_methods (C , "__contains__" )
331
386
return NotImplemented
332
387
388
+ class Collection (Sized , Iterable , Container ):
389
+
390
+ __slots__ = ()
391
+
392
+ @classmethod
393
+ def __subclasshook__ (cls , C ):
394
+ if cls is Collection :
395
+ return _check_methods (C , "__len__" , "__iter__" , "__contains__" )
396
+ return NotImplemented
333
397
334
398
class Callable (metaclass = ABCMeta ):
335
399
@@ -342,15 +406,14 @@ def __call__(self, *args, **kwds):
342
406
@classmethod
343
407
def __subclasshook__ (cls , C ):
344
408
if cls is Callable :
345
- if any ("__call__" in B .__dict__ for B in C .__mro__ ):
346
- return True
409
+ return _check_methods (C , "__call__" )
347
410
return NotImplemented
348
411
349
412
350
413
### SETS ###
351
414
352
415
353
- class Set (Sized , Iterable , Container ):
416
+ class Set (Collection ):
354
417
355
418
"""A set is a finite, iterable container.
356
419
@@ -575,7 +638,7 @@ def __isub__(self, it):
575
638
### MAPPINGS ###
576
639
577
640
578
- class Mapping (Sized , Iterable , Container ):
641
+ class Mapping (Collection ):
579
642
580
643
__slots__ = ()
581
644
@@ -623,6 +686,8 @@ def __eq__(self, other):
623
686
return NotImplemented
624
687
return dict (self .items ()) == dict (other .items ())
625
688
689
+ __reversed__ = None
690
+
626
691
Mapping .register (mappingproxy )
627
692
628
693
@@ -672,7 +737,7 @@ def __contains__(self, item):
672
737
except KeyError :
673
738
return False
674
739
else :
675
- return v == value
740
+ return v is value or v == value
676
741
677
742
def __iter__ (self ):
678
743
for key in self ._mapping :
@@ -687,7 +752,8 @@ class ValuesView(MappingView):
687
752
688
753
def __contains__ (self , value ):
689
754
for key in self ._mapping :
690
- if value == self ._mapping [key ]:
755
+ v = self ._mapping [key ]
756
+ if v is value or v == value :
691
757
return True
692
758
return False
693
759
@@ -796,7 +862,7 @@ def setdefault(self, key, default=None):
796
862
### SEQUENCES ###
797
863
798
864
799
- class Sequence (Sized , Iterable , Container ):
865
+ class Sequence (Reversible , Collection ):
800
866
801
867
"""All the operations on a read-only sequence.
802
868
@@ -822,7 +888,7 @@ def __iter__(self):
822
888
823
889
def __contains__ (self , value ):
824
890
for v in self :
825
- if v == value :
891
+ if v is value or v == value :
826
892
return True
827
893
return False
828
894
@@ -833,6 +899,9 @@ def __reversed__(self):
833
899
def index (self , value , start = 0 , stop = None ):
834
900
'''S.index(value, [start, [stop]]) -> integer -- return first index of value.
835
901
Raises ValueError if the value is not present.
902
+
903
+ Supporting start and stop arguments is optional, but
904
+ recommended.
836
905
'''
837
906
if start is not None and start < 0 :
838
907
start = max (len (self ) + start , 0 )
@@ -842,7 +911,8 @@ def index(self, value, start=0, stop=None):
842
911
i = start
843
912
while stop is None or i < stop :
844
913
try :
845
- if self [i ] == value :
914
+ v = self [i ]
915
+ if v is value or v == value :
846
916
return i
847
917
except IndexError :
848
918
break
@@ -851,7 +921,7 @@ def index(self, value, start=0, stop=None):
851
921
852
922
def count (self , value ):
853
923
'S.count(value) -> integer -- return number of occurrences of value'
854
- return sum (1 for v in self if v == value )
924
+ return sum (1 for v in self if v is value or v == value )
855
925
856
926
Sequence .register (tuple )
857
927
Sequence .register (str )
0 commit comments