1
1
# -*- coding: utf-8 -*-
2
- from ..error import GraphQLError
3
- from ..language import ast
4
- from ..type .definition import (
5
- GraphQLInterfaceType ,
6
- GraphQLUnionType ,
7
- )
8
- from ..type .directives import (
9
- GraphQLIncludeDirective ,
10
- GraphQLSkipDirective ,
11
- )
12
- from ..type .introspection import (
13
- SchemaMetaFieldDef ,
14
- TypeMetaFieldDef ,
15
- TypeNameMetaFieldDef ,
16
- )
17
- from ..utils import type_from_ast
18
- from .values import get_argument_values , get_variable_values
19
-
20
- Undefined = object ()
21
-
22
2
"""
23
3
Terminology
24
4
39
19
3) inline fragment "spreads" e.g. "...on Type { a }"
40
20
"""
41
21
42
-
43
- class ExecutionContext (object ):
44
- """Data that must be available at all points during query execution.
45
-
46
- Namely, schema of the type system that is currently executing,
47
- and the fragments defined in the query document"""
48
-
49
- def __init__ (self , schema , root , document_ast , operation_name , args ):
50
- """Constructs a ExecutionContext object from the arguments passed
51
- to execute, which we will pass throughout the other execution
52
- methods."""
53
- errors = []
54
- operations = {}
55
- fragments = {}
56
- for statement in document_ast .definitions :
57
- if isinstance (statement , ast .OperationDefinition ):
58
- name = ''
59
- if statement .name :
60
- name = statement .name .value
61
- operations [name ] = statement
62
- elif isinstance (statement , ast .FragmentDefinition ):
63
- fragments [statement .name .value ] = statement
64
- if not operation_name and len (operations ) != 1 :
65
- raise GraphQLError (
66
- 'Must provide operation name '
67
- 'if query contains multiple operations' )
68
- op_name = operation_name or next (iter (operations .keys ()))
69
- operation = operations .get (op_name )
70
- if not operation :
71
- raise GraphQLError ('Unknown operation name: {}' .format (op_name ))
72
- variables = get_variable_values (schema , operation .variable_definitions or [], args )
73
-
74
- self .schema = schema
75
- self .fragments = fragments
76
- self .root = root
77
- self .operation = operation
78
- self .variables = variables
79
- self .errors = errors
80
-
81
-
82
- class ExecutionResult (object ):
83
- """The result of execution. `data` is the result of executing the
84
- query, `errors` is null if no errors occurred, and is a
85
- non-empty array if an error occurred."""
86
-
87
- def __init__ (self , data , errors = None ):
88
- self .data = data
89
- self .errors = errors
22
+ from .base import ExecutionResult
23
+ from .executor import Executor
24
+ from .middlewares .sync import SynchronousExecutionMiddleware
90
25
91
26
92
27
def execute (schema , root , ast , operation_name = '' , args = None ):
93
28
"""
94
29
Executes an AST synchronously. Assumes that the AST is already validated.
95
30
"""
96
- from .parallel_execution import Executor
97
- from .middlewares import SynchronousExecutionMiddleware
98
- e = Executor (schema , [SynchronousExecutionMiddleware .SynchronousExecutionMiddleware ()])
31
+ e = Executor (schema , [SynchronousExecutionMiddleware ()])
99
32
return e .execute (ast , root , args , operation_name , validate_ast = False )
100
33
101
34
102
- def get_operation_root_type (schema , operation ):
103
- op = operation .operation
104
- if op == 'query' :
105
- return schema .get_query_type ()
106
- elif op == 'mutation' :
107
- mutation_type = schema .get_mutation_type ()
108
- if not mutation_type :
109
- raise GraphQLError (
110
- 'Schema is not configured for mutations' ,
111
- [operation ]
112
- )
113
- return mutation_type
114
- raise GraphQLError (
115
- 'Can only execute queries and mutations' ,
116
- [operation ]
117
- )
118
-
119
-
120
- def collect_fields (ctx , type , selection_set , fields , prev_fragment_names ):
121
- for selection in selection_set .selections :
122
- directives = selection .directives
123
- if isinstance (selection , ast .Field ):
124
- if not should_include_node (ctx , directives ):
125
- continue
126
- name = get_field_entry_key (selection )
127
- if name not in fields :
128
- fields [name ] = []
129
- fields [name ].append (selection )
130
- elif isinstance (selection , ast .InlineFragment ):
131
- if not should_include_node (ctx , directives ) or \
132
- not does_fragment_condition_match (ctx , selection , type ):
133
- continue
134
- collect_fields (
135
- ctx , type , selection .selection_set ,
136
- fields , prev_fragment_names )
137
- elif isinstance (selection , ast .FragmentSpread ):
138
- frag_name = selection .name .value
139
- if frag_name in prev_fragment_names or \
140
- not should_include_node (ctx , directives ):
141
- continue
142
- prev_fragment_names .add (frag_name )
143
- fragment = ctx .fragments .get (frag_name )
144
- frag_directives = fragment .directives
145
- if not fragment or \
146
- not should_include_node (ctx , frag_directives ) or \
147
- not does_fragment_condition_match (ctx , fragment , type ):
148
- continue
149
- collect_fields (
150
- ctx , type , fragment .selection_set ,
151
- fields , prev_fragment_names )
152
- return fields
153
-
154
-
155
- def should_include_node (ctx , directives ):
156
- """Determines if a field should be included based on the @include and
157
- @skip directives, where @skip has higher precidence than @include."""
158
- if directives :
159
- skip_ast = None
160
- for directive in directives :
161
- if directive .name .value == GraphQLSkipDirective .name :
162
- skip_ast = directive
163
- break
164
- if skip_ast :
165
- args = get_argument_values (
166
- GraphQLSkipDirective .args ,
167
- skip_ast .arguments ,
168
- ctx .variables ,
169
- )
170
- return not args .get ('if' )
171
-
172
- include_ast = None
173
- for directive in directives :
174
- if directive .name .value == GraphQLIncludeDirective .name :
175
- include_ast = directive
176
- break
177
- if include_ast :
178
- args = get_argument_values (
179
- GraphQLIncludeDirective .args ,
180
- include_ast .arguments ,
181
- ctx .variables ,
182
- )
183
- return bool (args .get ('if' ))
184
-
185
- return True
186
-
187
-
188
- def does_fragment_condition_match (ctx , fragment , type_ ):
189
- conditional_type = type_from_ast (ctx .schema , fragment .type_condition )
190
- if type (conditional_type ) == type (type_ ):
191
- return True
192
- if isinstance (conditional_type , (GraphQLInterfaceType , GraphQLUnionType )):
193
- return conditional_type .is_possible_type (type_ )
194
- return False
195
-
196
-
197
- def get_field_entry_key (node ):
198
- """Implements the logic to compute the key of a given field’s entry"""
199
- if node .alias :
200
- return node .alias .value
201
- return node .name .value
202
-
203
-
204
- class ResolveInfo (object ):
205
- def __init__ (self , field_name , field_asts , return_type , parent_type , context ):
206
- self .field_name = field_name
207
- self .field_asts = field_asts
208
- self .return_type = return_type
209
- self .parent_type = parent_type
210
- self .context = context
211
-
212
- @property
213
- def schema (self ):
214
- return self .context .schema
215
-
216
- @property
217
- def fragments (self ):
218
- return self .context .fragments
219
-
220
- @property
221
- def root_value (self ):
222
- return self .context .root_value
223
-
224
- @property
225
- def operation (self ):
226
- return self .context .operation
227
-
228
- @property
229
- def variable_values (self ):
230
- return self .context .variables
231
-
232
-
233
- def default_resolve_fn (source , args , info ):
234
- """If a resolve function is not given, then a default resolve behavior is used which takes the property of the source object
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."""
236
- name = info .field_name
237
- property = getattr (source , name , None )
238
- if callable (property ):
239
- return property ()
240
- return property
241
-
242
-
243
- def get_field_def (schema , parent_type , field_name ):
244
- """This method looks up the field on the given type defintion.
245
- It has special casing for the two introspection fields, __schema
246
- and __typename. __typename is special because it can always be
247
- queried as a field, even in situations where no other fields
248
- are allowed, like on a Union. __schema could get automatically
249
- added to the query type, but that would require mutating type
250
- definitions, which would cause issues."""
251
- if field_name == SchemaMetaFieldDef .name and schema .get_query_type () == parent_type :
252
- return SchemaMetaFieldDef
253
- elif field_name == TypeMetaFieldDef .name and schema .get_query_type () == parent_type :
254
- return TypeMetaFieldDef
255
- elif field_name == TypeNameMetaFieldDef .name :
256
- return TypeNameMetaFieldDef
257
- return parent_type .get_fields ().get (field_name )
35
+ __all__ = ['ExecutionResult' , 'Executor' , 'execute' ]
0 commit comments