Skip to content

Commit 138f594

Browse files
committed
Merge pull request #1 from jhgg/ast-from-value
Ast from value
2 parents fc2545c + 212503e commit 138f594

File tree

14 files changed

+288
-99
lines changed

14 files changed

+288
-99
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/type/introspection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import json
1+
from ..language.printer import print_ast
2+
from ..utils.ast_from_value import ast_from_value
23
from .definition import (
34
GraphQLArgument,
45
GraphQLEnumType,
@@ -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 ..compat import str_type
5+
from ..language import ast
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
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from ..language import ast
2+
from ..type.definition import (
3+
GraphQLEnumType,
4+
GraphQLInputObjectType,
5+
GraphQLList,
6+
GraphQLNonNull,
7+
GraphQLScalarType,
8+
)
9+
from .is_nullish import is_nullish
10+
11+
12+
def is_valid_literal_value(type, value_ast):
13+
if isinstance(type, GraphQLNonNull):
14+
if not value_ast:
15+
return False
16+
17+
of_type = type.of_type
18+
return is_valid_literal_value(of_type, value_ast)
19+
20+
if not value_ast:
21+
return True
22+
23+
if isinstance(value_ast, ast.Variable):
24+
return True
25+
26+
if isinstance(type, GraphQLList):
27+
item_type = type.of_type
28+
if isinstance(value_ast, ast.ListValue):
29+
return all(is_valid_literal_value(item_type, item_ast) for item_ast in value_ast.values)
30+
31+
return is_valid_literal_value(item_type, value_ast)
32+
33+
if isinstance(type, GraphQLInputObjectType):
34+
if not isinstance(value_ast, ast.ObjectValue):
35+
return False
36+
37+
fields = type.get_fields()
38+
field_asts = value_ast.fields
39+
40+
if any(not fields.get(field_ast.name.value, None) for field_ast in field_asts):
41+
return False
42+
43+
field_ast_map = {field_ast.name.value: field_ast for field_ast in field_asts}
44+
get_field_ast_value = lambda field_name: field_ast_map[
45+
field_name].value if field_name in field_ast_map else None
46+
47+
return all(is_valid_literal_value(field.type, get_field_ast_value(field_name))
48+
for field_name, field in fields.items())
49+
50+
assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), 'Must be input type'
51+
52+
return not is_nullish(type.parse_literal(value_ast))

graphql/core/utils/type_from_ast.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from ..language import ast
2+
from ..type.definition import (
3+
GraphQLList,
4+
GraphQLNonNull,
5+
)
6+
7+
8+
def type_from_ast(schema, input_type_ast):
9+
if isinstance(input_type_ast, ast.ListType):
10+
inner_type = type_from_ast(schema, input_type_ast.type)
11+
if inner_type:
12+
return GraphQLList(inner_type)
13+
else:
14+
return None
15+
16+
if isinstance(input_type_ast, ast.NonNullType):
17+
inner_type = type_from_ast(schema, input_type_ast.type)
18+
if inner_type:
19+
return GraphQLNonNull(inner_type)
20+
else:
21+
return None
22+
23+
assert isinstance(input_type_ast, ast.NamedType), 'Must be a type name.'
24+
return schema.get_type(input_type_ast.name.value)

0 commit comments

Comments
 (0)