1
1
# -*- coding: utf-8 -*-
2
- import collections
3
- from ..error import GraphQLError , format_error
2
+ from ..error import GraphQLError
4
3
from ..language import ast
5
4
from ..type .definition import (
6
- GraphQLEnumType ,
7
5
GraphQLInterfaceType ,
8
- GraphQLList ,
9
- GraphQLNonNull ,
10
- GraphQLObjectType ,
11
- GraphQLScalarType ,
12
6
GraphQLUnionType ,
13
7
)
14
8
from ..type .directives import (
20
14
TypeMetaFieldDef ,
21
15
TypeNameMetaFieldDef ,
22
16
)
23
- from ..utils import is_nullish , type_from_ast
17
+ from ..utils import type_from_ast
24
18
from .values import get_argument_values , get_variable_values
25
19
26
20
Undefined = object ()
27
21
28
-
29
22
"""
30
23
Terminology
31
24
@@ -52,6 +45,7 @@ class ExecutionContext(object):
52
45
53
46
Namely, schema of the type system that is currently executing,
54
47
and the fragments defined in the query document"""
48
+
55
49
def __init__ (self , schema , root , document_ast , operation_name , args ):
56
50
"""Constructs a ExecutionContext object from the arguments passed
57
51
to execute, which we will pass throughout the other execution
@@ -96,27 +90,13 @@ def __init__(self, data, errors=None):
96
90
97
91
98
92
def execute (schema , root , ast , operation_name = '' , args = None ):
99
- """Implements the "Evaluating requests" section of the spec."""
100
- assert schema , 'Must provide schema'
101
- ctx = ExecutionContext (schema , root , ast , operation_name , args )
102
- try :
103
- data = execute_operation (ctx , root , ctx .operation )
104
- except Exception as e :
105
- ctx .errors .append (e )
106
- data = None
107
- if not ctx .errors :
108
- return ExecutionResult (data )
109
- formatted_errors = list (map (format_error , ctx .errors ))
110
- return ExecutionResult (data , formatted_errors )
111
-
112
-
113
- def execute_operation (ctx , root , operation ):
114
- """Implements the "Evaluating operations" section of the spec."""
115
- type = get_operation_root_type (ctx .schema , operation )
116
- fields = collect_fields (ctx , type , operation .selection_set , {}, set ())
117
- if operation .operation == 'mutation' :
118
- return execute_fields_serially (ctx , type , root , fields )
119
- return execute_fields (ctx , type , root , fields )
93
+ """
94
+ Executes an AST synchronously. Assumes that the AST is already validated.
95
+ """
96
+ from .parallel_execution import Executor
97
+ from .middlewares import SynchronousExecutionMiddleware
98
+ e = Executor (schema , [SynchronousExecutionMiddleware .SynchronousExecutionMiddleware ()])
99
+ return e .execute (ast , root , args , operation_name , validate_ast = False )
120
100
121
101
122
102
def get_operation_root_type (schema , operation ):
@@ -137,24 +117,6 @@ def get_operation_root_type(schema, operation):
137
117
)
138
118
139
119
140
- def execute_fields_serially (ctx , parent_type , source , fields ):
141
- """Implements the "Evaluating selection sets" section of the spec
142
- for "write" mode."""
143
- results = {}
144
- for response_name , field_asts in fields .items ():
145
- result = resolve_field (ctx , parent_type , source , field_asts )
146
- if result is not Undefined :
147
- results [response_name ] = result
148
- return results
149
-
150
-
151
- def execute_fields (ctx , parent_type , source , fields ):
152
- """Implements the "Evaluating selection sets" section of the spec
153
- for "read" mode."""
154
- # FIXME: just fallback to serial execution for now.
155
- return execute_fields_serially (ctx , parent_type , source , fields )
156
-
157
-
158
120
def collect_fields (ctx , type , selection_set , fields , prev_fragment_names ):
159
121
for selection in selection_set .selections :
160
122
directives = selection .directives
@@ -268,136 +230,6 @@ def variable_values(self):
268
230
return self .context .variables
269
231
270
232
271
- def resolve_field (ctx , parent_type , source , field_asts ):
272
- """A wrapper function for resolving the field, that catches the error
273
- and adds it to the context's global if the error is not rethrowable."""
274
- field_ast = field_asts [0 ]
275
- field_name = field_ast .name .value
276
-
277
- field_def = get_field_def (ctx .schema , parent_type , field_name )
278
- if not field_def :
279
- return Undefined
280
-
281
- return_type = field_def .type
282
- resolve_fn = field_def .resolver or default_resolve_fn
283
-
284
- # Build a dict of arguments from the field.arguments AST, using the variables scope to fulfill any variable references.
285
- # TODO: find a way to memoize, in case this field is within a list type.
286
- args = get_argument_values (
287
- field_def .args , field_ast .arguments , ctx .variables
288
- )
289
-
290
- # The resolve function's optional third argument is a collection of
291
- # information about the current execution state.
292
- info = ResolveInfo (
293
- field_name ,
294
- field_asts ,
295
- return_type ,
296
- parent_type ,
297
- ctx
298
- )
299
-
300
- # If an error occurs while calling the field `resolve` function, ensure that it is wrapped as a GraphQLError with locations.
301
- # Log this error and return null if allowed, otherwise throw the error so the parent field can handle it.
302
- try :
303
- result = resolve_fn (source , args , info )
304
- except Exception as e :
305
- reported_error = GraphQLError (str (e ), [field_ast ], e )
306
- if isinstance (return_type , GraphQLNonNull ):
307
- raise reported_error
308
- ctx .errors .append (reported_error )
309
- return None
310
-
311
- return complete_value_catching_error (
312
- ctx , return_type , field_asts , info , result
313
- )
314
-
315
-
316
- def complete_value_catching_error (ctx , return_type , field_asts , info , result ):
317
- # If the field type is non-nullable, then it is resolved without any
318
- # protection from errors.
319
- if isinstance (return_type , GraphQLNonNull ):
320
- return complete_value (ctx , return_type , field_asts , info , result )
321
-
322
- # Otherwise, error protection is applied, logging the error and
323
- # resolving a null value for this field if one is encountered.
324
- try :
325
- return complete_value (ctx , return_type , field_asts , info , result )
326
- except Exception as e :
327
- ctx .errors .append (e )
328
- return None
329
-
330
-
331
- def complete_value (ctx , return_type , field_asts , info , result ):
332
- """Implements the instructions for completeValue as defined in the
333
- "Field entries" section of the spec.
334
-
335
- If the field type is Non-Null, then this recursively completes the value for the inner type. It throws a field error
336
- if that completion returns null, as per the "Nullability" section of the spec.
337
-
338
- If the field type is a List, then this recursively completes the value for the inner type on each item in the list.
339
-
340
- If the field type is a Scalar or Enum, ensures the completed value is a legal value of the type by calling the `serialize`
341
- method of GraphQL type definition.
342
-
343
- Otherwise, the field type expects a sub-selection set, and will complete the value by evaluating all sub-selections."""
344
- # If field type is NonNull, complete for inner type, and throw field error if result is null.
345
- if isinstance (return_type , GraphQLNonNull ):
346
- completed = complete_value (
347
- ctx , return_type .of_type , field_asts , info , result
348
- )
349
- if completed is None :
350
- raise GraphQLError (
351
- 'Cannot return null for non-nullable type.' ,
352
- field_asts
353
- )
354
- return completed
355
-
356
- # If result is null-like, return null.
357
- if is_nullish (result ):
358
- return None
359
-
360
- # If field type is List, complete each item in the list with the inner type
361
- if isinstance (return_type , GraphQLList ):
362
- assert isinstance (result , collections .Iterable ), \
363
- 'User Error: expected iterable, but did not find one.'
364
-
365
- item_type = return_type .of_type
366
- return [complete_value_catching_error (
367
- ctx , item_type , field_asts , info , item
368
- ) for item in result ]
369
-
370
- # If field type is Scalar or Enum, serialize to a valid value, returning null if coercion is not possible.
371
- if isinstance (return_type , (GraphQLScalarType , GraphQLEnumType )):
372
- serialized_result = return_type .serialize (result )
373
- if is_nullish (serialized_result ):
374
- return None
375
- return serialized_result
376
-
377
- # Field type must be Object, Interface or Union and expect sub-selections.
378
- if isinstance (return_type , GraphQLObjectType ):
379
- object_type = return_type
380
- elif isinstance (return_type , (GraphQLInterfaceType , GraphQLUnionType )):
381
- object_type = return_type .resolve_type (result )
382
- else :
383
- object_type = None
384
-
385
- if not object_type :
386
- return None
387
-
388
- # Collect sub-fields to execute to complete this value.
389
- subfield_asts = {}
390
- visited_fragment_names = set ()
391
- for field_ast in field_asts :
392
- selection_set = field_ast .selection_set
393
- if selection_set :
394
- subfield_asts = collect_fields (
395
- ctx , object_type , selection_set ,
396
- subfield_asts , visited_fragment_names )
397
-
398
- return execute_fields (ctx , object_type , result , subfield_asts )
399
-
400
-
401
233
def default_resolve_fn (source , args , info ):
402
234
"""If a resolve function is not given, then a default resolve behavior is used which takes the property of the source object
403
235
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."""
0 commit comments