Skip to content

Commit 8b870b4

Browse files
committed
Implement ast_from_value and tests. Also, move utils to their own files, as we are getting circular imports as introspection imports ast_from_value.
1 parent de3b371 commit 8b870b4

File tree

16 files changed

+341
-105
lines changed

16 files changed

+341
-105
lines changed

graphql/core/execution/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
TypeMetaFieldDef,
1616
TypeNameMetaFieldDef,
1717
)
18-
from ..utils import type_from_ast
18+
from ..utils.type_from_ast import type_from_ast
1919
from .values import get_argument_values, get_variable_values
2020

2121
Undefined = object()

graphql/core/execution/executor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ..language.source import Source
99
from ..type import GraphQLEnumType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, \
1010
GraphQLScalarType, GraphQLUnionType
11-
from ..utils import is_nullish
11+
from ..utils.is_nullish import is_nullish
1212
from ..validation import validate
1313
from .base import ExecutionContext, ExecutionResult, ResolveInfo, Undefined, collect_fields, default_resolve_fn, \
1414
get_argument_values, get_field_def, get_operation_root_type

graphql/core/execution/values.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
GraphQLScalarType,
1212
is_input_type
1313
)
14-
from ..utils import is_nullish, type_from_ast
14+
from ..utils.is_nullish import is_nullish
15+
from ..utils.type_from_ast import type_from_ast
1516

1617
__all__ = ['get_variable_values', 'get_argument_values']
1718

graphql/core/language/kinds.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Name
2+
3+
NAME = 'Name'
4+
5+
# Document
6+
7+
DOCUMENT = 'Document'
8+
OPERATION_DEFINITION = 'OperationDefinition'
9+
VARIABLE_DEFINITION = 'VariableDefinition'
10+
VARIABLE = 'Variable'
11+
SELECTION_SET = 'SelectionSet'
12+
FIELD = 'Field'
13+
ARGUMENT = 'Argument'
14+
15+
# Fragments
16+
17+
FRAGMENT_SPREAD = 'FragmentSpread'
18+
INLINE_FRAGMENT = 'InlineFragment'
19+
FRAGMENT_DEFINITION = 'FragmentDefinition'
20+
21+
# Values
22+
23+
INT = 'IntValue'
24+
FLOAT = 'FloatValue'
25+
STRING = 'StringValue'
26+
BOOLEAN = 'BooleanValue'
27+
ENUM = 'EnumValue'
28+
LIST = 'ListValue'
29+
OBJECT = 'ObjectValue'
30+
OBJECT_FIELD = 'ObjectField'
31+
32+
# Directives
33+
34+
DIRECTIVE = 'Directive'
35+
36+
# Types
37+
38+
NAMED_TYPE = 'NamedType'
39+
LIST_TYPE = 'ListType'
40+
NON_NULL_TYPE = 'NonNullType'
41+
42+
# Type Definitions
43+
44+
OBJECT_TYPE_DEFINITION = 'ObjectTypeDefinition'
45+
FIELD_DEFINITION = 'FieldDefinition'
46+
INPUT_VALUE_DEFINITION = 'InputValueDefinition'
47+
INTERFACE_TYPE_DEFINITION = 'InterfaceTypeDefinition'
48+
UNION_TYPE_DEFINITION = 'UnionTypeDefinition'
49+
SCALAR_TYPE_DEFINITION = 'ScalarTypeDefinition'
50+
ENUM_TYPE_DEFINITION = 'EnumTypeDefinition'
51+
ENUM_VALUE_DEFINITION = 'EnumValueDefinition'
52+
INPUT_OBJECT_TYPE_DEFINITION = 'InputObjectTypeDefinition'
53+
TYPE_EXTENSION_DEFINITION = 'TypeExtensionDefinition'

graphql/core/language/lexer.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def read_token(source, from_position):
143143

144144
if code == 46: # .
145145
if char_code_at(body, position + 1) == 46 and \
146-
char_code_at(body, position + 2) == 46:
146+
char_code_at(body, position + 2) == 46:
147147
return Token(TokenKind.SPREAD, position, position + 3)
148148
elif 65 <= code <= 90 or code == 95 or 97 <= code <= 122:
149149
# A-Z, _, a-z
@@ -167,11 +167,11 @@ def position_after_whitespace(body, start_position):
167167
while position < body_length:
168168
code = char_code_at(body, position)
169169
if code in (
170-
32, # space
171-
44, # comma
172-
160, # '\xa0'
173-
0x2028, # line separator
174-
0x2029, # paragraph separator
170+
32, # space
171+
44, # comma
172+
160, # '\xa0'
173+
0x2028, # line separator
174+
0x2029, # paragraph separator
175175
) or (code > 8 and code < 14): # whitespace
176176
position += 1
177177
elif code == 35: # #, skip comments

