Skip to content

Commit 9440f21

Browse files
committed
Refactor coerce to serialize/parse in scalar type definition (fix #32)
1 parent 204bf82 commit 9440f21

File tree

7 files changed

+115
-94
lines changed

7 files changed

+115
-94
lines changed

graphql/core/execution/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ def complete_value(ctx, return_type, field_asts, info, result):
336336
337337
If the field type is a List, then this recursively completes the value for the inner type on each item in the list.
338338
339-
If the field type is a Scalar or Enum, ensures the completed value is a legal value of the type by calling the `coerce`
339+
If the field type is a Scalar or Enum, ensures the completed value is a legal value of the type by calling the `serialize`
340340
method of GraphQL type definition.
341341
342342
Otherwise, the field type expects a sub-selection set, and will complete the value by evaluating all sub-selections."""
@@ -366,12 +366,12 @@ def complete_value(ctx, return_type, field_asts, info, result):
366366
ctx, item_type, field_asts, info, item
367367
) for item in result]
368368

369-
# If field type is Scalar or Enum, coerce to a valid value, returning null if coercion is not possible.
369+
# If field type is Scalar or Enum, serialize to a valid value, returning null if coercion is not possible.
370370
if isinstance(return_type, (GraphQLScalarType, GraphQLEnumType)):
371-
coerced_result = return_type.coerce(result)
372-
if is_nullish(coerced_result):
371+
serialized_result = return_type.serialize(result)
372+
if is_nullish(serialized_result):
373373
return None
374-
return coerced_result
374+
return serialized_result
375375

376376
# Field type must be Object, Interface or Union and expect sub-selections.
377377
if isinstance(return_type, GraphQLObjectType):

graphql/core/execution/values.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
def get_variable_values(schema, definition_asts, inputs):
1313
"""Prepares an object map of variables of the correct type based on the provided variable definitions and arbitrary input.
14-
If the input cannot be coerced to match the variable definitions, a GraphQLError will be thrown."""
14+
If the input cannot be parsed to match the variable definitions, a GraphQLError will be thrown."""
1515
if inputs is None:
1616
inputs = {}
1717
values = {}
@@ -109,7 +109,7 @@ def is_valid_value(type, value):
109109
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
110110
'Must be input type'
111111

112-
return not is_nullish(type.coerce(value))
112+
return not is_nullish(type.parse_value(value))
113113

114114

115115
def coerce_value(type, value):
@@ -143,9 +143,9 @@ def coerce_value(type, value):
143143
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
144144
'Must be input type'
145145

146-
coerced = type.coerce(value)
147-
if not is_nullish(coerced):
148-
return coerced
146+
parsed = type.parse_value(value)
147+
if not is_nullish(parsed):
148+
return parsed
149149

150150
return None
151151

@@ -201,8 +201,8 @@ def coerce_value_ast(type, value_ast, variables):
201201
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), \
202202
'Must be input type'
203203

204-
coerced = type.coerce_literal(value_ast)
205-
if not is_nullish(coerced):
206-
return coerced
204+
parsed = type.parse_literal(value_ast)
205+
if not is_nullish(parsed):
206+
return parsed
207207

208208
return None

