Skip to content

Commit db05a21

Browse files
committed
simplify CollectFields for @defer and @stream
Replicates graphql/graphql-js@2aedf25
1 parent e7da373 commit db05a21

File tree

6 files changed

+369
-408
lines changed

6 files changed

+369
-408
lines changed

docs/conf.py

Lines changed: 77 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -138,79 +138,77 @@
138138
}
139139

140140
# ignore the following undocumented or internal references:
141-
ignore_references = set(
142-
[
143-
"GNT",
144-
"GT",
145-
"KT",
146-
"T",
147-
"VT",
148-
"TContext",
149-
"Enum",
150-
"traceback",
151-
"types.TracebackType",
152-
"TypeMap",
153-
"AwaitableOrValue",
154-
"DeferredFragmentRecord",
155-
"DeferUsage",
156-
"EnterLeaveVisitor",
157-
"ExperimentalIncrementalExecutionResults",
158-
"FieldGroup",
159-
"FormattedIncrementalResult",
160-
"FormattedPendingResult",
161-
"FormattedSourceLocation",
162-
"GraphQLAbstractType",
163-
"GraphQLCompositeType",
164-
"GraphQLEnumValueMap",
165-
"GraphQLErrorExtensions",
166-
"GraphQLFieldResolver",
167-
"GraphQLInputType",
168-
"GraphQLNullableType",
169-
"GraphQLOutputType",
170-
"GraphQLTypeResolver",
171-
"GroupedFieldSet",
172-
"IncrementalDataRecord",
173-
"IncrementalResult",
174-
"InitialResultRecord",
175-
"Middleware",
176-
"PendingResult",
177-
"StreamItemsRecord",
178-
"StreamRecord",
179-
"SubsequentDataRecord",
180-
"asyncio.events.AbstractEventLoop",
181-
"collections.abc.MutableMapping",
182-
"collections.abc.MutableSet",
183-
"enum.Enum",
184-
"graphql.execution.collect_fields.DeferUsage",
185-
"graphql.execution.collect_fields.CollectFieldsResult",
186-
"graphql.execution.collect_fields.FieldGroup",
187-
"graphql.execution.execute.StreamArguments",
188-
"graphql.execution.execute.StreamUsage",
189-
"graphql.execution.map_async_iterable.map_async_iterable",
190-
"graphql.execution.incremental_publisher.CompletedResult",
191-
"graphql.execution.incremental_publisher.DeferredFragmentRecord",
192-
"graphql.execution.incremental_publisher.DeferredGroupedFieldSetRecord",
193-
"graphql.execution.incremental_publisher.FormattedCompletedResult",
194-
"graphql.execution.incremental_publisher.FormattedPendingResult",
195-
"graphql.execution.incremental_publisher.IncrementalPublisher",
196-
"graphql.execution.incremental_publisher.InitialResultRecord",
197-
"graphql.execution.incremental_publisher.PendingResult",
198-
"graphql.execution.incremental_publisher.StreamItemsRecord",
199-
"graphql.execution.incremental_publisher.StreamRecord",
200-
"graphql.execution.Middleware",
201-
"graphql.language.lexer.EscapeSequence",
202-
"graphql.language.visitor.EnterLeaveVisitor",
203-
"graphql.pyutils.ref_map.K",
204-
"graphql.pyutils.ref_map.V",
205-
"graphql.type.definition.GT_co",
206-
"graphql.type.definition.GNT_co",
207-
"graphql.type.definition.TContext",
208-
"graphql.type.schema.InterfaceImplementations",
209-
"graphql.validation.validation_context.VariableUsage",
210-
"graphql.validation.rules.known_argument_names.KnownArgumentNamesOnDirectivesRule",
211-
"graphql.validation.rules.provided_required_arguments.ProvidedRequiredArgumentsOnDirectivesRule",
212-
]
213-
)
141+
ignore_references = {
142+
"GNT",
143+
"GT",
144+
"KT",
145+
"T",
146+
"VT",
147+
"TContext",
148+
"Enum",
149+
"traceback",
150+
"types.TracebackType",
151+
"TypeMap",
152+
"AwaitableOrValue",
153+
"DeferredFragmentRecord",
154+
"DeferUsage",
155+
"EnterLeaveVisitor",
156+
"ExperimentalIncrementalExecutionResults",
157+
"FieldGroup",
158+
"FormattedIncrementalResult",
159+
"FormattedPendingResult",
160+
"FormattedSourceLocation",
161+
"GraphQLAbstractType",
162+
"GraphQLCompositeType",
163+
"GraphQLEnumValueMap",
164+
"GraphQLErrorExtensions",
165+
"GraphQLFieldResolver",
166+
"GraphQLInputType",
167+
"GraphQLNullableType",
168+
"GraphQLOutputType",
169+
"GraphQLTypeResolver",
170+
"GroupedFieldSet",
171+
"IncrementalDataRecord",
172+
"IncrementalResult",
173+
"InitialResultRecord",
174+
"Middleware",
175+
"PendingResult",
176+
"StreamItemsRecord",
177+
"StreamRecord",
178+
"SubsequentDataRecord",
179+
"asyncio.events.AbstractEventLoop",
180+
"collections.abc.MutableMapping",
181+
"collections.abc.MutableSet",
182+
"enum.Enum",
183+
"graphql.execution.build_field_plan.FieldGroup",
184+
"graphql.execution.build_field_plan.FieldPlan",
185+
"graphql.execution.collect_fields.DeferUsage",
186+
"graphql.execution.execute.StreamArguments",
187+
"graphql.execution.execute.StreamUsage",
188+
"graphql.execution.map_async_iterable.map_async_iterable",
189+
"graphql.execution.incremental_publisher.CompletedResult",
190+
"graphql.execution.incremental_publisher.DeferredFragmentRecord",
191+
"graphql.execution.incremental_publisher.DeferredGroupedFieldSetRecord",
192+
"graphql.execution.incremental_publisher.FormattedCompletedResult",
193+
"graphql.execution.incremental_publisher.FormattedPendingResult",
194+
"graphql.execution.incremental_publisher.IncrementalPublisher",
195+
"graphql.execution.incremental_publisher.InitialResultRecord",
196+
"graphql.execution.incremental_publisher.PendingResult",
197+
"graphql.execution.incremental_publisher.StreamItemsRecord",
198+
"graphql.execution.incremental_publisher.StreamRecord",
199+
"graphql.execution.Middleware",
200+
"graphql.language.lexer.EscapeSequence",
201+
"graphql.language.visitor.EnterLeaveVisitor",
202+
"graphql.pyutils.ref_map.K",
203+
"graphql.pyutils.ref_map.V",
204+
"graphql.type.definition.GT_co",
205+
"graphql.type.definition.GNT_co",
206+
"graphql.type.definition.TContext",
207+
"graphql.type.schema.InterfaceImplementations",
208+
"graphql.validation.validation_context.VariableUsage",
209+
"graphql.validation.rules.known_argument_names.KnownArgumentNamesOnDirectivesRule",
210+
"graphql.validation.rules.provided_required_arguments.ProvidedRequiredArgumentsOnDirectivesRule",
211+
}
214212

