Skip to content

Commit 1b4707b

Browse files
committed
AST to object (namedtuple)
1 parent 417062f commit 1b4707b

20 files changed

+275
-335
lines changed

graphql/core/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .executor import execute
2-
from .language import Source, parse
2+
from .language.source import Source
3+
from .language.parser import parse
34

45

56
def graphql(schema, request='', root=None, vars=None, operation_name=None):

graphql/core/executor/executor.py

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import re
44
from ..error import GraphQLError, format_error
55
from ..utils import type_from_ast, is_nullish
6-
from ..language import kinds as Kind
6+
from ..language import ast
77
from .values import get_variable_values, get_argument_values
88
from ..type.definition import (
99
GraphQLScalarType,
@@ -53,21 +53,21 @@ class ExecutionContext(object):
5353
5454
Namely, schema of the type system that is currently executing,
5555
and the fragments defined in the query document"""
56-
def __init__(self, schema, root, ast, operation_name, args):
56+
def __init__(self, schema, root, document_ast, operation_name, args):
5757
"""Constructs a ExecutionContext object from the arguments passed
5858
to execute, which we will pass throughout the other execution
5959
methods."""
6060
errors = []
6161
operations = {}
6262
fragments = {}
63-
for statement in ast['definitions']:
64-
if statement['kind'] == Kind.OPERATION_DEFINITION:
63+
for statement in document_ast.definitions:
64+
if isinstance(statement, ast.OperationDefinition):
6565
name = ''
66-
if statement.get('name'):
67-
name = statement['name']['value']
66+
if statement.name:
67+
name = statement.name.value
6868
operations[name] = statement
69-
elif statement['kind'] == Kind.FRAGMENT_DEFINITION:
70-
fragments[statement['name']['value']] = statement
69+
elif isinstance(statement, ast.FragmentDefinition):
70+
fragments[statement.name.value] = statement
7171
if not operation_name and len(operations) != 1:
7272
raise GraphQLError(
7373
'Must provide operation name '
@@ -76,7 +76,7 @@ def __init__(self, schema, root, ast, operation_name, args):
7676
operation = operations.get(op_name)
7777
if not operation:
7878
raise GraphQLError('Unknown operation name: {}'.format(op_name))
79-
variables = get_variable_values(schema, operation['variableDefinitions'] or [], args)
79+
variables = get_variable_values(schema, operation.variable_definitions or [], args)
8080

8181
self.schema = schema
8282
self.fragments = fragments
@@ -113,14 +113,14 @@ def execute(schema, root, ast, operation_name='', args=None):
113113
def execute_operation(ctx, root, operation):
114114
"""Implements the "Evaluating operations" section of the spec."""
115115
type = get_operation_root_type(ctx.schema, operation)
116-
fields = collect_fields(ctx, type, operation['selectionSet'], {}, set())
117-
if operation['operation'] == 'mutation':
116+
fields = collect_fields(ctx, type, operation.selection_set, {}, set())
117+
if operation.operation == 'mutation':
118118
return execute_fields_serially(ctx, type, root, fields)
119119
return execute_fields(ctx, type, root, fields)
120120

121121

122122
def get_operation_root_type(schema, operation):
123-
op = operation['operation']
123+
op = operation.operation
124124
if op == 'query':
125125
return schema.get_query_type()
126126
elif op == 'mutation':
@@ -156,37 +156,36 @@ def execute_fields(ctx, parent_type, source, fields):
156156

157157

158158
def collect_fields(ctx, type, selection_set, fields, prev_fragment_names):
159-
for selection in selection_set['selections']:
160-
kind = selection['kind']
161-
directives = selection.get('directives')
162-
if kind == Kind.FIELD:
159+
for selection in selection_set.selections:
160+
directives = selection.directives
161+
if isinstance(selection, ast.Field):
163162
if not should_include_node(ctx, directives):
164163
continue
165164
name = get_field_entry_key(selection)
166165
if name not in fields:
167166
fields[name] = []
168167
fields[name].append(selection)
169-
elif kind == Kind.INLINE_FRAGMENT:
168+
elif isinstance(selection, ast.InlineFragment):
170169
if not should_include_node(ctx, directives) or \
171170
not does_fragment_condition_match(ctx, selection, type):
172171
continue
173172
collect_fields(
174-
ctx, type, selection['selectionSet'],
173+
ctx, type, selection.selection_set,
175174
fields, prev_fragment_names)
176-
elif kind == Kind.FRAGMENT_SPREAD:
177-
frag_name = selection['name']['value']
175+
elif isinstance(selection, ast.FragmentSpread):
176+
frag_name = selection.name.value
178177
if frag_name in prev_fragment_names or \
179178
not should_include_node(ctx, directives):
180179
continue
181180
prev_fragment_names.add(frag_name)
182181
fragment = ctx.fragments.get(frag_name)
183-
frag_directives = fragment.get('directives')
182+
frag_directives = fragment.directives
184183
if not fragment or \
185184
not should_include_node(ctx, frag_directives) or \
186185
not does_fragment_condition_match(ctx, fragment, type):
187186
continue
188187
collect_fields(
189-
ctx, type, fragment['selectionSet'],
188+
ctx, type, fragment.selection_set,
190189
fields, prev_fragment_names)
191190
return fields
192191

@@ -197,26 +196,26 @@ def should_include_node(ctx, directives):
197196
if directives:
198197
skip_ast = None
199198
for directive in directives:
200-
if directive['name']['value'] == GraphQLSkipDirective.name:
199+
if directive.name.value == GraphQLSkipDirective.name:
201200
skip_ast = directive
202201
break
203202
if skip_ast:
204203
args = get_argument_values(
205204
GraphQLSkipDirective.args,
206-
skip_ast['arguments'],
205+
skip_ast.arguments,
207206
ctx.variables,
208207
)
209208
return not args.get('if')
210209

211210
include_ast = None
212211
for directive in directives:
213-
if directive['name']['value'] == GraphQLIncludeDirective.name:
212+
if directive.name.value == GraphQLIncludeDirective.name:
214213
include_ast = directive
215214
break
216215
if include_ast:
217216
args = get_argument_values(
218217
GraphQLIncludeDirective.args,
219-
include_ast['arguments'],
218+
include_ast.arguments,
220219
ctx.variables,
221220
)
222221
return bool(args.get('if'))
@@ -225,7 +224,7 @@ def should_include_node(ctx, directives):
225224

226225

227226
def does_fragment_condition_match(ctx, fragment, type_):
228-
conditional_type = type_from_ast(ctx.schema, fragment['typeCondition'])
227+
conditional_type = type_from_ast(ctx.schema, fragment.type_condition)
229228
if type(conditional_type) == type(type_):
230229
return True
231230
if isinstance(conditional_type, (GraphQLInterfaceType, GraphQLUnionType)):
@@ -235,9 +234,9 @@ def does_fragment_condition_match(ctx, fragment, type_):
235234

236235
def get_field_entry_key(node):
237236
"""Implements the logic to compute the key of a given field’s entry"""
238-
if node['alias']:
239-
return node['alias']['value']
240-
return node['name']['value']
237+
if node.alias:
238+
return node.alias.value
239+
return node.name.value
241240

242241

243242
def resolve_field(ctx, parent_type, source, field_asts):
@@ -256,7 +255,7 @@ def resolve_field(ctx, parent_type, source, field_asts):
256255
# TODO: find a way to memoize, in case this field is within a list type.
257256
if field_def.args is not None:
258257
args = get_argument_values(
259-
field_def.args, field_ast['arguments'], ctx.variables
258+
field_def.args, field_ast.arguments, ctx.variables
260259
)
261260
else:
262261
args = None
@@ -358,7 +357,7 @@ def complete_value(ctx, field_type, field_asts, result):
358357
subfield_asts = {}
359358
visited_fragment_names = set()
360359
for field_ast in field_asts:
361-
selection_set = field_ast.get('selectionSet')
360+
selection_set = field_ast.selection_set
362361
if selection_set:
363362
subfield_asts = collect_fields(
364363
ctx, object_type, selection_set,
@@ -377,7 +376,7 @@ def camel_to_snake_case(name):
377376
def default_resolve_fn(source, args, root, field_ast, *_):
378377
"""If a resolve function is not given, then a default resolve behavior is used which takes the property of the source object
379378
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."""
380-
name = field_ast['name']['value']
379+
name = field_ast.name.value
381380
property = getattr(source, name, None)
382381
if property is None:
383382
property = getattr(source, camel_to_snake_case(name), None)
@@ -394,7 +393,7 @@ def get_field_def(schema, parent_type, field_ast):
394393
are allowed, like on a Union. __schema could get automatically
395394
added to the query type, but that would require mutating type
396395
definitions, which would cause issues."""
397-
name = field_ast['name']['value']
396+
name = field_ast.name.value
398397
if name == SchemaMetaFieldDef.name and \
399398
schema.get_query_type() == parent_type:
400399
return SchemaMetaFieldDef

graphql/core/executor/values.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import collections
22
from ..error import GraphQLError
3-
from ..language import Kind
3+
from ..language import ast
44
from ..type import (GraphQLNonNull, GraphQLList, GraphQLInputObjectType,
55
GraphQLScalarType, GraphQLEnumType, is_input_type)
66
from ..utils import type_from_ast, is_nullish
@@ -20,7 +20,7 @@ def get_variable_values(schema, definition_asts, inputs):
2020
inputs = {}
2121
values = {}
2222
for def_ast in definition_asts:
23-
var_name = def_ast['variable']['name']['value']
23+
var_name = def_ast.variable.name.value
2424
value = get_variable_value(schema, def_ast, inputs.get(var_name))
2525
values[var_name] = value
2626
return values
@@ -32,13 +32,13 @@ def get_argument_values(arg_defs, arg_asts, variables):
3232
arg_ast_map = {}
3333
if arg_asts:
3434
for arg in arg_asts:
35-
arg_ast_map[arg['name']['value']] = arg
35+
arg_ast_map[arg.name.value] = arg
3636
result = {}
3737
for arg_def in arg_defs:
3838
name = arg_def.name
3939
value_ast = arg_ast_map.get(name)
4040
if value_ast:
41-
value_ast = value_ast['value']
41+
value_ast = value_ast.value
4242
value = coerce_value_ast(
4343
arg_def.type,
4444
value_ast,
@@ -52,25 +52,25 @@ def get_argument_values(arg_defs, arg_asts, variables):
5252

5353
def get_variable_value(schema, definition_ast, input):
5454
"""Given a variable definition, and any value of input, return a value which adheres to the variable definition, or throw an error."""
55-
type = type_from_ast(schema, definition_ast['type'])
55+
type = type_from_ast(schema, definition_ast.type)
5656
if not type or not is_input_type(type):
5757
raise GraphQLError(
5858
'Variable ${} expected value of type {} which cannot be used as an input type.'.format(
59-
definition_ast['variable']['name']['value'],
60-
print_ast(definition_ast['type']),
59+
definition_ast.variable.name.value,
60+
print_ast(definition_ast.type),
6161
),
6262
[definition_ast]
6363
)
6464
if is_valid_value(type, input):
6565
if is_nullish(input):
66-
default_value = definition_ast.get('defaultValue')
66+
default_value = definition_ast.default_value
6767
if default_value:
6868
return coerce_value_ast(type, default_value, None)
6969
return coerce_value(type, input)
7070
raise GraphQLError(
7171
'Variable ${} expected value of type {} but got: {}'.format(
72-
definition_ast['variable']['name']['value'],
73-
print_ast(definition_ast['type']),
72+
definition_ast.variable.name.value,
73+
print_ast(definition_ast.type),
7474
repr(input)
7575
),
7676
[definition_ast]
@@ -165,8 +165,8 @@ def coerce_value_ast(type, value_ast, variables):
165165
if not value_ast:
166166
return None
167167

168-
if value_ast['kind'] == Kind.VARIABLE:
169-
variable_name = value_ast['name']['value']
168+
if isinstance(value_ast, ast.Variable):
169+
variable_name = value_ast.name.value
170170
if not variables or variable_name not in variables:
171171
return None
172172
# Note: we're not doing any checking that this variable is correct. We're assuming that this query
@@ -175,25 +175,25 @@ def coerce_value_ast(type, value_ast, variables):
175175

176176
if isinstance(type, GraphQLList):
177177
item_type = type.of_type
178-
if value_ast['kind'] == Kind.ARRAY:
178+
if isinstance(value_ast, ast.ListValue):
179179
return [coerce_value_ast(item_type, item_ast, variables)
180-
for item_ast in value_ast['values']]
180+
for item_ast in value_ast.values]
181181
else:
182182
return [coerce_value_ast(item_type, value_ast, variables)]
183183

184184
if isinstance(type, GraphQLInputObjectType):
185185
fields = type.get_fields()
186-
if value_ast['kind'] != Kind.OBJECT:
186+
if not isinstance(value_ast, ast.ObjectValue):
187187
return None
188188
field_asts = {}
189-
for field in value_ast['fields']:
190-
field_asts[field['name']['value']] = field
189+
for field in value_ast.fields:
190+
field_asts[field.name.value] = field
191191
obj = {}
192192
for field_name, field in fields.items():
193193
field_ast = field_asts.get(field_name)
194194
field_value_ast = None
195195
if field_ast:
196-
field_value_ast = field_ast['value']
196+
field_value_ast = field_ast.value
197197
field_value = coerce_value_ast(
198198
field.type, field_value_ast, variables
199199
)

graphql/core/language/__init__.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +0,0 @@
1-
# flake8: noqa
2-
from .location import get_location
3-
from . import kinds as Kind
4-
from .lexer import Lexer
5-
from .parser import parse
6-
from .source import Source
7-
from .error import LanguageError

graphql/core/language/ast.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from collections import namedtuple
2+
3+
# Name
4+
Name = namedtuple('Name', 'loc value')
5+
6+
# Document
7+
Document = namedtuple('Document', 'loc definitions')
8+
OperationDefinition = namedtuple('OperationDefinition', 'loc operation name variable_definitions directives selection_set')
9+
VariableDefinition = namedtuple('VariableDefinition', 'loc variable type default_value')
10+
Variable = namedtuple('Variable', 'loc name')
11+
SelectionSet = namedtuple('SelectionSet', 'loc selections')
12+
Field = namedtuple('Field', 'loc alias name arguments directives selection_set')
13+
Argument = namedtuple('Argument', 'loc name value')
14+
15+
# Fragments
16+
FragmentSpread = namedtuple('FragmentSpread', 'loc name directives')
17+
InlineFragment = namedtuple('InlineFragment', 'loc type_condition directives selection_set')
18+
FragmentDefinition = namedtuple('FragmentDefinition', 'loc name type_condition directives selection_set')
19+
20+
# Values
21+
IntValue = namedtuple('IntValue', 'loc value')
22+
FloatValue = namedtuple('FloatValue', 'loc value')
23+
StringValue = namedtuple('StringValue', 'loc value')
24+
BooleanValue = namedtuple('BooleanValue', 'loc value')
25+
EnumValue = namedtuple('EnumValue', 'loc value')
26+
ListValue = namedtuple('ListValue', 'loc values')
27+
ObjectValue = namedtuple('ObjectValue', 'loc fields')
28+
ObjectField = namedtuple('ObjectField', 'loc name value')
29+
30+
# Directives
31+
Directive = namedtuple('Directive', 'loc name arguments')
32+
33+
# Types
34+
NamedType = namedtuple('NamedType', 'loc name')
35+
ListType = namedtuple('ListType', 'loc type')
36+
NonNullType = namedtuple('NonNullType', 'loc type')

0 commit comments

Comments
 (0)