Skip to content

Commit 0a4aa88

Browse files
committed
polish(incremental): simplify iterator
Replicates graphql/graphql-js@5cd5001
1 parent a7663f4 commit 0a4aa88

File tree

2 files changed

+43
-44
lines changed

2 files changed

+43
-44
lines changed

src/graphql/execution/incremental_graph.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class IncrementalGraph:
4848

4949
_root_nodes: dict[SubsequentResultRecord, None]
5050
_completed_queue: list[IncrementalDataRecordResult]
51-
_next_queue: list[Future[Iterable[IncrementalDataRecordResult]]]
51+
_next_queue: list[Future[Iterable[IncrementalDataRecordResult] | None]]
5252

5353
_tasks: set[Task[Any]]
5454

@@ -87,24 +87,31 @@ def add_completed_reconcilable_deferred_grouped_field_set(
8787
incremental_data_records, deferred_records
8888
)
8989

90-
async def completed_incremental_data(
90+
def current_completed_batch(
9191
self,
92-
) -> AsyncGenerator[Iterable[IncrementalDataRecordResult], None]:
93-
"""Asynchronously yield completed incremental data record results."""
92+
) -> Generator[IncrementalDataRecordResult, None, None]:
93+
"""Yield the current completed batch of incremental data record results."""
94+
queue = self._completed_queue
95+
while queue:
96+
yield queue.pop(0)
97+
if not self._root_nodes:
98+
self.abort()
99+
100+
def next_completed_batch(
101+
self,
102+
) -> Future[Iterable[IncrementalDataRecordResult] | None]:
103+
"""Return a future that resolves to the next completed batch."""
94104
loop = get_running_loop()
95-
while True:
96-
if self._completed_queue:
97-
first_result = self._completed_queue.pop(0)
98-
yield self._yield_current_completed_incremental_data(first_result)
99-
else:
100-
future: Future[Iterable[IncrementalDataRecordResult]] = (
101-
loop.create_future()
102-
)
103-
self._next_queue.append(future)
104-
try:
105-
yield await future
106-
except CancelledError:
107-
break # pragma: no cover
105+
future: Future[Iterable[IncrementalDataRecordResult] | None] = (
106+
loop.create_future()
107+
)
108+
self._next_queue.append(future)
109+
return future
110+
111+
def abort(self) -> None:
112+
"""Abort the incremental graph execution."""
113+
for resolve in self._next_queue:
114+
resolve.set_result(None)
108115

109116
def has_next(self) -> bool:
110117
"""Check if there are more results to process."""
@@ -332,11 +339,7 @@ def _yield_current_completed_incremental_data(
332339
) -> Generator[IncrementalDataRecordResult, None, None]:
333340
"""Yield the current completed incremental data."""
334341
yield first_result
335-
queue = self._completed_queue
336-
while queue:
337-
yield queue.pop(0)
338-
if not self._root_nodes:
339-
self.stop_incremental_data()
342+
yield from self.current_completed_batch()
340343

341344
def _enqueue(self, completed: IncrementalDataRecordResult) -> None:
342345
"""Enqueue completed incremental data record result."""

src/graphql/execution/incremental_publisher.py

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import (
88
TYPE_CHECKING,
99
Any,
10+
Iterable,
1011
NamedTuple,
1112
Protocol,
1213
cast,
@@ -134,38 +135,33 @@ async def _subscribe(
134135
incremental_graph = self._incremental_graph
135136
check_has_next = incremental_graph.has_next
136137
handle_completed_incremental_data = self._handle_completed_incremental_data
137-
completed_incremental_data = incremental_graph.completed_incremental_data()
138-
# use the raw iterator rather than 'async for' so as not to end the iterator
139-
# when exiting the loop with the next value
140-
get_next_results = completed_incremental_data.__aiter__().__anext__
141-
is_done = False
142-
try:
143-
while not is_done:
144-
try:
145-
completed_results = await get_next_results()
146-
except StopAsyncIteration: # pragma: no cover
147-
break
148138

149-
context = SubsequentIncrementalExecutionResultContext([], [], [])
150-
for completed_result in completed_results:
151-
await handle_completed_incremental_data(completed_result, context)
139+
try:
140+
while True:
141+
batch: Iterable[IncrementalDataRecordResult] | None = (
142+
incremental_graph.current_completed_batch()
143+
)
152144

153-
if context.incremental or context.completed: # pragma: no branch
154-
has_next = check_has_next()
145+
while batch is not None:
146+
context = SubsequentIncrementalExecutionResultContext([], [], [])
147+
for completed_result in batch:
148+
await handle_completed_incremental_data(
149+
completed_result, context
150+
)
155151

156-
if not has_next:
157-
is_done = True
152+
if context.incremental or context.completed: # pragma: no branch
153+
has_next = check_has_next()
158154

159-
subsequent_incremental_execution_result = (
160-
SubsequentIncrementalExecutionResult(
155+
yield SubsequentIncrementalExecutionResult(
161156
has_next=has_next,
162157
pending=context.pending or None,
163158
incremental=context.incremental or None,
164159
completed=context.completed or None,
165160
)
166-
)
167161

168-
yield subsequent_incremental_execution_result
162+
if not has_next:
163+
return
164+
batch = await incremental_graph.next_completed_batch()
169165
finally:
170166
await self._stop_async_iterators()
171167

0 commit comments

Comments
 (0)