Skip to content

Ast from value #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion graphql/core/execution/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
TypeMetaFieldDef,
TypeNameMetaFieldDef,
)
from ..utils import type_from_ast
from ..utils.type_from_ast import type_from_ast
from .values import get_argument_values, get_variable_values

Undefined = object()
Expand Down
2 changes: 1 addition & 1 deletion graphql/core/execution/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ..language.source import Source
from ..type import GraphQLEnumType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, \
GraphQLScalarType, GraphQLUnionType
from ..utils import is_nullish
from ..utils.is_nullish import is_nullish
from ..validation import validate
from .base import ExecutionContext, ExecutionResult, ResolveInfo, Undefined, collect_fields, default_resolve_fn, \
get_argument_values, get_field_def, get_operation_root_type
Expand Down
3 changes: 2 additions & 1 deletion graphql/core/execution/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
GraphQLScalarType,
is_input_type
)
from ..utils import is_nullish, type_from_ast
from ..utils.is_nullish import is_nullish
from ..utils.type_from_ast import type_from_ast

__all__ = ['get_variable_values', 'get_argument_values']

Expand Down
5 changes: 3 additions & 2 deletions graphql/core/type/introspection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from ..language.printer import print_ast
from ..utils.ast_from_value import ast_from_value
from .definition import (
GraphQLArgument,
GraphQLEnumType,
Expand Down Expand Up @@ -186,7 +187,7 @@ def input_fields(type, *_):
type=GraphQLString,
resolver=lambda input_val, *_:
None if input_val.default_value is None
else json.dumps(input_val.default_value)
else print_ast(ast_from_value(input_val.default_value, input_val))
)
})

Expand Down
Empty file added graphql/core/utils/__init__.py
Empty file.
70 changes: 70 additions & 0 deletions graphql/core/utils/ast_from_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import json
import re
import sys
from ..compat import str_type
from ..language import ast
from ..type.definition import (
GraphQLEnumType,
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
)
from ..type.scalars import GraphQLFloat
from .is_nullish import is_nullish


def ast_from_value(value, type=None):
if isinstance(type, GraphQLNonNull):
return ast_from_value(value, type.of_type)

if is_nullish(value):
return None

if isinstance(value, list):
item_type = type.of_type if isinstance(type, GraphQLList) else None
return ast.ListValue([ast_from_value(item, item_type) for item in value])

elif isinstance(type, GraphQLList):
return ast_from_value(value, type.of_type)

if isinstance(value, bool):
return ast.BooleanValue(value)

if isinstance(value, (int, float)):
string_num = str(value)
int_value = int(value)
is_int_value = string_num.isdigit()

if is_int_value or (int_value == value and value < sys.maxsize):
if type == GraphQLFloat:
return ast.FloatValue(str(float(value)))

return ast.IntValue(str(int(value)))

return ast.FloatValue(string_num)

if isinstance(value, str_type):
if isinstance(type, GraphQLEnumType) and re.match(r'^[_a-zA-Z][_a-zA-Z0-9]*$', value):
return ast.EnumValue(value)

return ast.StringValue(json.dumps(value)[1:-1])

assert isinstance(value, dict)

fields = []
is_graph_ql_input_object_type = isinstance(type, GraphQLInputObjectType)

for field_name, field_value in value.items():
field_type = None
if is_graph_ql_input_object_type:
field_def = type.get_fields().get(field_name)
field_type = field_def and field_def.type

field_value = ast_from_value(field_value, field_type)
if field_value:
fields.append(ast.ObjectField(
ast.Name(field_name),
field_value
))

return ast.ObjectValue(fields)
26 changes: 26 additions & 0 deletions graphql/core/utils/get_field_def.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from ..type.definition import (
GraphQLInterfaceType,
GraphQLObjectType,
GraphQLUnionType,
)
from ..type.introspection import SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef


def get_field_def(schema, parent_type, field_ast):
"""Not exactly the same as the executor's definition of get_field_def, in this
statically evaluated environment we do not always have an Object type,
and need to handle Interface and Union types."""
name = field_ast.name.value
if name == SchemaMetaFieldDef.name and schema.get_query_type() == parent_type:
return SchemaMetaFieldDef
elif name == TypeMetaFieldDef.name and schema.get_query_type() == parent_type:
return TypeMetaFieldDef
elif name == TypeNameMetaFieldDef.name and \
isinstance(parent_type, (
GraphQLObjectType,
GraphQLInterfaceType,
GraphQLUnionType,
)):
return TypeNameMetaFieldDef
elif isinstance(parent_type, (GraphQLObjectType, GraphQLInterfaceType)):
return parent_type.get_fields().get(name)
2 changes: 2 additions & 0 deletions graphql/core/utils/is_nullish.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def is_nullish(value):
return value is None or value != value
52 changes: 52 additions & 0 deletions graphql/core/utils/is_valid_literal_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from ..language import ast
from ..type.definition import (
GraphQLEnumType,
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
GraphQLScalarType,
)
from .is_nullish import is_nullish


def is_valid_literal_value(type, value_ast):
if isinstance(type, GraphQLNonNull):
if not value_ast:
return False

of_type = type.of_type
return is_valid_literal_value(of_type, value_ast)

if not value_ast:
return True

if isinstance(value_ast, ast.Variable):
return True

if isinstance(type, GraphQLList):
item_type = type.of_type
if isinstance(value_ast, ast.ListValue):
return all(is_valid_literal_value(item_type, item_ast) for item_ast in value_ast.values)

return is_valid_literal_value(item_type, value_ast)

