99from abc import ABCMeta , abstractmethod
1010import sys
1111
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" ,
1516 "Set" , "MutableSet" ,
1617 "Mapping" , "MutableMapping" ,
1718 "MappingView" , "KeysView" , "ItemsView" , "ValuesView" ,
@@ -59,10 +60,27 @@ async def _coro(): pass
5960coroutine = type (_coro )
6061_coro .close () # Prevent ResourceWarning
6162del _coro
63+ ## asynchronous generator ##
64+ async def _ag (): yield
65+ _ag = _ag ()
66+ async_generator = type (_ag )
67+ del _ag
6268
6369
6470### ONE-TRICK PONIES ###
6571
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+
6684class Hashable (metaclass = ABCMeta ):
6785
6886 __slots__ = ()
@@ -74,11 +92,7 @@ def __hash__(self):
7492 @classmethod
7593 def __subclasshook__ (cls , C ):
7694 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__" )
8296 return NotImplemented
8397
8498
@@ -93,11 +107,7 @@ def __await__(self):
93107 @classmethod
94108 def __subclasshook__ (cls , C ):
95109 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__" )
101111 return NotImplemented
102112
103113
@@ -138,14 +148,7 @@ def close(self):
138148 @classmethod
139149 def __subclasshook__ (cls , C ):
140150 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' )
149152 return NotImplemented
150153
151154
@@ -163,8 +166,7 @@ def __aiter__(self):
163166 @classmethod
164167 def __subclasshook__ (cls , C ):
165168 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__" )
168170 return NotImplemented
169171
170172
@@ -183,12 +185,61 @@ def __aiter__(self):
183185 @classmethod
184186 def __subclasshook__ (cls , C ):
185187 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__" )
189189 return NotImplemented
190190
191191
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+
192243class Iterable (metaclass = ABCMeta ):
193244
194245 __slots__ = ()
@@ -201,8 +252,7 @@ def __iter__(self):
201252 @classmethod
202253 def __subclasshook__ (cls , C ):
203254 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__" )
206256 return NotImplemented
207257
208258
@@ -221,9 +271,7 @@ def __iter__(self):
221271 @classmethod
222272 def __subclasshook__ (cls , C ):
223273 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__' )
227275 return NotImplemented
228276
229277Iterator .register (bytes_iterator )
@@ -242,6 +290,22 @@ def __subclasshook__(cls, C):
242290Iterator .register (zip_iterator )
243291
244292
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+
245309class Generator (Iterator ):
246310
247311 __slots__ = ()
@@ -285,17 +349,10 @@ def close(self):
285349 @classmethod
286350 def __subclasshook__ (cls , C ):
287351 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' )
296354 return NotImplemented
297355
298-
299356Generator .register (generator )
300357
301358
@@ -310,8 +367,7 @@ def __len__(self):
310367 @classmethod
311368 def __subclasshook__ (cls , C ):
312369 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__" )
315371 return NotImplemented
316372
317373
@@ -326,10 +382,18 @@ def __contains__(self, x):
326382 @classmethod
327383 def __subclasshook__ (cls , C ):
328384 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__" )
331386 return NotImplemented
332387
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
333397
334398class Callable (metaclass = ABCMeta ):
335399
@@ -342,15 +406,14 @@ def __call__(self, *args, **kwds):
342406 @classmethod
343407 def __subclasshook__ (cls , C ):
344408 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__" )
347410 return NotImplemented
348411
349412
350413### SETS ###
351414
352415
353- class Set (Sized , Iterable , Container ):
416+ class Set (Collection ):
354417
355418 """A set is a finite, iterable container.
356419
@@ -575,7 +638,7 @@ def __isub__(self, it):
575638### MAPPINGS ###
576639
577640
578- class Mapping (Sized , Iterable , Container ):
641+ class Mapping (Collection ):
579642
580643 __slots__ = ()
581644
@@ -623,6 +686,8 @@ def __eq__(self, other):
623686 return NotImplemented
624687 return dict (self .items ()) == dict (other .items ())
625688
689+ __reversed__ = None
690+
626691Mapping .register (mappingproxy )
627692
628693
@@ -672,7 +737,7 @@ def __contains__(self, item):
672737 except KeyError :
673738 return False
674739 else :
675- return v == value
740+ return v is value or v == value
676741
677742 def __iter__ (self ):
678743 for key in self ._mapping :
@@ -687,7 +752,8 @@ class ValuesView(MappingView):
687752
688753 def __contains__ (self , value ):
689754 for key in self ._mapping :
690- if value == self ._mapping [key ]:
755+ v = self ._mapping [key ]
756+ if v is value or v == value :
691757 return True
692758 return False
693759
@@ -796,7 +862,7 @@ def setdefault(self, key, default=None):
796862### SEQUENCES ###
797863
798864
799- class Sequence (Sized , Iterable , Container ):
865+ class Sequence (Reversible , Collection ):
800866
801867 """All the operations on a read-only sequence.
802868
@@ -822,7 +888,7 @@ def __iter__(self):
822888
823889 def __contains__ (self , value ):
824890 for v in self :
825- if v == value :
891+ if v is value or v == value :
826892 return True
827893 return False
828894
@@ -833,6 +899,9 @@ def __reversed__(self):
833899 def index (self , value , start = 0 , stop = None ):
834900 '''S.index(value, [start, [stop]]) -> integer -- return first index of value.
835901 Raises ValueError if the value is not present.
902+
903+ Supporting start and stop arguments is optional, but
904+ recommended.
836905 '''
837906 if start is not None and start < 0 :
838907 start = max (len (self ) + start , 0 )
@@ -842,7 +911,8 @@ def index(self, value, start=0, stop=None):
842911 i = start
843912 while stop is None or i < stop :
844913 try :
845- if self [i ] == value :
914+ v = self [i ]
915+ if v is value or v == value :
846916 return i
847917 except IndexError :
848918 break
@@ -851,7 +921,7 @@ def index(self, value, start=0, stop=None):
851921
852922 def count (self , value ):
853923 '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 )
855925
856926Sequence .register (tuple )
857927Sequence .register (str )
0 commit comments