graphql/core/type/definition.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,30 @@ def coerce_odd(value):
6161
return value
6262
return None
6363
64-
OddType = GraphQLScalarType(name='Odd', coerce=coerce_odd)
64+
OddType = GraphQLScalarType(name='Odd', serialize=coerce_odd)
6565
"""
66-
def __init__(self, name, description=None, coerce=None, coerce_literal=None):
66+
def __init__(self, name, description=None, serialize=None, parse_value=None, parse_literal=None):
6767
assert name, 'Type must be named.'
6868
self.name = name
6969
self.description = description
70-
self._coerce = coerce
71-
self._coerce_literal = coerce_literal
72-
73-
def coerce(self, value):
74-
return self._coerce(value)
70+
assert callable(serialize)
71+
if parse_value or parse_literal:
72+
assert callable(parse_value) and callable(parse_literal)
73+
self._serialize = serialize
74+
self._parse_value = parse_value
75+
self._parse_literal = parse_literal
76+
77+
def serialize(self, value):
78+
return self._serialize(value)
79+
80+
def parse_value(self, value):
81+
if self._parse_value:
82+
return self._parse_value(value)
83+
return None
7584

76-
def coerce_literal(self, value):
77-
if self._coerce_literal:
78-
return self._coerce_literal(value)
85+
def parse_literal(self, value_ast):
86+
if self._parse_literal:
87+
return self._parse_literal(value_ast)
7988
return None
8089

8190
def __str__(self):
@@ -310,16 +319,23 @@ def get_values(self):
310319
self._value_map = self._define_value_map()
311320
return self._value_map
312321

313-
def coerce(self, value):
322+
def serialize(self, value):
323+
if isinstance(value, collections.Hashable):
324+
enum_value = self._get_value_lookup().get(value)
325+
if enum_value:
326+
return enum_value.name
327+
return None
328+
329+
def parse_value(self, value):
314330
if isinstance(value, collections.Hashable):
315331
enum_value = self._get_value_lookup().get(value)
316332
if enum_value:
317333
return enum_value.name
318334
return None
319335

320-
def coerce_literal(self, value):
321-
if isinstance(value, ast.EnumValue):
322-
enum_value = self._get_name_lookup().get(value.value)
336+
def parse_literal(self, value_ast):
337+
if isinstance(value_ast, ast.EnumValue):
338+
enum_value = self._get_name_lookup().get(value_ast.value)
323339
if enum_value:
324340
return enum_value.value
325341

graphql/core/type/scalars.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@ def coerce_int(value):
2626
return None
2727

2828

29-
def coerce_int_literal(ast):
29+
def parse_int_literal(ast):
3030
if isinstance(ast, IntValue):
3131
num = int(ast.value)
3232
if MIN_INT <= num <= MAX_INT:
3333
return num
3434

3535
GraphQLInt = GraphQLScalarType(name='Int',
36-
coerce=coerce_int,
37-
coerce_literal=coerce_int_literal)
36+
serialize=coerce_int,
37+
parse_value=coerce_int,
38+
parse_literal=parse_int_literal)
3839

3940

4041
def coerce_float(value):
@@ -47,14 +48,15 @@ def coerce_float(value):
4748
return None
4849

4950

50-
def coerce_float_literal(ast):
51+
def parse_float_literal(ast):
5152
if isinstance(ast, (FloatValue, IntValue)):
5253
return float(ast.value)
5354
return None
5455

5556
GraphQLFloat = GraphQLScalarType(name='Float',
56-
coerce=coerce_float,
57-
coerce_literal=coerce_float_literal)
57+
serialize=coerce_float,
58+
parse_value=coerce_float,
59+
parse_literal=parse_float_literal)
5860

5961

6062
def coerce_string(value):
@@ -63,31 +65,34 @@ def coerce_string(value):
6365
return str(value)
6466

6567

66-
def coerce_string_literal(ast):
68+
def parse_string_literal(ast):
6769
if isinstance(ast, StringValue):
6870
return ast.value
6971
return None
7072

7173
GraphQLString = GraphQLScalarType(name='String',
72-
coerce=coerce_string,
73-
coerce_literal=coerce_string_literal)
74+
serialize=coerce_string,
75+
parse_value=coerce_string,
76+
parse_literal=parse_string_literal)
7477

7578

76-
def coerce_boolean_literal(ast):
79+
def parse_boolean_literal(ast):
7780
if isinstance(ast, BooleanValue):
7881
return ast.value
7982
return None
8083

8184
GraphQLBoolean = GraphQLScalarType(name='Boolean',
82-
coerce=bool,
83-
coerce_literal=coerce_boolean_literal)
85+
serialize=bool,
86+
parse_value=bool,
87+
parse_literal=parse_boolean_literal)
8488

8589

86-
def coerce_id_literal(ast):
90+
def parse_id_literal(ast):
8791
if isinstance(ast, (StringValue, IntValue)):
8892
return ast.value
8993
return None
9094

9195
GraphQLID = GraphQLScalarType(name='ID',
92-
coerce=str,
93-
coerce_literal=coerce_id_literal)
96+
serialize=str,
97+
parse_value=str,
98+
parse_literal=parse_id_literal)

graphql/core/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,4 @@ def is_valid_literal_value(type, value_ast):
211211

212212
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), 'Must be input type'
213213

214-
return not is_nullish(type.coerce_literal(value_ast))
214+
return not is_nullish(type.parse_literal(value_ast))

tests/core_type/test_coercion.py

Lines changed: 0 additions & 51 deletions
This file was deleted.

tests/core_type/test_serialization.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from graphql.core.type import (
2+
GraphQLInt,
3+
GraphQLFloat,
4+
GraphQLString,
5+
GraphQLBoolean,
6+
)
7+
8+
def test_serializes_output_int():
9+
assert GraphQLInt.serialize(1) == 1
10+
assert GraphQLInt.serialize(0) == 0
11+
assert GraphQLInt.serialize(-1) == -1
12+
assert GraphQLInt.serialize(0.1) == 0
13+
assert GraphQLInt.serialize(1.1) == 1
14+
assert GraphQLInt.serialize(-1.1) == -1
15+
assert GraphQLInt.serialize(1e5) == 100000
16+
assert GraphQLInt.serialize(1e100) is None
17+
assert GraphQLInt.serialize(-1e100) is None
18+
assert GraphQLInt.serialize('-1.1') == -1
19+
assert GraphQLInt.serialize('one') is None
20+
assert GraphQLInt.serialize(False) == 0
21+
assert GraphQLInt.serialize(True) == 1
22+
23+
24+
def test_serializes_output_float():
25+
assert GraphQLFloat.serialize(1) == 1.0
26+
assert GraphQLFloat.serialize(0) == 0.0
27+
assert GraphQLFloat.serialize(-1) == -1.0
28+
assert GraphQLFloat.serialize(0.1) == 0.1
29+
assert GraphQLFloat.serialize(1.1) == 1.1
30+
assert GraphQLFloat.serialize(-1.1) == -1.1
31+
assert GraphQLFloat.serialize('-1.1') == -1.1
32+
assert GraphQLFloat.serialize('one') is None
33+
assert GraphQLFloat.serialize(False) == 0
34+
assert GraphQLFloat.serialize(True) == 1
35+
36+
37+
def test_serializes_output_string():
38+
assert GraphQLString.serialize('string') == 'string'
39+
assert GraphQLString.serialize(1) == '1'
40+
assert GraphQLString.serialize(-1.1) == '-1.1'
41+
assert GraphQLString.serialize(True) == 'true'
42+
assert GraphQLString.serialize(False) == 'false'
43+
44+
45+
def test_serializes_output_boolean():
46+
assert GraphQLBoolean.serialize('string') is True
47+
assert GraphQLBoolean.serialize('') is False
48+
assert GraphQLBoolean.serialize(1) is True
49+
assert GraphQLBoolean.serialize(0) is False
50+
assert GraphQLBoolean.serialize(True) is True
51+
assert GraphQLBoolean.serialize(False) is False

0 commit comments

Comments
 (0)