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" ,
@@ -61,7 +62,7 @@ async def _coro(): pass
61
62
# _coro.close() # Prevent ResourceWarning
62
63
del _coro
63
64
## asynchronous generator ##
64
- ## This should be reverted, once async generators are supported.
65
+ ## Truffle todo: This should be reverted, once async generators are supported.
65
66
## Temporary fix.
66
67
#async def _ag(): yield
67
68
#_ag = _ag()
@@ -71,6 +72,18 @@ async def _coro(): pass
71
72
72
73
### ONE-TRICK PONIES ###
73
74
75
+ def _check_methods (C , * methods ):
76
+ mro = C .__mro__
77
+ for method in methods :
78
+ for B in mro :
79
+ if method in B .__dict__ :
80
+ if B .__dict__ [method ] is None :
81
+ return NotImplemented
82
+ break
83
+ else :
84
+ return NotImplemented
85
+ return True
86
+
74
87
class Hashable (metaclass = ABCMeta ):
75
88
76
89
__slots__ = ()
@@ -82,11 +95,7 @@ def __hash__(self):
82
95
@classmethod
83
96
def __subclasshook__ (cls , C ):
84
97
if cls is Hashable :
85
- for B in C .__mro__ :
86
- if "__hash__" in B .__dict__ :
87
- if B .__dict__ ["__hash__" ]:
88
- return True
89
- break
98
+ return _check_methods (C , "__hash__" )
90
99
return NotImplemented
91
100
92
101
@@ -101,11 +110,7 @@ def __await__(self):
101
110
@classmethod
102
111
def __subclasshook__ (cls , C ):
103
112
if cls is Awaitable :
104
- for B in C .__mro__ :
105
- if "__await__" in B .__dict__ :
106
- if B .__dict__ ["__await__" ]:
107
- return True
108
- break
113
+ return _check_methods (C , "__await__" )
109
114
return NotImplemented
110
115
111
116
@@ -146,14 +151,7 @@ def close(self):
146
151
@classmethod
147
152
def __subclasshook__ (cls , C ):
148
153
if cls is Coroutine :
149
- mro = C .__mro__
150
- for method in ('__await__' , 'send' , 'throw' , 'close' ):
151
- for base in mro :
152
- if method in base .__dict__ :
153
- break
154
- else :
155
- return NotImplemented
156
- return True
154
+ return _check_methods (C , '__await__' , 'send' , 'throw' , 'close' )
157
155
return NotImplemented
158
156
159
157
@@ -171,8 +169,7 @@ def __aiter__(self):
171
169
@classmethod
172
170
def __subclasshook__ (cls , C ):
173
171
if cls is AsyncIterable :
174
- if any ("__aiter__" in B .__dict__ for B in C .__mro__ ):
175
- return True
172
+ return _check_methods (C , "__aiter__" )
176
173
return NotImplemented
177
174
178
175
@@ -191,12 +188,61 @@ def __aiter__(self):
191
188
@classmethod
192
189
def __subclasshook__ (cls , C ):
193
190
if cls is AsyncIterator :
194
- if (any ("__anext__" in B .__dict__ for B in C .__mro__ ) and
195
- any ("__aiter__" in B .__dict__ for B in C .__mro__ )):
196
- return True
191
+ return _check_methods (C , "__anext__" , "__aiter__" )
197
192
return NotImplemented
198
193
199
194
195
+ class AsyncGenerator (AsyncIterator ):
196
+
197
+ __slots__ = ()
198
+
199
+ async def __anext__ (self ):
200
+ """Return the next item from the asynchronous generator.
201
+ When exhausted, raise StopAsyncIteration.
202
+ """
203
+ return await self .asend (None )
204
+
205
+ @abstractmethod
206
+ async def asend (self , value ):
207
+ """Send a value into the asynchronous generator.
208
+ Return next yielded value or raise StopAsyncIteration.
209
+ """
210
+ raise StopAsyncIteration
211
+
212
+ @abstractmethod
213
+ async def athrow (self , typ , val = None , tb = None ):
214
+ """Raise an exception in the asynchronous generator.
215
+ Return next yielded value or raise StopAsyncIteration.
216
+ """
217
+ if val is None :
218
+ if tb is None :
219
+ raise typ
220
+ val = typ ()
221
+ if tb is not None :
222
+ val = val .with_traceback (tb )
223
+ raise val
224
+
225
+ async def aclose (self ):
226
+ """Raise GeneratorExit inside coroutine.
227
+ """
228
+ try :
229
+ await self .athrow (GeneratorExit )
230
+ except (GeneratorExit , StopAsyncIteration ):
231
+ pass
232
+ else :
233
+ raise RuntimeError ("asynchronous generator ignored GeneratorExit" )
234
+
235
+ @classmethod
236
+ def __subclasshook__ (cls , C ):
237
+ if cls is AsyncGenerator :
238
+ return _check_methods (C , '__aiter__' , '__anext__' ,
239
+ 'asend' , 'athrow' , 'aclose' )
240
+ return NotImplemented
241
+
242
+
243
+ AsyncGenerator .register (async_generator )
244
+
245
+
200
246
class Iterable (metaclass = ABCMeta ):
201
247
202
248
__slots__ = ()
@@ -209,8 +255,7 @@ def __iter__(self):
209
255
@classmethod
210
256
def __subclasshook__ (cls , C ):
211
257
if cls is Iterable :
212
- if any ("__iter__" in B .__dict__ for B in C .__mro__ ):
213
- return True
258
+ return _check_methods (C , "__iter__" )
214
259
return NotImplemented
215
260
216
261
@@ -229,9 +274,7 @@ def __iter__(self):
229
274
@classmethod
230
275
def __subclasshook__ (cls , C ):
231
276
if cls is Iterator :
232
- if (any ("__next__" in B .__dict__ for B in C .__mro__ ) and
233
- any ("__iter__" in B .__dict__ for B in C .__mro__ )):
234
- return True
277
+ return _check_methods (C , '__iter__' , '__next__' )
235
278
return NotImplemented
236
279
237
280
Iterator .register (bytes_iterator )
@@ -250,6 +293,22 @@ def __subclasshook__(cls, C):
250
293
Iterator .register (zip_iterator )
251
294
252
295
296
+ class Reversible (Iterable ):
297
+
298
+ __slots__ = ()
299
+
300
+ @abstractmethod
301
+ def __reversed__ (self ):
302
+ while False :
303
+ yield None
304
+
305
+ @classmethod
306
+ def __subclasshook__ (cls , C ):
307
+ if cls is Reversible :
308
+ return _check_methods (C , "__reversed__" , "__iter__" )
309
+ return NotImplemented
310
+
311
+
253
312
class Generator (Iterator ):
254
313
255
314
__slots__ = ()
@@ -293,17 +352,10 @@ def close(self):
293
352
@classmethod
294
353
def __subclasshook__ (cls , C ):
295
354
if cls is Generator :
296
- mro = C .__mro__
297
- for method in ('__iter__' , '__next__' , 'send' , 'throw' , 'close' ):
298
- for base in mro :
299
- if method in base .__dict__ :
300
- break
301
- else :
302
- return NotImplemented
303
- return True
355
+ return _check_methods (C , '__iter__' , '__next__' ,
356
+ 'send' , 'throw' , 'close' )
304
357
return NotImplemented
305
358
306
-
307
359
Generator .register (generator )
308
360
309
361
@@ -318,8 +370,7 @@ def __len__(self):
318
370
@classmethod
319
371
def __subclasshook__ (cls , C ):
320
372
if cls is Sized :
321
- if any ("__len__" in B .__dict__ for B in C .__mro__ ):
322
- return True
373
+ return _check_methods (C , "__len__" )
323
374
return NotImplemented
324
375
325
376
@@ -334,10 +385,18 @@ def __contains__(self, x):
334
385
@classmethod
335
386
def __subclasshook__ (cls , C ):
336
387
if cls is Container :
337
- if any ("__contains__" in B .__dict__ for B in C .__mro__ ):
338
- return True
388
+ return _check_methods (C , "__contains__" )
339
389
return NotImplemented
340
390
391
+ class Collection (Sized , Iterable , Container ):
392
+
393
+ __slots__ = ()
394
+
395
+ @classmethod
396
+ def __subclasshook__ (cls , C ):
397
+ if cls is Collection :
398
+ return _check_methods (C , "__len__" , "__iter__" , "__contains__" )
399
+ return NotImplemented
341
400
342
401
class Callable (metaclass = ABCMeta ):
343
402
@@ -350,15 +409,14 @@ def __call__(self, *args, **kwds):
350
409
@classmethod
351
410
def __subclasshook__ (cls , C ):
352
411
if cls is Callable :
353
- if any ("__call__" in B .__dict__ for B in C .__mro__ ):
354
- return True
412
+ return _check_methods (C , "__call__" )
355
413
return NotImplemented
356
414
357
415
358
416
### SETS ###
359
417
360
418
361
- class Set (Sized , Iterable , Container ):
419
+ class Set (Collection ):
362
420
363
421
"""A set is a finite, iterable container.
364
422
@@ -583,7 +641,7 @@ def __isub__(self, it):
583
641
### MAPPINGS ###
584
642
585
643
586
- class Mapping (Sized , Iterable , Container ):
644
+ class Mapping (Collection ):
587
645
588
646
__slots__ = ()
589
647
@@ -631,6 +689,8 @@ def __eq__(self, other):
631
689
return NotImplemented
632
690
return dict (self .items ()) == dict (other .items ())
633
691
692
+ __reversed__ = None
693
+
634
694
Mapping .register (mappingproxy )
635
695
636
696
@@ -680,7 +740,7 @@ def __contains__(self, item):
680
740
except KeyError :
681
741
return False
682
742
else :
683
- return v == value
743
+ return v is value or v == value
684
744
685
745
def __iter__ (self ):
686
746
for key in self ._mapping :
@@ -695,7 +755,8 @@ class ValuesView(MappingView):
695
755
696
756
def __contains__ (self , value ):
697
757
for key in self ._mapping :
698
- if value == self ._mapping [key ]:
758
+ v = self ._mapping [key ]
759
+ if v is value or v == value :
699
760
return True
700
761
return False
701
762
@@ -804,7 +865,7 @@ def setdefault(self, key, default=None):
804
865
### SEQUENCES ###
805
866
806
867
807
- class Sequence (Sized , Iterable , Container ):
868
+ class Sequence (Reversible , Collection ):
808
869
809
870
"""All the operations on a read-only sequence.
810
871
@@ -830,7 +891,7 @@ def __iter__(self):
830
891
831
892
def __contains__ (self , value ):
832
893
for v in self :
833
- if v == value :
894
+ if v is value or v == value :
834
895
return True
835
896
return False
836
897
@@ -841,6 +902,9 @@ def __reversed__(self):
841
902
def index (self , value , start = 0 , stop = None ):
842
903
'''S.index(value, [start, [stop]]) -> integer -- return first index of value.
843
904
Raises ValueError if the value is not present.
905
+
906
+ Supporting start and stop arguments is optional, but
907
+ recommended.
844
908
'''
845
909
if start is not None and start < 0 :
846
910
start = max (len (self ) + start , 0 )
@@ -850,7 +914,8 @@ def index(self, value, start=0, stop=None):
850
914
i = start
851
915
while stop is None or i < stop :
852
916
try :
853
- if self [i ] == value :
917
+ v = self [i ]
918
+ if v is value or v == value :
854
919
return i
855
920
except IndexError :
856
921
break
@@ -859,7 +924,7 @@ def index(self, value, start=0, stop=None):
859
924
860
925
def count (self , value ):
861
926
'S.count(value) -> integer -- return number of occurrences of value'
862
- return sum (1 for v in self if v == value )
927
+ return sum (1 for v in self if v is value or v == value )
863
928
864
929
Sequence .register (tuple )
865
930
Sequence .register (str )
0 commit comments