graphql/core/type/introspection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import json
21
from .definition import (
32
GraphQLArgument,
43
GraphQLEnumType,
@@ -12,6 +11,8 @@
1211
GraphQLScalarType,
1312
GraphQLUnionType,
1413
)
14+
from ..language.printer import print_ast
15+
from ..utils.ast_from_value import ast_from_value
1516
from .scalars import GraphQLBoolean, GraphQLString
1617

1718
__Schema = GraphQLObjectType(
@@ -186,7 +187,7 @@ def input_fields(type, *_):
186187
type=GraphQLString,
187188
resolver=lambda input_val, *_:
188189
None if input_val.default_value is None
189-
else json.dumps(input_val.default_value)
190+
else print_ast(ast_from_value(input_val.default_value, input_val))
190191
)
191192
})
192193

graphql/core/utils/__init__.py

Whitespace-only changes.

graphql/core/utils/ast_from_value.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import json
2+
import re
3+
import sys
4+
from ..language import ast
5+
from ..compat import str_type
6+
from ..type.definition import (
7+
GraphQLEnumType,
8+
GraphQLInputObjectType,
9+
GraphQLList,
10+
GraphQLNonNull,
11+
)
12+
from ..type.scalars import GraphQLFloat
13+
from .is_nullish import is_nullish
14+
15+
16+
def ast_from_value(value, type=None):
17+
if isinstance(type, GraphQLNonNull):
18+
return ast_from_value(value, type.of_type)
19+
20+
if is_nullish(value):
21+
return None
22+
23+
if isinstance(value, list):
24+
item_type = type.of_type if isinstance(type, GraphQLList) else None
25+
return ast.ListValue([ast_from_value(item, item_type) for item in value])
26+
27+
elif isinstance(type, GraphQLList):
28+
return ast_from_value(value, type.of_type)
29+
30+
if isinstance(value, bool):
31+
return ast.BooleanValue(value)
32+
33+
if isinstance(value, (int, float)):
34+
string_num = str(value)
35+
int_value = int(value)
36+
is_int_value = string_num.isdigit()
37+
38+
if is_int_value or (int_value == value and value < sys.maxsize):
39+
if type == GraphQLFloat:
40+
return ast.FloatValue(str(float(value)))
41+
42+
return ast.IntValue(str(int(value)))
43+
44+
return ast.FloatValue(string_num)
45+
46+
if isinstance(value, str_type):
47+
if isinstance(type, GraphQLEnumType) and re.match(r'^[_a-zA-Z][_a-zA-Z0-9]*$', value):
48+
return ast.EnumValue(value)
49+
50+
return ast.StringValue(json.dumps(value)[1:-1])
51+
52+
assert isinstance(value, dict)
53+
54+
fields = []
55+
is_graph_ql_input_object_type = isinstance(type, GraphQLInputObjectType)
56+
57+
for field_name, field_value in value.items():
58+
field_type = None
59+
if is_graph_ql_input_object_type:
60+
field_def = type.get_fields().get(field_name)
61+
field_type = field_def and field_def.type
62+
63+
field_value = ast_from_value(field_value, field_type)
64+
if field_value:
65+
fields.append(ast.ObjectField(
66+
ast.Name(field_name),
67+
field_value
68+
))
69+
70+
return ast.ObjectValue(fields)

graphql/core/utils/get_field_def.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from ..type.definition import (
2+
GraphQLInterfaceType,
3+
GraphQLObjectType,
4+
GraphQLUnionType,
5+
)
6+
from ..type.introspection import SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef
7+
8+
9+
def get_field_def(schema, parent_type, field_ast):
10+
"""Not exactly the same as the executor's definition of get_field_def, in this
11+
statically evaluated environment we do not always have an Object type,
12+
and need to handle Interface and Union types."""
13+
name = field_ast.name.value
14+
if name == SchemaMetaFieldDef.name and schema.get_query_type() == parent_type:
15+
return SchemaMetaFieldDef
16+
elif name == TypeMetaFieldDef.name and schema.get_query_type() == parent_type:
17+
return TypeMetaFieldDef
18+
elif name == TypeNameMetaFieldDef.name and \
19+
isinstance(parent_type, (
20+
GraphQLObjectType,
21+
GraphQLInterfaceType,
22+
GraphQLUnionType,
23+
)):
24+
return TypeNameMetaFieldDef
25+
elif isinstance(parent_type, (GraphQLObjectType, GraphQLInterfaceType)):
26+
return parent_type.get_fields().get(name)

graphql/core/utils/is_nullish.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def is_nullish(value):
2+
return value is None or value != value

0 commit comments

Comments
 (0)