Skip to content

Commit e7e24bf

Browse files
committed
Isolated typemap into individual module for simplicity.
1 parent 512a24e commit e7e24bf

File tree

2 files changed

+154
-138
lines changed

2 files changed

+154
-138
lines changed

graphql/type/schema.py

Lines changed: 16 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
from collections import Iterable, OrderedDict, defaultdict
2-
from functools import reduce
3-
4-
from ..utils.type_comparators import is_equal_type, is_type_sub_type_of
5-
from .definition import (GraphQLInputObjectType, GraphQLInterfaceType,
6-
GraphQLList, GraphQLNonNull, GraphQLObjectType,
7-
GraphQLUnionType, GraphQLInputObjectField, is_input_type, is_output_type, GraphQLField, GraphQLArgument)
1+
from collections import Iterable
2+
from .definition import GraphQLObjectType
83
from .directives import (GraphQLDirective, GraphQLIncludeDirective,
94
GraphQLSkipDirective)
105
from .introspection import IntrospectionSchema
6+
from .typemap import GraphQLTypeMap
117

128

139
class GraphQLSchema(object):
@@ -52,22 +48,18 @@ def __init__(self, query, mutation=None, subscription=None, directives=None, typ
5248
'Schema directives must be List[GraphQLDirective] if provided but got: {}.'.format(
5349
directives
5450
)
55-
5651
self._directives = directives
57-
self._possible_type_map = defaultdict(set)
58-
self._type_map = self._build_type_map(types)
59-
# Keep track of all implementations by interface name.
60-
self._implementations = defaultdict(list)
61-
for type in self._type_map.values():
62-
if isinstance(type, GraphQLObjectType):
63-
for interface in type.get_interfaces():
64-
self._implementations[interface.name].append(type)
65-
66-
# Enforce correct interface implementations.
67-
for type in self._type_map.values():
68-
if isinstance(type, GraphQLObjectType):
69-
for interface in type.get_interfaces():
70-
assert_object_implements_interface(self, type, interface)
52+
53+
initial_types = [
54+
query,
55+
mutation,
56+
subscription,
57+
IntrospectionSchema
58+
]
59+
if types:
60+
initial_types += types
61+
self._type_map = GraphQLTypeMap(initial_types)
62+
7163

7264
def get_query_type(self):
7365
return self._query
@@ -94,122 +86,8 @@ def get_directive(self, name):
9486

9587
return None
9688

97-
def _build_type_map(self, _types):
98-
types = [
99-
self.get_query_type(),
100-
self.get_mutation_type(),
101-
self.get_subscription_type(),
102-
IntrospectionSchema
103-
]
104-
if _types:
105-
types += _types
106-
107-
type_map = reduce(self._type_map_reducer, types, OrderedDict())
108-
return type_map
109-
11089
def get_possible_types(self, abstract_type):
111-
if isinstance(abstract_type, GraphQLUnionType):
112-
return abstract_type.get_types()
113-
assert isinstance(abstract_type, GraphQLInterfaceType)
114-
return self._implementations[abstract_type.name]
90+
return self._type_map.get_possible_types(abstract_type)
11591

11692
def is_possible_type(self, abstract_type, possible_type):
117-
if not self._possible_type_map[abstract_type.name]:
118-
possible_types = self.get_possible_types(abstract_type)
119-
self._possible_type_map[abstract_type.name].update([p.name for p in possible_types])
120-
121-
return possible_type.name in self._possible_type_map[abstract_type.name]
122-
123-
def _type_map_reducer(self, map, type):
124-
if not type:
125-
return map
126-
127-
if isinstance(type, GraphQLList) or isinstance(type, GraphQLNonNull):
128-
return self._type_map_reducer(map, type.of_type)
129-
130-
if type.name in map:
131-
assert map[type.name] == type, (
132-
'Schema must contain unique named types but contains multiple types named "{}".'
133-
).format(type.name)
134-
135-
return map
136-
137-
map[type.name] = type
138-
139-
reduced_map = map
140-
141-
if isinstance(type, (GraphQLUnionType)):
142-
for t in type.get_types():
143-
reduced_map = self._type_map_reducer(reduced_map, t)
144-
145-
if isinstance(type, GraphQLObjectType):
146-
for t in type.get_interfaces():
147-
reduced_map = self._type_map_reducer(reduced_map, t)
148-
149-
if isinstance(type, (GraphQLObjectType, GraphQLInterfaceType, GraphQLInputObjectType)):
150-
field_map = type.get_fields()
151-
type_is_input = isinstance(type, GraphQLInputObjectType)
152-
for field_name, field in field_map.items():
153-
if type_is_input:
154-
assert isinstance(field, GraphQLInputObjectField), (
155-
'{}.{} must be an instance of GraphQLInputObjectField.'.format(type, field_name)
156-
)
157-
assert is_input_type(field.type), (
158-
'{}.{} field type must be Input Type but got: {}.'.format(type, field_name, field.type)
159-
)
160-
else:
161-
assert isinstance(field, (GraphQLField, GraphQLField)), (
162-
'{}.{} must be an instance of GraphQLField.'.format(type, field_name)
163-
)
164-
assert is_output_type(field.type), (
165-
'{}.{} field type must be Output Type but got: {}.'.format(type, field_name, field.type)
166-
)
167-
for arg_name, arg in field.args.items():
168-
assert isinstance(arg, (GraphQLArgument, GraphQLArgument)), (
169-
'{}.{}({}:) argument must be an instance of GraphQLArgument.'.format(type, field_name, arg_name)
170-
)
171-
assert is_input_type(arg.type), (
172-
'{}.{}({}:) argument type must be Input Type but got: {}.'.format(type, field_name, arg_name,
173-
arg.type)
174-
)
175-
reduced_map = self._type_map_reducer(reduced_map, arg.type)
176-
177-
reduced_map = self._type_map_reducer(reduced_map, getattr(field, 'type', None))
178-
179-
return reduced_map
180-
181-
182-
def assert_object_implements_interface(schema, object, interface):
183-
object_field_map = object.get_fields()
184-
interface_field_map = interface.get_fields()
185-
186-
for field_name, interface_field in interface_field_map.items():
187-
object_field = object_field_map.get(field_name)
188-
189-
assert object_field, '"{}" expects field "{}" but "{}" does not provide it.'.format(
190-
interface, field_name, object
191-
)
192-
193-
assert is_type_sub_type_of(schema, object_field.type, interface_field.type), (
194-
'{}.{} expects type "{}" but {}.{} provides type "{}".'
195-
).format(interface, field_name, interface_field.type, object, field_name, object_field.type)
196-
197-
for arg_name, interface_arg in interface_field.args.items():
198-
object_arg = object_field.args.get(arg_name)
199-
200-
assert object_arg, (
201-
'{}.{} expects argument "{}" but {}.{} does not provide it.'
202-
).format(interface, field_name, arg_name, object, field_name)
203-
204-
assert is_equal_type(interface_arg.type, object_arg.type), (
205-
'{}.{}({}:) expects type "{}" but {}.{}({}:) provides type "{}".'
206-
).format(interface, field_name, arg_name, interface_arg.type, object, field_name, arg_name, object_arg.type)
207-
208-
for arg_name, object_arg in object_field.args.items():
209-
interface_arg = interface_field.args.get(arg_name)
210-
if not interface_arg:
211-
assert not isinstance(object_arg.type, GraphQLNonNull), (
212-
'{}.{}({}:) is of required type '
213-
'"{}" but is not also provided by the '
214-
'interface {}.{}.'
215-
).format(object, field_name, arg_name, object_arg.type, interface, field_name)
93+
return self._type_map.is_possible_type(abstract_type, possible_type)

graphql/type/typemap.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
from collections import OrderedDict, defaultdict
2+
from functools import reduce
3+
4+
from ..utils.type_comparators import is_equal_type, is_type_sub_type_of
5+
from .definition import (GraphQLInputObjectType, GraphQLInterfaceType,
6+
GraphQLList, GraphQLNonNull, GraphQLObjectType,
7+
GraphQLUnionType, GraphQLInputObjectField,
8+
is_output_type, is_input_type, GraphQLField,
9+
GraphQLArgument)
10+
11+
12+
class GraphQLTypeMap(OrderedDict):
13+
14+
def __init__(self, types):
15+
super(GraphQLTypeMap, self).__init__()
16+
self.update(reduce(self.reducer, types, OrderedDict()))
17+
self._possible_type_map = defaultdict(set)
18+
19+
# Keep track of all implementations by interface name.
20+
self._implementations = defaultdict(list)
21+
for type in self.values():
22+
if isinstance(type, GraphQLObjectType):
23+
for interface in type.get_interfaces():
24+
self._implementations[interface.name].append(type)
25+
26+
# Enforce correct interface implementations.
27+
for type in self.values():
28+
if isinstance(type, GraphQLObjectType):
29+
for interface in type.get_interfaces():
30+
self.assert_object_implements_interface(self, type, interface)
31+
32+
def get_possible_types(self, abstract_type):
33+
if isinstance(abstract_type, GraphQLUnionType):
34+
return abstract_type.get_types()
35+
assert isinstance(abstract_type, GraphQLInterfaceType)
36+
return self._implementations[abstract_type.name]
37+
38+
def is_possible_type(self, abstract_type, possible_type):
39+
if not self._possible_type_map[abstract_type.name]:
40+
possible_types = self.get_possible_types(abstract_type)
41+
self._possible_type_map[abstract_type.name].update([p.name for p in possible_types])
42+
43+
return possible_type.name in self._possible_type_map[abstract_type.name]
44+
45+
@classmethod
46+
def reducer(cls, map, type):
47+
if not type:
48+
return map
49+
50+
if isinstance(type, GraphQLList) or isinstance(type, GraphQLNonNull):
51+
return cls.reducer(map, type.of_type)
52+
53+
if type.name in map:
54+
assert map[type.name] == type, (
55+
'Schema must contain unique named types but contains multiple types named "{}".'
56+
).format(type.name)
57+
58+
return map
59+
60+
map[type.name] = type
61+
62+
reduced_map = map
63+
64+
if isinstance(type, (GraphQLUnionType)):
65+
for t in type.get_types():
66+
reduced_map = cls.reducer(reduced_map, t)
67+
68+
if isinstance(type, GraphQLObjectType):
69+
for t in type.get_interfaces():
70+
reduced_map = cls.reducer(reduced_map, t)
71+
72+
if isinstance(type, (GraphQLObjectType, GraphQLInterfaceType, GraphQLInputObjectType)):
73+
field_map = type.get_fields()
74+
type_is_input = isinstance(type, GraphQLInputObjectType)
75+
for field_name, field in field_map.items():
76+
if type_is_input:
77+
assert isinstance(field, GraphQLInputObjectField), (
78+
'{}.{} must be an instance of GraphQLInputObjectField.'.format(type, field_name)
79+
)
80+
assert is_input_type(field.type), (
81+
'{}.{} field type must be Input Type but got: {}.'.format(type, field_name, field.type)
82+
)
83+
else:
84+
assert isinstance(field, (GraphQLField, GraphQLField)), (
85+
'{}.{} must be an instance of GraphQLField.'.format(type, field_name)
86+
)
87+
assert is_output_type(field.type), (
88+
'{}.{} field type must be Output Type but got: {}.'.format(type, field_name, field.type)
89+
)
90+
for arg_name, arg in field.args.items():
91+
assert isinstance(arg, (GraphQLArgument, GraphQLArgument)), (
92+
'{}.{}({}:) argument must be an instance of GraphQLArgument.'.format(type, field_name, arg_name)
93+
)
94+
assert is_input_type(arg.type), (
95+
'{}.{}({}:) argument type must be Input Type but got: {}.'.format(type, field_name, arg_name,
96+
arg.type)
97+
)
98+
reduced_map = cls.reducer(reduced_map, arg.type)
99+
100+
reduced_map = cls.reducer(reduced_map, getattr(field, 'type', None))
101+
102+
return reduced_map
103+
104+
@classmethod
105+
def assert_object_implements_interface(cls, schema, object, interface):
106+
object_field_map = object.get_fields()
107+
interface_field_map = interface.get_fields()
108+
109+
for field_name, interface_field in interface_field_map.items():
110+
object_field = object_field_map.get(field_name)
111+
112+
assert object_field, '"{}" expects field "{}" but "{}" does not provide it.'.format(
113+
interface, field_name, object
114+
)
115+
116+
assert is_type_sub_type_of(schema, object_field.type, interface_field.type), (
117+
'{}.{} expects type "{}" but {}.{} provides type "{}".'
118+
).format(interface, field_name, interface_field.type, object, field_name, object_field.type)
119+
120+
for arg_name, interface_arg in interface_field.args.items():
121+
object_arg = object_field.args.get(arg_name)
122+
123+
assert object_arg, (
124+
'{}.{} expects argument "{}" but {}.{} does not provide it.'
125+
).format(interface, field_name, arg_name, object, field_name)
126+
127+
assert is_equal_type(interface_arg.type, object_arg.type), (
128+
'{}.{}({}:) expects type "{}" but {}.{}({}:) provides type "{}".'
129+
).format(interface, field_name, arg_name, interface_arg.type, object, field_name, arg_name, object_arg.type)
130+
131+
for arg_name, object_arg in object_field.args.items():
132+
interface_arg = interface_field.args.get(arg_name)
133+
if not interface_arg:
134+
assert not isinstance(object_arg.type, GraphQLNonNull), (
135+
'{}.{}({}:) is of required type '
136+
'"{}" but is not also provided by the '
137+
'interface {}.{}.'
138+
).format(object, field_name, arg_name, object_arg.type, interface, field_name)

0 commit comments

Comments
 (0)