Skip to content

Commit 68a735a

Browse files
committed
Remove excessive cache inside build_schema and extend_schema
Replicates graphql/graphql-js@afc6b7c
1 parent 4158436 commit 68a735a

File tree

3 files changed

+132
-147
lines changed

3 files changed

+132
-147
lines changed

graphql/type/schema.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ class GraphQLSchema:
5252
directives=specifiedDirectives + [myCustomDirective])
5353
"""
5454

55-
query: Optional[GraphQLObjectType]
56-
mutation: Optional[GraphQLObjectType]
57-
subscription: Optional[GraphQLObjectType]
55+
query_type: Optional[GraphQLObjectType]
56+
mutation_type: Optional[GraphQLObjectType]
57+
subscription_type: Optional[GraphQLObjectType]
5858
type_map: TypeMap
5959
directives: List[GraphQLDirective]
6060
ast_node: Optional[ast.SchemaDefinitionNode]
@@ -139,9 +139,9 @@ def __init__(
139139

140140
def to_kwargs(self) -> Dict[str, Any]:
141141
return dict(
142-
query=self.query,
143-
mutation=self.mutation,
144-
subscription=self.subscription,
142+
query=self.query_type,
143+
mutation=self.mutation_type,
144+
subscription=self.subscription_type,
145145
types=self.type_map.values(),
146146
directives=self.directives[:],
147147
ast_node=self.ast_node,

graphql/utilities/build_ast_schema.py

Lines changed: 56 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Callable, Dict, List, NoReturn, Optional, Union, cast
1+
from typing import Callable, Dict, List, NoReturn, Optional, Union, cast
22

33
from ..language import (
44
DirectiveDefinitionNode,
@@ -57,7 +57,6 @@
5757
)
5858
from .value_from_ast import value_from_ast
5959

60-
TypeDefinitionsMap = Dict[str, TypeDefinitionNode]
6160
TypeResolver = Callable[[str], GraphQLNamedType]
6261

6362
__all__ = [
@@ -98,34 +97,39 @@ def build_ast_schema(
9897
assert_valid_sdl(document_ast)
9998

10099
schema_def: Optional[SchemaDefinitionNode] = None
101-
node_map: TypeDefinitionsMap = {}
100+
type_defs: List[TypeDefinitionNode] = []
102101
directive_defs: List[DirectiveDefinitionNode] = []
103102
append_directive_def = directive_defs.append
104103
for def_ in document_ast.definitions:
105104
if isinstance(def_, SchemaDefinitionNode):
106105
schema_def = def_
107106
elif isinstance(def_, TypeDefinitionNode):
108107
def_ = cast(TypeDefinitionNode, def_)
109-
node_map[def_.name.value] = def_
108+
type_defs.append(def_)
110109
elif isinstance(def_, DirectiveDefinitionNode):
111110
append_directive_def(def_)
112111

112+
def resolve_type(type_name: str) -> GraphQLNamedType:
113+
type_ = type_map.get(type_name)
114+
if not type:
115+
raise TypeError(f"Type '{type_name}' not found in document.")
116+
return type_
117+
118+
ast_builder = ASTDefinitionBuilder(
119+
assume_valid=assume_valid, resolve_type=resolve_type
120+
)
121+
122+
type_map = {node.name.value: ast_builder.build_type(node) for node in type_defs}
123+
113124
if schema_def:
114-
operation_types: Dict[OperationType, Any] = get_operation_types(schema_def)
125+
operation_types = get_operation_types(schema_def)
115126
else:
116127
operation_types = {
117-
OperationType.QUERY: node_map.get("Query"),
118-
OperationType.MUTATION: node_map.get("Mutation"),
119-
OperationType.SUBSCRIPTION: node_map.get("Subscription"),
128+
OperationType.QUERY: "Query",
129+
OperationType.MUTATION: "Mutation",
130+
OperationType.SUBSCRIPTION: "Subscription",
120131
}
121132

122-
def resolve_type(type_name: str):
123-
raise TypeError(f"Type '{type_name}' not found in document.")
124-
125-
ast_builder = ASTDefinitionBuilder(
126-
node_map, assume_valid=assume_valid, resolve_type=resolve_type
127-
)
128-
129133
directives = [
130134
ast_builder.build_directive(directive_def) for directive_def in directive_defs
131135
]
@@ -138,35 +142,31 @@ def resolve_type(type_name: str):
138142
if not any(directive.name == "deprecated" for directive in directives):
139143
directives.append(GraphQLDeprecatedDirective)
140144

141-
# Note: While this could make early assertions to get the correctly typed values
142-
# below, that would throw immediately while type system validation with
143-
# `validate_schema()` will produce more actionable results.
144145
query_type = operation_types.get(OperationType.QUERY)
145146
mutation_type = operation_types.get(OperationType.MUTATION)
146147
subscription_type = operation_types.get(OperationType.SUBSCRIPTION)
147148
return GraphQLSchema(
148-
query=cast(GraphQLObjectType, ast_builder.build_type(query_type))
149-
if query_type
150-
else None,
151-
mutation=cast(GraphQLObjectType, ast_builder.build_type(mutation_type))
149+
# Note: While this could make early assertions to get the correctly
150+
# typed values below, that would throw immediately while type system
151+
# validation with `validate_schema()` will produce more actionable results.
152+
query=cast(GraphQLObjectType, type_map.get(query_type)) if query_type else None,
153+
mutation=cast(GraphQLObjectType, type_map.get(mutation_type))
152154
if mutation_type
153155
else None,
154-
subscription=cast(GraphQLObjectType, ast_builder.build_type(subscription_type))
156+
subscription=cast(GraphQLObjectType, type_map.get(subscription_type))
155157
if subscription_type
156158
else None,
157-
types=[ast_builder.build_type(node) for node in node_map.values()],
159+
types=list(type_map.values()),
158160
directives=directives,
159161
ast_node=schema_def,
160162
assume_valid=assume_valid,
161163
)
162164

163165

164-
def get_operation_types(
165-
schema: SchemaDefinitionNode
166-
) -> Dict[OperationType, NamedTypeNode]:
167-
op_types: Dict[OperationType, NamedTypeNode] = {}
166+
def get_operation_types(schema: SchemaDefinitionNode) -> Dict[OperationType, str]:
167+
op_types: Dict[OperationType, str] = {}
168168
for operation_type in schema.operation_types:
169-
op_types[operation_type.operation] = operation_type.type
169+
op_types[operation_type.operation] = operation_type.type.name.value
170170
return op_types
171171

172172

@@ -175,48 +175,34 @@ def default_type_resolver(type_name: str, *_args) -> NoReturn:
175175
raise TypeError(f"Type '{type_name}' not found in document.")
176176

177177

178+
std_type_map: Dict[str, Union[GraphQLNamedType, GraphQLObjectType]] = {
179+
**specified_scalar_types,
180+
**introspection_types,
181+
}
182+
183+
178184
class ASTDefinitionBuilder:
179185
def __init__(
180186
self,
181-
type_definitions_map: TypeDefinitionsMap,
182187
assume_valid: bool = False,
183188
resolve_type: TypeResolver = default_type_resolver,
184189
) -> None:
185-
self._type_definitions_map = type_definitions_map
186190
self._assume_valid = assume_valid
187191
self._resolve_type = resolve_type
188-
# Initialize to the GraphQL built in scalars and introspection types.
189-
self._cache: Dict[str, GraphQLNamedType] = {
190-
**specified_scalar_types,
191-
**introspection_types,
192-
}
193192

194-
def build_type(
195-
self, node: Union[NamedTypeNode, TypeDefinitionNode]
196-
) -> GraphQLNamedType:
197-
type_name = node.name.value
198-
cache = self._cache
199-
if type_name not in cache:
200-
if isinstance(node, NamedTypeNode):
201-
def_node = self._type_definitions_map.get(type_name)
202-
cache[type_name] = (
203-
self._make_schema_def(def_node)
204-
if def_node
205-
else self._resolve_type(node.name.value)
206-
)
207-
else:
208-
cache[type_name] = self._make_schema_def(node)
209-
return cache[type_name]
210-
211-
def _build_wrapped_type(self, type_node: TypeNode) -> GraphQLType:
212-
if isinstance(type_node, ListTypeNode):
213-
return GraphQLList(self._build_wrapped_type(type_node.type))
214-
if isinstance(type_node, NonNullTypeNode):
193+
def get_named_type(self, node: NamedTypeNode) -> GraphQLNamedType:
194+
name = node.name.value
195+
return std_type_map.get(name) or self._resolve_type(name)
196+
197+
def get_wrapped_type(self, node: TypeNode) -> GraphQLType:
198+
if isinstance(node, ListTypeNode):
199+
return GraphQLList(self.get_wrapped_type(node.type))
200+
if isinstance(node, NonNullTypeNode):
215201
return GraphQLNonNull(
216202
# Note: GraphQLNonNull constructor validates this type
217-
cast(GraphQLNullableType, self._build_wrapped_type(type_node.type))
203+
cast(GraphQLNullableType, self.get_wrapped_type(node.type))
218204
)
219-
return self.build_type(cast(NamedTypeNode, type_node))
205+
return self.get_named_type(cast(NamedTypeNode, node))
220206

221207
def build_directive(self, directive: DirectiveDefinitionNode) -> GraphQLDirective:
222208
locations = [DirectiveLocation[node.value] for node in directive.locations]
@@ -235,7 +221,7 @@ def build_field(self, field: FieldDefinitionNode) -> GraphQLField:
235221
# Note: While this could make assertions to get the correctly typed value, that
236222
# would throw immediately while type system validation with `validate_schema()`
237223
# will produce more actionable results.
238-
type_ = self._build_wrapped_type(field.type)
224+
type_ = self.get_wrapped_type(field.type)
239225
type_ = cast(GraphQLOutputType, type_)
240226
return GraphQLField(
241227
type_=type_,
@@ -249,7 +235,7 @@ def build_arg(self, value: InputValueDefinitionNode) -> GraphQLArgument:
249235
# Note: While this could make assertions to get the correctly typed value, that
250236
# would throw immediately while type system validation with `validate_schema()`
251237
# will produce more actionable results.
252-
type_ = self._build_wrapped_type(value.type)
238+
type_ = self.get_wrapped_type(value.type)
253239
type_ = cast(GraphQLInputType, type_)
254240
return GraphQLArgument(
255241
type_=type_,
@@ -262,7 +248,7 @@ def build_input_field(self, value: InputValueDefinitionNode) -> GraphQLInputFiel
262248
# Note: While this could make assertions to get the correctly typed value, that
263249
# would throw immediately while type system validation with `validate_schema()`
264250
# will produce more actionable results.
265-
type_ = self._build_wrapped_type(value.type)
251+
type_ = self.get_wrapped_type(value.type)
266252
type_ = cast(GraphQLInputType, type_)
267253
return GraphQLInputField(
268254
type_=type_,
@@ -279,7 +265,11 @@ def build_enum_value(value: EnumValueDefinitionNode) -> GraphQLEnumValue:
279265
ast_node=value,
280266
)
281267

282-
def _make_schema_def(self, ast_node: TypeDefinitionNode) -> GraphQLNamedType:
268+
def build_type(self, ast_node: TypeDefinitionNode) -> GraphQLNamedType:
269+
name = ast_node.name.value
270+
if name in std_type_map:
271+
return std_type_map[name]
272+
283273
method = {
284274
"object_type_definition": self._make_type_def,
285275
"interface_type_definition": self._make_interface_def,
@@ -289,6 +279,7 @@ def _make_schema_def(self, ast_node: TypeDefinitionNode) -> GraphQLNamedType:
289279
"input_object_type_definition": self._make_input_object_def,
290280
}.get(ast_node.kind)
291281
if not method:
282+
# Not reachable. All possible type definition nodes have been considered.
292283
raise TypeError(f"Type kind '{ast_node.kind}' not supported.")
293284
return method(ast_node) # type: ignore
294285

@@ -302,7 +293,7 @@ def _make_type_def(self, ast_node: ObjectTypeDefinitionNode) -> GraphQLObjectTyp
302293
interfaces = cast(
303294
Thunk[GraphQLInterfaceList],
304295
(
305-
(lambda: [self.build_type(ref) for ref in interface_nodes])
296+
(lambda: [self.get_named_type(ref) for ref in interface_nodes])
306297
if interface_nodes
307298
else []
308299
),
@@ -373,7 +364,7 @@ def _make_union_def(self, type_def: UnionTypeDefinitionNode) -> GraphQLUnionType
373364
# `validate_schema()` will get more actionable results.
374365
types = cast(
375366
Thunk[GraphQLTypeList],
376-
(lambda: [self.build_type(ref) for ref in type_nodes])
367+
(lambda: [self.get_named_type(ref) for ref in type_nodes])
377368
if type_nodes
378369
else [],
379370
)

0 commit comments

Comments
 (0)