if isinstance(type, GraphQLInputObjectType):
if not isinstance(value_ast, ast.ObjectValue):
return False

fields = type.get_fields()
field_asts = value_ast.fields

if any(not fields.get(field_ast.name.value, None) for field_ast in field_asts):
return False

field_ast_map = {field_ast.name.value: field_ast for field_ast in field_asts}
get_field_ast_value = lambda field_name: field_ast_map[
field_name].value if field_name in field_ast_map else None

return all(is_valid_literal_value(field.type, get_field_ast_value(field_name))
for field_name, field in fields.items())

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

return not is_nullish(type.parse_literal(value_ast))
24 changes: 24 additions & 0 deletions graphql/core/utils/type_from_ast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from ..language import ast
from ..type.definition import (
GraphQLList,
GraphQLNonNull,
)


def type_from_ast(schema, input_type_ast):
if isinstance(input_type_ast, ast.ListType):
inner_type = type_from_ast(schema, input_type_ast.type)
if inner_type:
return GraphQLList(inner_type)
else:
return None

if isinstance(input_type_ast, ast.NonNullType):
inner_type = type_from_ast(schema, input_type_ast.type)
if inner_type:
return GraphQLNonNull(inner_type)
else:
return None

assert isinstance(input_type_ast, ast.NamedType), 'Must be a type name.'
return schema.get_type(input_type_ast.name.value)
96 changes: 4 additions & 92 deletions graphql/core/utils.py → graphql/core/utils/type_info.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,14 @@
from .language import ast
from .type.definition import (
GraphQLEnumType,
from ..language import ast
from ..type.definition import (
GraphQLInputObjectType,
GraphQLInterfaceType,
GraphQLList,
GraphQLNonNull,
GraphQLObjectType,
GraphQLScalarType,
GraphQLUnionType,
get_named_type,
get_nullable_type,
is_composite_type,
)
from .type.introspection import SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef


def type_from_ast(schema, input_type_ast):
if isinstance(input_type_ast, ast.ListType):
inner_type = type_from_ast(schema, input_type_ast.type)
if inner_type:
return GraphQLList(inner_type)
else:
return None
if isinstance(input_type_ast, ast.NonNullType):
inner_type = type_from_ast(schema, input_type_ast.type)
if inner_type:
return GraphQLNonNull(inner_type)
else:
return None
assert isinstance(input_type_ast, ast.NamedType), 'Must be a type name.'
return schema.get_type(input_type_ast.name.value)


def is_nullish(value):
return value is None or value != value
from .get_field_def import get_field_def
from .type_from_ast import type_from_ast


def pop(lst):
Expand Down Expand Up @@ -149,66 +124,3 @@ def leave(self, node):
pop(self._input_type_stack)
elif isinstance(node, (ast.ListType, ast.ObjectField)):
pop(self._input_type_stack)


def get_field_def(schema, parent_type, field_ast):
"""Not exactly the same as the executor's definition of get_field_def, in this
statically evaluated environment we do not always have an Object type,
and need to handle Interface and Union types."""
name = field_ast.name.value
if name == SchemaMetaFieldDef.name and schema.get_query_type() == parent_type:
return SchemaMetaFieldDef
elif name == TypeMetaFieldDef.name and schema.get_query_type() == parent_type:
return TypeMetaFieldDef
elif name == TypeNameMetaFieldDef.name and \
isinstance(parent_type, (
GraphQLObjectType,
GraphQLInterfaceType,
GraphQLUnionType,
)):
return TypeNameMetaFieldDef
elif isinstance(parent_type, (GraphQLObjectType, GraphQLInterfaceType)):
return parent_type.get_fields().get(name)


def is_valid_literal_value(type, value_ast):
if isinstance(type, GraphQLNonNull):
if not value_ast:
return False

of_type = type.of_type
return is_valid_literal_value(of_type, value_ast)

if not value_ast:
return True

if isinstance(value_ast, ast.Variable):
return True

if isinstance(type, GraphQLList):
item_type = type.of_type
if isinstance(value_ast, ast.ListValue):
return all(is_valid_literal_value(item_type, item_ast) for item_ast in value_ast.values)

return is_valid_literal_value(item_type, value_ast)

if isinstance(type, GraphQLInputObjectType):
if not isinstance(value_ast, ast.ObjectValue):
return False

fields = type.get_fields()
field_asts = value_ast.fields

if any(not fields.get(field_ast.name.value, None) for field_ast in field_asts):
return False

field_ast_map = {field_ast.name.value: field_ast for field_ast in field_asts}
get_field_ast_value = lambda field_name: field_ast_map[
field_name].value if field_name in field_ast_map else None

return all(is_valid_literal_value(field.type, get_field_ast_value(field_name))
for field_name, field in fields.items())

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

return not is_nullish(type.parse_literal(value_ast))
2 changes: 1 addition & 1 deletion graphql/core/validation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from ..language.ast import FragmentDefinition, FragmentSpread
from ..language.visitor import Visitor, visit
from ..type import GraphQLSchema
from ..utils import TypeInfo
from ..utils.type_info import TypeInfo

specified_rules = [
Rules.UniqueOperationNames,
Expand Down
3 changes: 2 additions & 1 deletion graphql/core/validation/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
is_input_type,
is_leaf_type,
)
from ..utils import is_valid_literal_value, type_from_ast
from ..utils.is_valid_literal_value import is_valid_literal_value
from ..utils.type_from_ast import type_from_ast
from .utils import DefaultOrderedDict, PairSet


Expand Down
Loading