Skip to content

Commit 078c3a7

Browse files
committed
Add GraphQLSchema types field, refactored code and added extra comments
Related GraphQL-js commits: graphql/graphql-js@6a1f23e graphql/graphql-js@09be751
1 parent 747c64a commit 078c3a7

24 files changed

+333
-180
lines changed

graphql/execution/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def does_fragment_condition_match(ctx, fragment, type_):
230230
return True
231231

232232
if isinstance(conditional_type, (GraphQLInterfaceType, GraphQLUnionType)):
233-
return conditional_type.is_possible_type(type_)
233+
return ctx.schema.is_possible_type(conditional_type, type_)
234234

235235
return False
236236

graphql/execution/executor.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ def complete_abstract_value(exe_context, return_type, field_asts, info, result):
312312
if not runtime_type:
313313
return None
314314

315-
if not return_type.is_possible_type(runtime_type):
315+
schema = exe_context.schema
316+
if not schema.is_possible_type(return_type, runtime_type):
316317
raise GraphQLError(
317318
u'Runtime Object type "{}" is not a possible type for "{}".'.format(runtime_type, return_type),
318319
field_asts
@@ -322,7 +323,7 @@ def complete_abstract_value(exe_context, return_type, field_asts, info, result):
322323

323324

324325
def get_default_resolve_type_fn(value, info, abstract_type):
325-
possible_types = abstract_type.get_possible_types()
326+
possible_types = info.schema.get_possible_types(abstract_type)
326327
for type in possible_types:
327328
if callable(type.is_type_of) and type.is_type_of(value, info):
328329
return type

graphql/execution/tests/test_abstract.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ def test_is_type_of_used_to_resolve_runtime_type_for_interface():
8181
resolver=lambda *_: [Dog('Odie', True), Cat('Garfield', False)]
8282
)
8383
}
84-
)
84+
),
85+
types=[CatType, DogType]
8586
)
8687

8788
query = '''
@@ -136,7 +137,8 @@ def test_is_type_of_used_to_resolve_runtime_type_for_union():
136137
resolver=lambda *_: [Dog('Odie', True), Cat('Garfield', False)]
137138
)
138139
}
139-
)
140+
),
141+
types=[CatType, DogType]
140142
)
141143

142144
query = '''
@@ -206,7 +208,8 @@ def test_resolve_type_on_interface_yields_useful_error():
206208
resolver=lambda *_: [Dog('Odie', True), Cat('Garfield', False), Human('Jon')]
207209
)
208210
}
209-
)
211+
),
212+
types=[DogType, CatType]
210213
)
211214

