Skip to content

Commit 417062f

Browse files
committed
More variables test / raise when failed to build execution context
1 parent f807530 commit 417062f

File tree

6 files changed

+206
-96
lines changed

6 files changed

+206
-96
lines changed

graphql/core/executor/executor.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ 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, errors):
56+
def __init__(self, schema, root, 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."""
60+
errors = []
6061
operations = {}
6162
fragments = {}
6263
for statement in ast['definitions']:
@@ -98,16 +99,15 @@ def __init__(self, data, errors=None):
9899
def execute(schema, root, ast, operation_name='', args=None):
99100
"""Implements the "Evaluating requests" section of the spec."""
100101
assert schema, 'Must provide schema'
101-
errors = []
102+
ctx = ExecutionContext(schema, root, ast, operation_name, args)
102103
try:
103-
ctx = ExecutionContext(schema, root, ast, operation_name, args, errors)
104104
data = execute_operation(ctx, root, ctx.operation)
105105
except Exception as e:
106-
errors.append(e)
106+
ctx.errors.append(e)
107107
data = None
108-
if not errors:
108+
if not ctx.errors:
109109
return ExecutionResult(data)
110-
return ExecutionResult(data, map(format_error, errors))
110+
return ExecutionResult(data, map(format_error, ctx.errors))
111111

112112

113113
def execute_operation(ctx, root, operation):

graphql/core/executor/values.py

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from ..error import GraphQLError
33
from ..language import Kind
44
from ..type import (GraphQLNonNull, GraphQLList, GraphQLInputObjectType,
5-
GraphQLScalarType, GraphQLEnumType)
5+
GraphQLScalarType, GraphQLEnumType, is_input_type)
66
from ..utils import type_from_ast, is_nullish
77

88
__all__ = ['get_variable_values', 'get_argument_values']
@@ -39,24 +39,33 @@ def get_argument_values(arg_defs, arg_asts, variables):
3939
value_ast = arg_ast_map.get(name)
4040
if value_ast:
4141
value_ast = value_ast['value']
42-
result[name] = coerce_value_ast(
42+
value = coerce_value_ast(
4343
arg_def.type,
4444
value_ast,
4545
variables
4646
)
47+
if is_nullish(value) and not is_nullish(arg_def.default_value):
48+
value = arg_def.default_value
49+
result[name] = value
4750
return result
4851

4952

5053
def get_variable_value(schema, definition_ast, input):
5154
"""Given a variable definition, and any value of input, return a value which adheres to the variable definition, or throw an error."""
5255
type = type_from_ast(schema, definition_ast['type'])
53-
if not type:
54-
return None
56+
if not type or not is_input_type(type):
57+
raise GraphQLError(
58+
'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']),
61+
),
62+
[definition_ast]
63+
)
5564
if is_valid_value(type, input):
5665
if is_nullish(input):
5766
default_value = definition_ast.get('defaultValue')
5867
if default_value:
59-
return coerce_value_ast(type, default_value)
68+
return coerce_value_ast(type, default_value, None)
6069
return coerce_value(type, input)
6170
raise GraphQLError(
6271
'Variable ${} expected value of type {} but got: {}'.format(
@@ -87,16 +96,24 @@ def is_valid_value(type, value):
8796
return is_valid_value(item_type, value)
8897

8998
if isinstance(type, GraphQLInputObjectType):
99+
if not isinstance(value, collections.Mapping):
100+
return False
90101
fields = type.get_fields()
102+
103+
# Ensure every provided field is defined.
104+
if any(field_name not in fields for field_name in value.keys()):
105+
return False
106+
107+
# Ensure every defined field is valid.
91108
return all(
92-
is_valid_value(fields[field_name].type, value[field_name])
109+
is_valid_value(fields[field_name].type, value.get(field_name))
93110
for field_name in fields
94111
)
95112

96-
if isinstance(type, (GraphQLScalarType, GraphQLEnumType)):
97-
return not is_nullish(type.coerce(value))
113+
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
114+
'Must be input type'
98115

99-
return False
116+
return not is_nullish(type.coerce(value))
100117

101118

102119
def coerce_value(type, value):
@@ -127,10 +144,12 @@ def coerce_value(type, value):
127144
obj[field_name] = field_value
128145
return obj
129146

130-
if isinstance(type, (GraphQLScalarType, GraphQLEnumType)):
131-
coerced = type.coerce(value)
132-
if not is_nullish(coerced):
133-
return coerced
147+
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
148+
'Must be input type'
149+
150+
coerced = type.coerce(value)
151+
if not is_nullish(coerced):
152+
return coerced
134153

135154
return None
136155

@@ -183,9 +202,11 @@ def coerce_value_ast(type, value_ast, variables):
183202
obj[field_name] = field_value
184203
return obj
185204

186-
if isinstance(type, (GraphQLScalarType, GraphQLEnumType)):
187-
coerced = type.coerce_literal(value_ast)
188-
if not is_nullish(coerced):
189-
return coerced
205+
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
206+
'Must be input type'
207+
208+
coerced = type.coerce_literal(value_ast)
209+
if not is_nullish(coerced):
210+
return coerced
190211

191212
return None

graphql/core/type/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
GraphQLInputObjectField,
1414
GraphQLList,
1515
GraphQLNonNull,
16+
is_input_type,
1617
)
1718
from .scalars import (
1819
GraphQLInt,

graphql/core/type/definition.py

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,6 @@
1818
1919
// Predicates
2020
21-
/**
22-
* These types may be used as input types for arguments and directives.
23-
*/
24-
export type GraphQLInputType =
25-
GraphQLScalarType |
26-
GraphQLEnumType |
27-
GraphQLInputObjectType |
28-
GraphQLList |
29-
GraphQLNonNull;
30-
31-
export function isInputType(type: ?GraphQLType): boolean {
32-
var nakedType = getUnmodifiedType(type);
33-
return (
34-
nakedType instanceof GraphQLScalarType ||
35-
nakedType instanceof GraphQLEnumType ||
36-
nakedType instanceof GraphQLInputObjectType
37-
);
38-
}
39-
4021
/**
4122
* These types may be used as output types as the result of fields.
4223
*/
@@ -120,31 +101,25 @@
120101
export function getNullableType(type: ?GraphQLType): ?GraphQLNullableType {
121102
return type instanceof GraphQLNonNull ? type.ofType : type;
122103
}
123-
124-
/**
125-
* These types have no modifiers like List or NonNull.
126-
*/
127-
export type GraphQLUnmodifiedType =
128-
GraphQLScalarType |
129-
GraphQLObjectType |
130-
GraphQLInterfaceType |
131-
GraphQLUnionType |
132-
GraphQLEnumType |
133-
GraphQLInputObjectType;
134-
135-
export function getUnmodifiedType(type: ?GraphQLType): ?GraphQLUnmodifiedType {
136-
var unmodifiedType = type;
137-
while (
138-
unmodifiedType instanceof GraphQLList ||
139-
unmodifiedType instanceof GraphQLNonNull
140-
) {
141-
unmodifiedType = unmodifiedType.ofType;
142-
}
143-
return unmodifiedType;
144-
}
145104
'''
146105

147106

107+
def is_input_type(type):
108+
named_type = get_named_type(type)
109+
return isinstance(named_type, (
110+
GraphQLScalarType,
111+
GraphQLEnumType,
112+
GraphQLInputObjectType,
113+
))
114+
115+
116+
def get_named_type(type):
117+
unmodified_type = type
118+
while isinstance(unmodified_type, (GraphQLList, GraphQLNonNull)):
119+
unmodified_type = unmodified_type.of_type
120+
return unmodified_type
121+
122+
148123
class GraphQLType(object):
149124
def __str__(self):
150125
return self.name

tests/core_executor/test_executor.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
from pytest import raises
12
from graphql.core.executor import execute
23
from graphql.core.language import parse
34
from graphql.core.type import (GraphQLSchema, GraphQLObjectType, GraphQLField,
45
GraphQLArgument, GraphQLList, GraphQLInt, GraphQLString)
6+
from graphql.core.error import GraphQLError
57

68

79
def test_executes_arbitary_code():
@@ -267,10 +269,9 @@ class Data(object):
267269
Type = GraphQLObjectType('Type', {
268270
'a': GraphQLField(GraphQLString)
269271
})
270-
result = execute(GraphQLSchema(Type), Data(), ast)
271-
assert not result.data
272-
assert len(result.errors) == 1
273-
assert result.errors[0].message == 'Must provide operation name if query contains multiple operations'
272+
with raises(GraphQLError) as excinfo:
273+
execute(GraphQLSchema(Type), Data(), ast)
274+
assert 'Must provide operation name if query contains multiple operations' in str(excinfo.value)
274275

275276

276277
def test_uses_the_query_schema_for_queries():

0 commit comments

Comments
 (0)