Skip to content

Commit 55dcd92

Browse files
committed
Implement missing execution test.
* Implements the test "does not include arguments that were not set". In doing this, I realized that the `get_argument_values` function was improperly implemented. I also noticed that `coerce_value_ast` is `value_from_ast` utility. Changing `get_argument_values` broke a lot of tests that should have used .get() when expecting a Nullable value.
1 parent c49b5c9 commit 55dcd92

File tree

5 files changed

+150
-98
lines changed

5 files changed

+150
-98
lines changed

graphql/core/execution/values.py

Lines changed: 19 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import collections
22
from ..compat import str_type
33
from ..error import GraphQLError
4-
from ..language import ast
54
from ..language.printer import print_ast
65
from ..type import (
76
GraphQLEnumType,
@@ -13,6 +12,7 @@
1312
)
1413
from ..utils.is_nullish import is_nullish
1514
from ..utils.type_from_ast import type_from_ast
15+
from ..utils.value_from_ast import value_from_ast
1616

1717
__all__ = ['get_variable_values', 'get_argument_values']
1818

@@ -33,29 +33,37 @@ def get_variable_values(schema, definition_asts, inputs):
3333
def get_argument_values(arg_defs, arg_asts, variables):
3434
"""Prepares an object map of argument values given a list of argument
3535
definitions and list of argument AST nodes."""
36-
arg_ast_map = {}
3736
if arg_asts:
38-
for arg in arg_asts:
39-
arg_ast_map[arg.name.value] = arg
37+
arg_ast_map = {arg.name.value: arg for arg in arg_asts}
38+
39+
else:
40+
arg_ast_map = {}
41+
4042
result = {}
4143
for arg_def in arg_defs:
4244
name = arg_def.name
4345
value_ast = arg_ast_map.get(name)
4446
if value_ast:
4547
value_ast = value_ast.value
46-
value = coerce_value_ast(
48+
49+
value = value_from_ast(
4750
arg_def.type,
4851
value_ast,
4952
variables
5053
)
51-
if is_nullish(value) and not is_nullish(arg_def.default_value):
54+
55+
if is_nullish(value):
5256
value = arg_def.default_value
53-
result[name] = value
57+
58+
if not is_nullish(value):
59+
result[name] = value
60+
5461
return result
5562

5663

5764
def get_variable_value(schema, definition_ast, input):
58-
"""Given a variable definition, and any value of input, return a value which adheres to the variable definition, or throw an error."""
65+
"""Given a variable definition, and any value of input, return a value which adheres to the variable definition,
66+
or throw an error."""
5967
type = type_from_ast(schema, definition_ast.type)
6068
if not type or not is_input_type(type):
6169
raise GraphQLError(
@@ -65,12 +73,14 @@ def get_variable_value(schema, definition_ast, input):
6573
),
6674
[definition_ast]
6775
)
76+
6877
if is_valid_value(type, input):
6978
if is_nullish(input):
7079
default_value = definition_ast.default_value
7180
if default_value:
72-
return coerce_value_ast(type, default_value, None)
81+
return value_from_ast(type, default_value, None)
7382
return coerce_value(type, input)
83+
7484
raise GraphQLError(
7585
'Variable ${} expected value of type {} but got: {}'.format(
7686
definition_ast.variable.name.value,
@@ -156,61 +166,3 @@ def coerce_value(type, value):
156166
return parsed
157167

158168
return None
159-
160-
161-
def coerce_value_ast(type, value_ast, variables):
162-
"""Given a type and a value AST node known to match this type, build a
163-
runtime value."""
164-
if isinstance(type, GraphQLNonNull):
165-
# Note: we're not checking that the result of coerceValueAST is non-null.
166-
# We're assuming that this query has been validated and the value used here is of the correct type.
167-
return coerce_value_ast(type.of_type, value_ast, variables)
168-
169-
if not value_ast:
170-
return None
171-
172-
if isinstance(value_ast, ast.Variable):
173-
variable_name = value_ast.name.value
174-
if not variables or variable_name not in variables:
175-
return None
176-
# Note: we're not doing any checking that this variable is correct. We're assuming that this query
177-
# has been validated and the variable usage here is of the correct type.
178-
return variables[variable_name]
179-
180-
if isinstance(type, GraphQLList):
181-
item_type = type.of_type
182-
if isinstance(value_ast, ast.ListValue):
183-
return [coerce_value_ast(item_type, item_ast, variables)
184-
for item_ast in value_ast.values]
185-
else:
186-
return [coerce_value_ast(item_type, value_ast, variables)]
187-
188-
if isinstance(type, GraphQLInputObjectType):
189-
fields = type.get_fields()
190-
if not isinstance(value_ast, ast.ObjectValue):
191-
return None
192-
field_asts = {}
193-
for field in value_ast.fields:
194-
field_asts[field.name.value] = field
195-
obj = {}
196-
for field_name, field in fields.items():
197-
field_ast = field_asts.get(field_name)
198-
field_value_ast = None
199-
if field_ast:
200-
field_value_ast = field_ast.value
201-
field_value = coerce_value_ast(
202-
field.type, field_value_ast, variables
203-
)
204-
if field_value is None:
205-
field_value = field.default_value
206-
obj[field_name] = field_value
207-
return obj
208-
209-
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
210-
'Must be input type'
211-
212-
parsed = type.parse_literal(value_ast)
213-
if not is_nullish(parsed):
214-
return parsed
215-
216-
return None

graphql/core/utils/value_from_ast.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from ..language import ast
2+
from ..type import (GraphQLEnumType, GraphQLInputObjectType, GraphQLList, GraphQLNonNull, GraphQLScalarType)
3+
from .is_nullish import is_nullish
4+
5+
6+
def value_from_ast(type, value_ast, variables):
7+
"""Given a type and a value AST node known to match this type, build a
8+
runtime value."""
9+
if isinstance(type, GraphQLNonNull):
10+
# Note: we're not checking that the result of coerceValueAST is non-null.
11+
# We're assuming that this query has been validated and the value used here is of the correct type.
12+
return value_from_ast(type.of_type, value_ast, variables)
13+
14+
if not value_ast:
15+
return None
16+
17+
if isinstance(value_ast, ast.Variable):
18+
variable_name = value_ast.name.value
19+
if not variables or variable_name not in variables:
20+
return None
21+
# Note: we're not doing any checking that this variable is correct. We're assuming that this query
22+
# has been validated and the variable usage here is of the correct type.
23+
return variables[variable_name]
24+
25+
if isinstance(type, GraphQLList):
26+
item_type = type.of_type
27+
if isinstance(value_ast, ast.ListValue):
28+
return [value_from_ast(item_type, item_ast, variables)
29+
for item_ast in value_ast.values]
30+
else:
31+
return [value_from_ast(item_type, value_ast, variables)]
32+
33+
if isinstance(type, GraphQLInputObjectType):
34+
fields = type.get_fields()
35+
if not isinstance(value_ast, ast.ObjectValue):
36+
return None
37+
field_asts = {}
38+
for field in value_ast.fields:
39+
field_asts[field.name.value] = field
40+
obj = {}
41+
for field_name, field in fields.items():
42+
field_ast = field_asts.get(field_name)
43+
field_value_ast = None
44+
if field_ast:
45+
field_value_ast = field_ast.value
46+
field_value = value_from_ast(
47+
field.type, field_value_ast, variables
48+
)
49+
if field_value is None:
50+
field_value = field.default_value
51+
obj[field_name] = field_value
52+
return obj
53+
54+
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
55+
'Must be input type'
56+
57+
parsed = type.parse_literal(value_ast)
58+
if not is_nullish(parsed):
59+
return parsed
60+
61+
return None

tests/core_execution/test_executor.py

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import json
12
from pytest import raises
23
from graphql.core.execution import execute
34
from graphql.core.language.parser import parse
45
from graphql.core.type import (GraphQLSchema, GraphQLObjectType, GraphQLField,
5-
GraphQLArgument, GraphQLList, GraphQLInt, GraphQLString)
6+
GraphQLArgument, GraphQLList, GraphQLInt, GraphQLString,
7+
GraphQLBoolean)
68
from graphql.core.error import GraphQLError
79

810

@@ -29,7 +31,7 @@ class DeepData(object):
2931
a = 'Already Been Done'
3032
b = 'Boring'
3133
c = ['Contrived', None, 'Confusing']
32-
34+
3335
def deeper(self):
3436
return [Data(), None, Data()]
3537

@@ -71,15 +73,15 @@ def deeper(self):
7173
'e': 'Egg',
7274
'f': 'Fish',
7375
'pic': 'Pic of size: 100',
74-
'promise': { 'a': 'Apple' },
76+
'promise': {'a': 'Apple'},
7577
'deep': {
76-
'a': 'Already Been Done',
77-
'b': 'Boring',
78-
'c': [ 'Contrived', None, 'Confusing' ],
79-
'deeper': [
80-
{ 'a': 'Apple', 'b': 'Banana' },
81-
None,
82-
{ 'a': 'Apple', 'b': 'Banana' } ] }
78+
'a': 'Already Been Done',
79+
'b': 'Boring',
80+
'c': ['Contrived', None, 'Confusing'],
81+
'deeper': [
82+
{'a': 'Apple', 'b': 'Banana'},
83+
None,
84+
{'a': 'Apple', 'b': 'Banana'}]}
8385
}
8486

