64
64
from .values import get_argument_values , get_variable_values
65
65
66
66
67
+ try : # pragma: no cover
68
+ anext
69
+ except NameError : # pragma: no cover (Python < 3.10)
70
+ # noinspection PyShadowingBuiltins
71
+ async def anext (iterator : AsyncIterator ) -> Any :
72
+ """Return the next item from an async iterator."""
73
+ return await iterator .__anext__ ()
74
+
75
+
67
76
__all__ = [
68
77
"create_source_event_stream" ,
69
78
"default_field_resolver" ,
@@ -684,6 +693,67 @@ def complete_value(
684
693
f" '{ inspect (return_type )} '."
685
694
)
686
695
696
+ async def complete_async_iterator_value (
697
+ self ,
698
+ item_type : GraphQLOutputType ,
699
+ field_nodes : List [FieldNode ],
700
+ info : GraphQLResolveInfo ,
701
+ path : Path ,
702
+ iterator : AsyncIterator [Any ],
703
+ ) -> List [Any ]:
704
+ """Complete an async iterator.
705
+
706
+ Complete a async iterator value by completing the result and calling
707
+ recursively until all the results are completed.
708
+ """
709
+ is_awaitable = self .is_awaitable
710
+ awaitable_indices : List [int ] = []
711
+ append_awaitable = awaitable_indices .append
712
+ completed_results : List [Any ] = []
713
+ append_result = completed_results .append
714
+ index = 0
715
+ while True :
716
+ field_path = path .add_key (index , None )
717
+ try :
718
+ try :
719
+ value = await anext (iterator )
720
+ except StopAsyncIteration :
721
+ break
722
+ try :
723
+ completed_item = self .complete_value (
724
+ item_type , field_nodes , info , field_path , value
725
+ )
726
+ if is_awaitable (completed_item ):
727
+ append_awaitable (index )
728
+ append_result (completed_item )
729
+ except Exception as raw_error :
730
+ append_result (None )
731
+ error = located_error (raw_error , field_nodes , field_path .as_list ())
732
+ self .handle_field_error (error , item_type )
733
+ except Exception as raw_error :
734
+ append_result (None )
735
+ error = located_error (raw_error , field_nodes , field_path .as_list ())
736
+ self .handle_field_error (error , item_type )
737
+ break
738
+ index += 1
739
+
740
+ if not awaitable_indices :
741
+ return completed_results
742
+
743
+ if len (awaitable_indices ) == 1 :
744
+ # If there is only one index, avoid the overhead of parallelization.
745
+ index = awaitable_indices [0 ]
746
+ completed_results [index ] = await completed_results [index ]
747
+ else :
748
+ for index , result in zip (
749
+ awaitable_indices ,
750
+ await gather (
751
+ * (completed_results [index ] for index in awaitable_indices )
752
+ ),
753
+ ):
754
+ completed_results [index ] = result
755
+ return completed_results
756
+
687
757
def complete_list_value (
688
758
self ,
689
759
return_type : GraphQLList [GraphQLOutputType ],
@@ -696,20 +766,16 @@ def complete_list_value(
696
766
697
767
Complete a list value by completing each item in the list with the inner type.
698
768
"""
699
- if not is_iterable (result ):
700
- # experimental: allow async iterables
701
- if isinstance (result , AsyncIterable ):
702
- # noinspection PyShadowingNames
703
- async def async_iterable_to_list (
704
- async_result : AsyncIterable [Any ],
705
- ) -> Any :
706
- sync_result = [item async for item in async_result ]
707
- return self .complete_list_value (
708
- return_type , field_nodes , info , path , sync_result
709
- )
769
+ item_type = return_type .of_type
710
770
711
- return async_iterable_to_list (result )
771
+ if isinstance (result , AsyncIterable ):
772
+ iterator = result .__aiter__ ()
712
773
774
+ return self .complete_async_iterator_value (
775
+ item_type , field_nodes , info , path , iterator
776
+ )
777
+
778
+ if not is_iterable (result ):
713
779
raise GraphQLError (
714
780
"Expected Iterable, but did not find one for field"
715
781
f" '{ info .parent_type .name } .{ info .field_name } '."
@@ -718,7 +784,6 @@ async def async_iterable_to_list(
718
784
# This is specified as a simple map, however we're optimizing the path where
719
785
# the list contains no coroutine objects by avoiding creating another coroutine
720
786
# object.
721
- item_type = return_type .of_type
722
787
is_awaitable = self .is_awaitable
723
788
awaitable_indices : List [int ] = []
724
789
append_awaitable = awaitable_indices .append
0 commit comments