@@ -789,6 +789,148 @@ async def gen():
789789 applied_twice = aiter (applied_once )
790790 self .assertIs (applied_once , applied_twice )
791791
792+ def make_counter (self ):
793+ state = {'n' : 0 }
794+ async def counter ():
795+ state ['n' ] += 1
796+ return state ['n' ]
797+ return counter
798+
799+ def collect (self , ait ):
800+ async def consume ():
801+ return [i async for i in ait ]
802+ return self .loop .run_until_complete (consume ())
803+
804+ def test_aiter_callable_stop (self ):
805+ self .assertEqual (self .collect (aiter (self .make_counter (), 4 )), [1 , 2 , 3 ])
806+ self .assertEqual (self .collect (aiter (self .make_counter (), stop_value = 4 )),
807+ [1 , 2 , 3 ])
808+
809+ def test_aiter_callable_stop_exception (self ):
810+ counter = self .make_counter ()
811+ async def spam ():
812+ value = await counter ()
813+ if value > 3 :
814+ raise LookupError
815+ return value
816+ self .assertEqual (self .collect (aiter (spam , stop_exception = LookupError )),
817+ [1 , 2 , 3 ])
818+ counter = self .make_counter ()
819+ self .assertEqual (
820+ self .collect (aiter (spam , stop_exception = (ZeroDivisionError ,
821+ LookupError ))),
822+ [1 , 2 , 3 ])
823+
824+ def test_aiter_callable_stop_and_exception (self ):
825+ counter = self .make_counter ()
826+ async def spam ():
827+ value = await counter ()
828+ if value > 5 :
829+ raise LookupError
830+ return value
831+ self .assertEqual (
832+ self .collect (aiter (spam , 3 , stop_exception = LookupError )), [1 , 2 ])
833+ counter = self .make_counter ()
834+ self .assertEqual (
835+ self .collect (aiter (spam , 100 , stop_exception = LookupError )),
836+ [1 , 2 , 3 , 4 , 5 ])
837+
838+ def test_aiter_callable_stop_exception_redundant (self ):
839+ # StopAsyncIteration and an empty tuple stop the iteration in any
840+ # case, so they are the same as no exception argument
841+ counter = self .make_counter ()
842+ async def spam ():
843+ value = await counter ()
844+ if value > 3 :
845+ raise StopAsyncIteration
846+ return value
847+ self .assertEqual (
848+ self .collect (aiter (spam , stop_exception = StopAsyncIteration )),
849+ [1 , 2 , 3 ])
850+ counter = self .make_counter ()
851+ self .assertEqual (self .collect (aiter (spam , stop_exception = ())),
852+ [1 , 2 , 3 ])
853+
854+ def test_aiter_callable_stop_async_iteration (self ):
855+ # StopAsyncIteration stops the iteration even if other exception
856+ # is specified
857+ counter = self .make_counter ()
858+ async def spam ():
859+ value = await counter ()
860+ if value > 3 :
861+ raise StopAsyncIteration
862+ return value
863+ self .assertEqual (self .collect (aiter (spam , stop_exception = LookupError )),
864+ [1 , 2 , 3 ])
865+
866+ def test_aiter_callable_other_exception (self ):
867+ async def spam ():
868+ raise ZeroDivisionError
869+ it = aiter (spam , stop_exception = LookupError )
870+ with self .assertRaises (ZeroDivisionError ):
871+ self .loop .run_until_complete (anext (it ))
872+
873+ def test_aiter_callable_exhausted (self ):
874+ it = aiter (self .make_counter (), 3 )
875+ self .assertEqual (self .collect (it ), [1 , 2 ])
876+ self .assertEqual (self .loop .run_until_complete (anext (it , 'default' )),
877+ 'default' )
878+ with self .assertRaises (StopAsyncIteration ):
879+ self .loop .run_until_complete (anext (it ))
880+
881+ def test_aiter_callable_lazy (self ):
882+ # The callable is only called when the awaitable is awaited
883+ calls = []
884+ async def spam ():
885+ calls .append (1 )
886+ return len (calls )
887+ it = aiter (spam , 10 )
888+ awaitable = it .__anext__ ()
889+ self .assertEqual (calls , [])
890+ self .assertEqual (self .loop .run_until_complete (awaitable ), 1 )
891+ self .assertEqual (calls , [1 ])
892+
893+ def test_aiter_callable_awaitable (self ):
894+ it = aiter (self .make_counter (), 10 )
895+ awaitable = it .__anext__ ()
896+ self .assertIsNone (awaitable .close ())
897+ with self .assertRaises (RuntimeError ):
898+ self .loop .run_until_complete (awaitable )
899+ awaitable = it .__anext__ ()
900+ with self .assertRaises (KeyError ):
901+ awaitable .throw (KeyError ('injected' ))
902+
903+ def test_aiter_callable_cancel (self ):
904+ # Cancellation is delivered to the awaited callable result
905+ cancelled = []
906+ async def spam ():
907+ try :
908+ await asyncio .sleep (10 )
909+ except asyncio .CancelledError :
910+ cancelled .append (1 )
911+ raise
912+ async def consume ():
913+ async for _ in aiter (spam , None ):
914+ pass
915+ async def main ():
916+ task = asyncio .ensure_future (consume ())
917+ await asyncio .sleep (0 )
918+ task .cancel ()
919+ with self .assertRaises (asyncio .CancelledError ):
920+ await task
921+ self .loop .run_until_complete (main ())
922+ self .assertEqual (cancelled , [1 ])
923+
924+ def test_aiter_callable_errors (self ):
925+ async def gen ():
926+ yield 1
927+ self .assertRaises (TypeError , aiter , gen (), 1 )
928+ self .assertRaises (TypeError , aiter , [1 , 2 ], stop_exception = LookupError )
929+ self .assertRaises (TypeError , aiter , len , stop_exception = 42 )
930+ self .assertRaises (TypeError , aiter , len ,
931+ stop_exception = (LookupError , 42 ))
932+ self .assertRaises (TypeError , aiter , len , stop_exception = LookupError ())
933+
792934 def test_anext_bad_args (self ):
793935 async def gen ():
794936 yield 1
0 commit comments