212215
query = '''

graphql/execution/tests/test_resolve.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from collections import OrderedDict
21
import json
2+
from collections import OrderedDict
33

44
from graphql import graphql
5-
from graphql.type import GraphQLSchema, GraphQLString, GraphQLObjectType, GraphQLInt, GraphQLField, GraphQLArgument
5+
from graphql.type import (GraphQLArgument, GraphQLField, GraphQLInt,
6+
GraphQLObjectType, GraphQLSchema, GraphQLString)
67

78

89
def _test_schema(test_field):

graphql/execution/tests/test_union_interface.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def resolve_pet_type(value, info):
7373
is_type_of=lambda value, info: isinstance(value, Person)
7474
)
7575

76-
schema = GraphQLSchema(PersonType)
76+
schema = GraphQLSchema(query=PersonType, types=[PetType])
7777

7878
garfield = Cat('Garfield', False)
7979
odie = Dog('Odie', True)
@@ -114,7 +114,7 @@ def test_can_introspect_on_union_and_intersection_types():
114114
'kind': 'INTERFACE',
115115
'interfaces': None,
116116
'fields': [{'name': 'name'}],
117-
'possibleTypes': [{'name': 'Dog'}, {'name': 'Cat'}, {'name': 'Person'}],
117+
'possibleTypes': [{'name': 'Person'}, {'name': 'Dog'}, {'name': 'Cat'}],
118118
'inputFields': None
119119
},
120120
'Pet': {

graphql/type/definition.py

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ def __init__(self, name, fields, interfaces=None, is_type_of=None, description=N
187187
self._provided_interfaces = interfaces
188188
self._field_map = None
189189
self._interfaces = None
190-
add_impl_to_interfaces(self)
191190

192191
def get_fields(self):
193192
if self._field_map is None:
@@ -289,11 +288,6 @@ def define_interfaces(type, interfaces):
289288
return interfaces
290289

291290

292-
def add_impl_to_interfaces(impl):
293-
for type in impl.get_interfaces():
294-
type._impls.append(impl)
295-
296-
297291
class GraphQLField(object):
298292
__slots__ = 'name', 'type', 'args', 'resolver', 'deprecation_reason', 'description'
299293

@@ -360,7 +354,7 @@ class GraphQLInterfaceType(GraphQLType):
360354
'name': GraphQLField(GraphQLString),
361355
})
362356
"""
363-
__slots__ = 'name', 'description', 'resolve_type', '_fields', '_impls', '_field_map', '_possible_type_names'
357+
__slots__ = 'name', 'description', 'resolve_type', '_fields', '_field_map'
364358

365359
def __init__(self, name, fields=None, resolve_type=None, description=None):
366360
assert name, 'Type must be named.'
@@ -374,26 +368,14 @@ def __init__(self, name, fields=None, resolve_type=None, description=None):
374368
self.resolve_type = resolve_type
375369
self._fields = fields
376370

377-
self._impls = []
378371
self._field_map = None
379-
self._possible_type_names = None
380372

381373
def get_fields(self):
382374
if self._field_map is None:
383375
self._field_map = define_field_map(self, self._fields)
384376

385377
return self._field_map
386378

387-
def get_possible_types(self):
388-
return self._impls
389-
390-
def is_possible_type(self, type):
391-
if self._possible_type_names is None:
392-
self._possible_type_names = set(
393-
t.name for t in self.get_possible_types()
394-
)
395-
return type.name in self._possible_type_names
396-
397379

398380
class GraphQLUnionType(GraphQLType):
399381
"""Union Type Definition
@@ -413,7 +395,7 @@ def resolve_type(self, value):
413395
if isinstance(value, Cat):
414396
return CatType()
415397
"""
416-
__slots__ = 'name', 'description', 'resolve_type', '_types', '_possible_type_names', '_possible_types'
398+
__slots__ = 'name', 'description', 'resolve_type', '_types'
417399

418400
def __init__(self, name, types=None, resolve_type=None, description=None):
419401
assert name, 'Type must be named.'
@@ -425,23 +407,10 @@ def __init__(self, name, types=None, resolve_type=None, description=None):
425407
assert callable(resolve_type), '{} must provide "resolve_type" as a function.'.format(self)
426408

427409
self.resolve_type = resolve_type
428-
self._types = types
429-
self._possible_types = None
430-
self._possible_type_names = None
431-
432-
def get_possible_types(self):
433-
if self._possible_types is None:
434-
self._possible_types = define_types(self, self._types)
435-
436-
return self._possible_types
437-
438-
def is_possible_type(self, type):
439-
if self._possible_type_names is None:
440-
self._possible_type_names = set(
441-
t.name for t in self.get_possible_types()
442-
)
410+
self._types = define_types(self, types)
443411

444-
return type.name in self._possible_type_names
412+
def get_types(self):
413+
return self._types
445414

446415

447416
def define_types(union_type, types):

graphql/type/introspection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,9 @@ def interfaces(type, *_):
168168
return type.get_interfaces()
169169

170170
@staticmethod
171-
def possible_types(type, *_):
171+
def possible_types(type, args, info):
172172
if isinstance(type, (GraphQLInterfaceType, GraphQLUnionType)):
173-
return type.get_possible_types()
173+
return info.schema.get_possible_types(type)
174174

175175
@staticmethod
176176
def enum_values(type, args, *_):

graphql/type/schema.py

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from collections import OrderedDict
1+
from collections import Iterable, OrderedDict, defaultdict
22
from functools import reduce
33

44
from ..utils.type_comparators import is_equal_type, is_type_sub_type_of
@@ -23,9 +23,9 @@ class GraphQLSchema(object):
2323
mutation=MyAppMutationRootType
2424
)
2525
"""
26-
__slots__ = '_query', '_mutation', '_subscription', '_type_map', '_directives',
26+
__slots__ = '_query', '_mutation', '_subscription', '_type_map', '_directives', '_implementations', '_possible_type_map'
2727

28-
def __init__(self, query, mutation=None, subscription=None, directives=None):
28+
def __init__(self, query, mutation=None, subscription=None, directives=None, types=None):
2929
assert isinstance(query, GraphQLObjectType), 'Schema query must be Object Type but got: {}.'.format(query)
3030
if mutation:
3131
assert isinstance(mutation, GraphQLObjectType), \
@@ -35,6 +35,10 @@ def __init__(self, query, mutation=None, subscription=None, directives=None):
3535
assert isinstance(subscription, GraphQLObjectType), \
3636
'Schema subscription must be Object Type but got: {}.'.format(subscription)
3737

38+
if types:
39+
assert isinstance(types, Iterable), \
40+
'Schema types must be iterable if provided but got: {}.'.format(types)
41+
3842
self._query = query
3943
self._mutation = mutation
4044
self._subscription = subscription
@@ -50,13 +54,20 @@ def __init__(self, query, mutation=None, subscription=None, directives=None):
5054
)
5155

5256
self._directives = directives
53-
self._type_map = self._build_type_map()
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)
5465

5566
# Enforce correct interface implementations.
5667
for type in self._type_map.values():
5768
if isinstance(type, GraphQLObjectType):
5869
for interface in type.get_interfaces():
59-
assert_object_implements_interface(type, interface)
70+
assert_object_implements_interface(self, type, interface)
6071

6172
def get_query_type(self):
6273
return self._query
@@ -83,11 +94,32 @@ def get_directive(self, name):
8394

8495
return None
8596

86-
def _build_type_map(self):
87-
types = [self.get_query_type(), self.get_mutation_type(), self.get_subscription_type(), IntrospectionSchema]
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+
88107
type_map = reduce(type_map_reducer, types, OrderedDict())
89108
return type_map
90109

110+
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]
115+
116+
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+
91123

92124
def type_map_reducer(map, type):
93125
if not type:
@@ -107,8 +139,8 @@ def type_map_reducer(map, type):
107139

108140
reduced_map = map
109141

110-
if isinstance(type, (GraphQLUnionType, GraphQLInterfaceType)):
111-
for t in type.get_possible_types():
142+
if isinstance(type, (GraphQLUnionType)):
143+
for t in type.get_types():
112144
reduced_map = type_map_reducer(reduced_map, t)
113145

114146
if isinstance(type, GraphQLObjectType):
@@ -129,7 +161,7 @@ def type_map_reducer(map, type):
129161
return reduced_map
130162

131163

132-
def assert_object_implements_interface(object, interface):
164+
def assert_object_implements_interface(schema, object, interface):
133165
object_field_map = object.get_fields()
134166
interface_field_map = interface.get_fields()
135167

@@ -140,7 +172,7 @@ def assert_object_implements_interface(object, interface):
140172
interface, field_name, object
141173
)
142174

143-
assert is_type_sub_type_of(object_field.type, interface_field.type), (
175+
assert is_type_sub_type_of(schema, object_field.type, interface_field.type), (
144176
'{}.{} expects type "{}" but {}.{} provides type "{}".'
145177
).format(interface, field_name, interface_field.type, object, field_name, object_field.type)
146178

graphql/type/tests/test_definition.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def test_includes_interfaces_thunk_subtypes_in_the_type_map():
183183
fields={
184184
'iface': GraphQLField(SomeInterface)
185185
}
186-
))
186+
), types=[SomeSubtype])
187187

188188
assert schema.get_type_map()['SomeSubtype'] is SomeSubtype
189189

@@ -196,7 +196,7 @@ def test_includes_interfaces_subtypes_in_the_type_map():
196196
interfaces=[SomeInterface],
197197
is_type_of=lambda: None
198198
)
199-
schema = GraphQLSchema(query=GraphQLObjectType(name='Query', fields={'iface': GraphQLField(SomeInterface)}))
199+
schema = GraphQLSchema(query=GraphQLObjectType(name='Query', fields={'iface': GraphQLField(SomeInterface)}), types=[SomeSubtype])
200200
assert schema.get_type_map()['SomeSubtype'] == SomeSubtype
201201

202202

graphql/type/tests/test_validation.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ def schema_with_field_type(t):
107107
fields={
108108
'f': GraphQLField(t)
109109
}
110-
)
110+
),
111+
types=[t]
111112
)
112113

113114

@@ -218,7 +219,7 @@ def test_it_rejects_a_schema_which_have_same_named_objects_implementing_an_inter
218219
)
219220

220221
with raises(AssertionError) as excinfo:
221-
GraphQLSchema(query=QueryType)
222+
GraphQLSchema(query=QueryType, types=[FirstBadObject, SecondBadObject])
222223

223224
assert str(excinfo.value) == 'Schema must contain unique named types but contains multiple types named ' \
224225
'"BadObject".'

0 commit comments

Comments
 (0)