1
- # -*- coding: utf-8 -*-
2
- import logging
3
- from traceback import format_exception
4
-
5
- from ..error import GraphQLError
6
- from ..language import ast
7
- from ..pyutils .default_ordered_dict import DefaultOrderedDict
8
- from ..type .definition import GraphQLInterfaceType , GraphQLUnionType
9
- from ..type .directives import GraphQLIncludeDirective , GraphQLSkipDirective
10
- from ..type .introspection import (SchemaMetaFieldDef , TypeMetaFieldDef ,
11
- TypeNameMetaFieldDef )
12
- from ..utils .type_from_ast import type_from_ast
13
- from .values import get_argument_values , get_variable_values
14
-
15
- logger = logging .getLogger (__name__ )
16
-
17
-
18
- class ExecutionContext (object ):
19
- """Data that must be available at all points during query execution.
20
-
21
- Namely, schema of the type system that is currently executing,
22
- and the fragments defined in the query document"""
23
-
24
- __slots__ = 'schema' , 'fragments' , 'root_value' , 'operation' , 'variable_values' , 'errors' , 'context_value' , \
25
- 'argument_values_cache' , 'executor' , 'middleware' , 'allow_subscriptions' , '_subfields_cache'
26
-
27
- def __init__ (self , schema , document_ast , root_value , context_value , variable_values , operation_name , executor , middleware , allow_subscriptions ):
28
- """Constructs a ExecutionContext object from the arguments passed
29
- to execute, which we will pass throughout the other execution
30
- methods."""
31
- errors = []
32
- operation = None
33
- fragments = {}
34
-
35
- for definition in document_ast .definitions :
36
- if isinstance (definition , ast .OperationDefinition ):
37
- if not operation_name and operation :
38
- raise GraphQLError (
39
- 'Must provide operation name if query contains multiple operations.' )
40
-
41
- if not operation_name or definition .name and definition .name .value == operation_name :
42
- operation = definition
43
-
44
- elif isinstance (definition , ast .FragmentDefinition ):
45
- fragments [definition .name .value ] = definition
46
-
47
- else :
48
- raise GraphQLError (
49
- u'GraphQL cannot execute a request containing a {}.' .format (
50
- definition .__class__ .__name__ ),
51
- definition
52
- )
53
-
54
- if not operation :
55
- if operation_name :
56
- raise GraphQLError (
57
- u'Unknown operation named "{}".' .format (operation_name ))
58
-
59
- else :
60
- raise GraphQLError ('Must provide an operation.' )
61
-
62
- variable_values = get_variable_values (
63
- schema , operation .variable_definitions or [], variable_values )
64
-
65
- self .schema = schema
66
- self .fragments = fragments
67
- self .root_value = root_value
68
- self .operation = operation
69
- self .variable_values = variable_values
70
- self .errors = errors
71
- self .context_value = context_value
72
- self .argument_values_cache = {}
73
- self .executor = executor
74
- self .middleware = middleware
75
- self .allow_subscriptions = allow_subscriptions
76
- self ._subfields_cache = {}
77
-
78
- def get_field_resolver (self , field_resolver ):
79
- if not self .middleware :
80
- return field_resolver
81
- return self .middleware .get_field_resolver (field_resolver )
82
-
83
- def get_argument_values (self , field_def , field_ast ):
84
- k = field_def , field_ast
85
- result = self .argument_values_cache .get (k )
86
- if not result :
87
- result = self .argument_values_cache [k ] = get_argument_values (field_def .args , field_ast .arguments ,
88
- self .variable_values )
89
-
90
- return result
91
-
92
- def report_error (self , error , traceback = None ):
93
- exception = format_exception (type (error ), error , getattr (error , 'stack' , None ) or traceback )
94
- logger .error ('' .join (exception ))
95
- self .errors .append (error )
96
-
97
- def get_sub_fields (self , return_type , field_asts ):
98
- k = return_type , tuple (field_asts )
99
- if k not in self ._subfields_cache :
100
- subfield_asts = DefaultOrderedDict (list )
101
- visited_fragment_names = set ()
102
- for field_ast in field_asts :
103
- selection_set = field_ast .selection_set
104
- if selection_set :
105
- subfield_asts = collect_fields (
106
- self , return_type , selection_set ,
107
- subfield_asts , visited_fragment_names
108
- )
109
- self ._subfields_cache [k ] = subfield_asts
110
- return self ._subfields_cache [k ]
111
-
112
-
113
- class SubscriberExecutionContext (object ):
114
- __slots__ = 'exe_context' , 'errors'
115
-
116
- def __init__ (self , exe_context ):
117
- self .exe_context = exe_context
118
- self .errors = []
119
-
120
- def reset (self ):
121
- self .errors = []
122
-
123
- def __getattr__ (self , name ):
124
- return getattr (self .exe_context , name )
1
+ # We keep the following imports to preserve compatibility
2
+ from .utils import (
3
+ ExecutionContext ,
4
+ SubscriberExecutionContext ,
5
+ get_operation_root_type ,
6
+ collect_fields ,
7
+ should_include_node ,
8
+ does_fragment_condition_match ,
9
+ get_field_entry_key ,
10
+ default_resolve_fn ,
11
+ get_field_def
12
+ )
125
13
126
14
127
15
class ExecutionResult (object ):
@@ -152,149 +40,6 @@ def __eq__(self, other):
152
40
)
153
41
154
42
155
- def get_operation_root_type (schema , operation ):
156
- op = operation .operation
157
- if op == 'query' :
158
- return schema .get_query_type ()
159
-
160
- elif op == 'mutation' :
161
- mutation_type = schema .get_mutation_type ()
162
-
163
- if not mutation_type :
164
- raise GraphQLError (
165
- 'Schema is not configured for mutations' ,
166
- [operation ]
167
- )
168
-
169
- return mutation_type
170
-
171
- elif op == 'subscription' :
172
- subscription_type = schema .get_subscription_type ()
173
-
174
- if not subscription_type :
175
- raise GraphQLError (
176
- 'Schema is not configured for subscriptions' ,
177
- [operation ]
178
- )
179
-
180
- return subscription_type
181
-
182
- raise GraphQLError (
183
- 'Can only execute queries, mutations and subscriptions' ,
184
- [operation ]
185
- )
186
-
187
-
188
- def collect_fields (ctx , runtime_type , selection_set , fields , prev_fragment_names ):
189
- """
190
- Given a selectionSet, adds all of the fields in that selection to
191
- the passed in map of fields, and returns it at the end.
192
-
193
- collect_fields requires the "runtime type" of an object. For a field which
194
- returns and Interface or Union type, the "runtime type" will be the actual
195
- Object type returned by that field.
196
- """
197
- for selection in selection_set .selections :
198
- directives = selection .directives
199
-
200
- if isinstance (selection , ast .Field ):
201
- if not should_include_node (ctx , directives ):
202
- continue
203
-
204
- name = get_field_entry_key (selection )
205
- fields [name ].append (selection )
206
-
207
- elif isinstance (selection , ast .InlineFragment ):
208
- if not should_include_node (
209
- ctx , directives ) or not does_fragment_condition_match (
210
- ctx , selection , runtime_type ):
211
- continue
212
-
213
- collect_fields (ctx , runtime_type ,
214
- selection .selection_set , fields , prev_fragment_names )
215
-
216
- elif isinstance (selection , ast .FragmentSpread ):
217
- frag_name = selection .name .value
218
-
219
- if frag_name in prev_fragment_names or not should_include_node (ctx , directives ):
220
- continue
221
-
222
- prev_fragment_names .add (frag_name )
223
- fragment = ctx .fragments .get (frag_name )
224
- frag_directives = fragment .directives
225
- if not fragment or not \
226
- should_include_node (ctx , frag_directives ) or not \
227
- does_fragment_condition_match (ctx , fragment , runtime_type ):
228
- continue
229
-
230
- collect_fields (ctx , runtime_type ,
231
- fragment .selection_set , fields , prev_fragment_names )
232
-
233
- return fields
234
-
235
-
236
- def should_include_node (ctx , directives ):
237
- """Determines if a field should be included based on the @include and
238
- @skip directives, where @skip has higher precidence than @include."""
239
- # TODO: Refactor based on latest code
240
- if directives :
241
- skip_ast = None
242
-
243
- for directive in directives :
244
- if directive .name .value == GraphQLSkipDirective .name :
245
- skip_ast = directive
246
- break
247
-
248
- if skip_ast :
249
- args = get_argument_values (
250
- GraphQLSkipDirective .args ,
251
- skip_ast .arguments ,
252
- ctx .variable_values ,
253
- )
254
- if args .get ('if' ) is True :
255
- return False
256
-
257
- include_ast = None
258
-
259
- for directive in directives :
260
- if directive .name .value == GraphQLIncludeDirective .name :
261
- include_ast = directive
262
- break
263
-
264
- if include_ast :
265
- args = get_argument_values (
266
- GraphQLIncludeDirective .args ,
267
- include_ast .arguments ,
268
- ctx .variable_values ,
269
- )
270
-
271
- if args .get ('if' ) is False :
272
- return False
273
-
274
- return True
275
-
276
-
277
- def does_fragment_condition_match (ctx , fragment , type_ ):
278
- type_condition_ast = fragment .type_condition
279
- if not type_condition_ast :
280
- return True
281
-
282
- conditional_type = type_from_ast (ctx .schema , type_condition_ast )
283
- if conditional_type .is_same_type (type_ ):
284
- return True
285
-
286
- if isinstance (conditional_type , (GraphQLInterfaceType , GraphQLUnionType )):
287
- return ctx .schema .is_possible_type (conditional_type , type_ )
288
-
289
- return False
290
-
291
-
292
- def get_field_entry_key (node ):
293
- """Implements the logic to compute the key of a given field's entry"""
294
- if node .alias :
295
- return node .alias .value
296
- return node .name .value
297
-
298
43
299
44
class ResolveInfo (object ):
300
45
__slots__ = ('field_name' , 'field_asts' , 'return_type' , 'parent_type' ,
@@ -313,30 +58,3 @@ def __init__(self, field_name, field_asts, return_type, parent_type,
313
58
self .variable_values = variable_values
314
59
self .context = context
315
60
self .path = path
316
-
317
-
318
- def default_resolve_fn (source , info , ** args ):
319
- """If a resolve function is not given, then a default resolve behavior is used which takes the property of the source object
320
- of the same name as the field and returns it as the result, or if it's a function, returns the result of calling that function."""
321
- name = info .field_name
322
- property = getattr (source , name , None )
323
- if callable (property ):
324
- return property ()
325
- return property
326
-
327
-
328
- def get_field_def (schema , parent_type , field_name ):
329
- """This method looks up the field on the given type defintion.
330
- It has special casing for the two introspection fields, __schema
331
- and __typename. __typename is special because it can always be
332
- queried as a field, even in situations where no other fields
333
- are allowed, like on a Union. __schema could get automatically
334
- added to the query type, but that would require mutating type
335
- definitions, which would cause issues."""
336
- if field_name == '__schema' and schema .get_query_type () == parent_type :
337
- return SchemaMetaFieldDef
338
- elif field_name == '__type' and schema .get_query_type () == parent_type :
339
- return TypeMetaFieldDef
340
- elif field_name == '__typename' :
341
- return TypeNameMetaFieldDef
342
- return parent_type .fields .get (field_name )
0 commit comments