8587
DataType = GraphQLObjectType('DataType', lambda: {
@@ -141,17 +143,17 @@ def test_merges_parallel_fragments():
141143
result = execute(schema, None, ast)
142144
assert not result.errors
143145
assert result.data == \
144-
{
145-
'a': 'Apple',
146-
'b': 'Banana',
147-
'c': 'Cherry',
148-
'deep': {
149-
'b': 'Banana',
150-
'c': 'Cherry',
151-
'deeper': {
152-
'b': 'Banana',
153-
'c': 'Cherry' } }
154-
}
146+
{
147+
'a': 'Apple',
148+
'b': 'Banana',
149+
'c': 'Cherry',
150+
'deep': {
151+
'b': 'Banana',
152+
'c': 'Cherry',
153+
'deeper': {
154+
'b': 'Banana',
155+
'c': 'Cherry'}}
156+
}
155157

156158

157159
def test_threads_context_correctly():
@@ -165,7 +167,7 @@ class Data(object):
165167
def resolver(context, *_):
166168
assert context.context_thing == 'thing'
167169
resolver.got_here = True
168-
170+
169171
resolver.got_here = False
170172

171173
Type = GraphQLObjectType('Type', {
@@ -237,8 +239,10 @@ def error(self):
237239

238240
def test_uses_the_inline_operation_if_no_operation_is_provided():
239241
doc = '{ a }'
242+
240243
class Data(object):
241244
a = 'b'
245+
242246
ast = parse(doc)
243247
Type = GraphQLObjectType('Type', {
244248
'a': GraphQLField(GraphQLString)
@@ -250,8 +254,10 @@ class Data(object):
250254

251255
def test_uses_the_only_operation_if_no_operation_is_provided():
252256
doc = 'query Example { a }'
257+
253258
class Data(object):
254259
a = 'b'
260+
255261
ast = parse(doc)
256262
Type = GraphQLObjectType('Type', {
257263
'a': GraphQLField(GraphQLString)
@@ -263,8 +269,10 @@ class Data(object):
263269

264270
def test_raises_the_inline_operation_if_no_operation_is_provided():
265271
doc = 'query Example { a } query OtherExample { a }'
272+
266273
class Data(object):
267274
a = 'b'
275+
268276
ast = parse(doc)
269277
Type = GraphQLObjectType('Type', {
270278
'a': GraphQLField(GraphQLString)
@@ -276,9 +284,11 @@ class Data(object):
276284

277285
def test_uses_the_query_schema_for_queries():
278286
doc = 'query Q { a } mutation M { c }'
287+
279288
class Data(object):
280289
a = 'b'
281290
c = 'd'
291+
282292
ast = parse(doc)
283293
Q = GraphQLObjectType('Q', {
284294
'a': GraphQLField(GraphQLString)
@@ -293,9 +303,11 @@ class Data(object):
293303

294304
def test_uses_the_mutation_schema_for_queries():
295305
doc = 'query Q { a } mutation M { c }'
306+
296307
class Data(object):
297308
a = 'b'
298309
c = 'd'
310+
299311
ast = parse(doc)
300312
Q = GraphQLObjectType('Q', {
301313
'a': GraphQLField(GraphQLString)
@@ -320,8 +332,10 @@ def test_avoids_recursion():
320332
...Frag
321333
}
322334
'''
335+
323336
class Data(object):
324337
a = 'b'
338+
325339
ast = parse(doc)
326340
Type = GraphQLObjectType('Type', {
327341
'a': GraphQLField(GraphQLString)
@@ -343,3 +357,28 @@ def test_does_not_include_illegal_fields_in_output():
343357
result = execute(GraphQLSchema(Q, M), None, ast)
344358
assert not result.errors
345359
assert result.data == {}
360+
361+
362+
def test_does_not_include_arguments_that_were_not_set():
363+
schema = GraphQLSchema(GraphQLObjectType(
364+
'Type',
365+
{
366+
'field': GraphQLField(
367+
GraphQLString,
368+
resolver=lambda data, args, *_: args and json.dumps(args, sort_keys=True, separators=(',',':')),
369+
args={
370+
'a': GraphQLArgument(GraphQLBoolean),
371+
'b': GraphQLArgument(GraphQLBoolean),
372+
'c': GraphQLArgument(GraphQLBoolean),
373+
'd': GraphQLArgument(GraphQLInt),
374+
'e': GraphQLArgument(GraphQLInt),
375+
}
376+
)
377+
}
378+
))
379+
380+
ast = parse('{ field(a: true, c: false, e: 0) }')
381+
result = execute(schema, None, ast)
382+
assert result.data == {
383+
'field': '{"a":true,"c":false,"e":0}'
384+
}

0 commit comments

Comments
 (0)