215213
ignore_references.update(__builtins__.keys())
216214

@@ -228,10 +226,12 @@ def on_missing_reference(app, env, node, contnode):
228226
name = target.rsplit(".", 1)[-1]
229227
if name in ("GT", "GNT", "KT", "T", "VT"):
230228
return contnode
231-
if typ == "obj":
232-
if target.startswith("typing."):
233-
if name in ("Any", "Optional", "Union"):
234-
return contnode
229+
if (
230+
typ == "obj"
231+
and target.startswith("typing.")
232+
and name in ("Any", "Optional", "Union")
233+
):
234+
return contnode
235235
if typ != "class":
236236
return None
237237
if "." in target: # maybe too specific
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
"""Build field plan"""
2+
3+
from __future__ import annotations
4+
5+
import sys
6+
from typing import TYPE_CHECKING, Dict, NamedTuple
7+
8+
from ..pyutils import RefMap, RefSet
9+
from .collect_fields import DeferUsage, FieldDetails
10+
11+
if TYPE_CHECKING:
12+
from ..language import FieldNode
13+
14+
try:
15+
from typing import TypeAlias
16+
except ImportError: # Python < 3.10
17+
from typing_extensions import TypeAlias
18+
19+
__all__ = [
20+
"DeferUsageSet",
21+
"FieldGroup",
22+
"FieldPlan",
23+
"GroupedFieldSet",
24+
"NewGroupedFieldSetDetails",
25+
"build_field_plan",
26+
]
27+
28+
29+
DeferUsageSet: TypeAlias = RefSet[DeferUsage]
30+
31+
32+
class FieldGroup(NamedTuple):
33+
"""A group of fields with defer usages."""
34+
35+
fields: list[FieldDetails]
36+
defer_usages: DeferUsageSet | None = None
37+
known_defer_usages: DeferUsageSet | None = None
38+
39+
def to_nodes(self) -> list[FieldNode]:
40+
"""Return the field nodes in this group."""
41+
return [field_details.node for field_details in self.fields]
42+
43+
44+
if sys.version_info < (3, 9):
45+
GroupedFieldSet: TypeAlias = Dict[str, FieldGroup]
46+
else: # Python >= 3.9
47+
GroupedFieldSet: TypeAlias = dict[str, FieldGroup]
48+
49+
50+
class NewGroupedFieldSetDetails(NamedTuple):
51+
"""Details of a new grouped field set."""
52+
53+
grouped_field_set: GroupedFieldSet
54+
should_initiate_defer: bool
55+
56+
57+
class FieldPlan(NamedTuple):
58+
"""A plan for executing fields."""
59+
60+
grouped_field_set: GroupedFieldSet
61+
new_grouped_field_set_details_map: RefMap[DeferUsageSet, NewGroupedFieldSetDetails]
62+
new_defer_usages: list[DeferUsage]
63+
64+
65+
def build_field_plan(
66+
fields: dict[str, list[FieldDetails]],
67+
parent_defer_usages: DeferUsageSet | None = None,
68+
known_defer_usages: DeferUsageSet | None = None,
69+
) -> FieldPlan:
70+
"""Build a plan for executing fields."""
71+
if parent_defer_usages is None:
72+
parent_defer_usages = RefSet()
73+
if known_defer_usages is None:
74+
known_defer_usages = RefSet()
75+
76+
new_defer_usages: RefSet[DeferUsage] = RefSet()
77+
new_known_defer_usages: RefSet[DeferUsage] = RefSet(known_defer_usages)
78+
79+
grouped_field_set: GroupedFieldSet = {}
80+
81+
new_grouped_field_set_details_map: RefMap[
82+
DeferUsageSet, NewGroupedFieldSetDetails
83+
] = RefMap()
84+
85+
map_: dict[str, tuple[DeferUsageSet, list[FieldDetails]]] = {}
86+
87+
for response_key, field_details_list in fields.items():
88+
defer_usage_set: RefSet[DeferUsage] = RefSet()
89+
in_original_result = False
90+
for field_details in field_details_list:
91+
defer_usage = field_details.defer_usage
92+
if defer_usage is None:
93+
in_original_result = True
94+
continue
95+
defer_usage_set.add(defer_usage)
96+
if defer_usage not in known_defer_usages:
97+
new_defer_usages.add(defer_usage)
98+
new_known_defer_usages.add(defer_usage)
99+
if in_original_result:
100+
defer_usage_set.clear()
101+
else:
102+
defer_usage_set -= {
103+
defer_usage
104+
for defer_usage in defer_usage_set
105+
if any(
106+
ancestor in defer_usage_set for ancestor in defer_usage.ancestors
107+
)
108+
}
109+
map_[response_key] = (defer_usage_set, field_details_list)
110+
111+
for response_key, [defer_usage_set, field_details_list] in map_.items():
112+
if defer_usage_set == parent_defer_usages:
113+
field_group = grouped_field_set.get(response_key)
114+
if field_group is None: # pragma: no cover else
115+
field_group = FieldGroup([], defer_usage_set, new_known_defer_usages)
116+
grouped_field_set[response_key] = field_group
117+
field_group.fields.extend(field_details_list)
118+
continue
119+
120+
for (
121+
new_grouped_field_set_defer_usage_set,
122+
new_grouped_field_set_details,
123+
) in new_grouped_field_set_details_map.items():
124+
if new_grouped_field_set_defer_usage_set == defer_usage_set:
125+
new_grouped_field_set = new_grouped_field_set_details.grouped_field_set
126+
break
127+
else:
128+
new_grouped_field_set = {}
129+
new_grouped_field_set_details = NewGroupedFieldSetDetails(
130+
new_grouped_field_set,
131+
any(
132+
defer_usage not in parent_defer_usages
133+
for defer_usage in defer_usage_set
134+
),
135+
)
136+
new_grouped_field_set_details_map[defer_usage_set] = (
137+
new_grouped_field_set_details
138+
)
139+
140+
field_group = new_grouped_field_set.get(response_key)
141+
if field_group is None: # pragma: no cover else
142+
field_group = FieldGroup([], defer_usage_set, new_known_defer_usages)
143+
new_grouped_field_set[response_key] = field_group
144+
field_group.fields.extend(field_details_list)
145+
146+
return FieldPlan(
147+
grouped_field_set, new_grouped_field_set_details_map, list(new_defer_usages)
148+
)

0 commit comments

Comments
 (0)