11import logging
2+ import re
23from abc import ABC , abstractmethod
4+ from math import isfinite
35from typing import Any , Dict , Iterable , List , Mapping , Optional , Tuple , Union , cast
46
57from graphql import (
68 ArgumentNode ,
9+ BooleanValueNode ,
710 DocumentNode ,
11+ EnumValueNode ,
812 FieldNode ,
13+ FloatValueNode ,
914 FragmentDefinitionNode ,
1015 FragmentSpreadNode ,
1116 GraphQLArgument ,
17+ GraphQLError ,
1218 GraphQLField ,
19+ GraphQLID ,
1320 GraphQLInputObjectType ,
1421 GraphQLInputType ,
1522 GraphQLInterfaceType ,
2027 GraphQLSchema ,
2128 GraphQLWrappingType ,
2229 InlineFragmentNode ,
30+ IntValueNode ,
2331 ListTypeNode ,
2432 ListValueNode ,
2533 NamedTypeNode ,
3139 OperationDefinitionNode ,
3240 OperationType ,
3341 SelectionSetNode ,
42+ StringValueNode ,
3443 TypeNode ,
3544 Undefined ,
3645 ValueNode ,
3746 VariableDefinitionNode ,
3847 VariableNode ,
3948 assert_named_type ,
49+ is_enum_type ,
4050 is_input_object_type ,
51+ is_leaf_type ,
4152 is_list_type ,
4253 is_non_null_type ,
4354 is_wrapping_type ,
4455 print_ast ,
4556)
46- from graphql .pyutils import FrozenList
47- from graphql .utilities import ast_from_value as default_ast_from_value
57+ from graphql .pyutils import FrozenList , inspect
4858
4959from .utils import to_camel_case
5060
5161log = logging .getLogger (__name__ )
5262
63+ _re_integer_string = re .compile ("^-?(?:0|[1-9][0-9]*)$" )
64+
65+
66+ def ast_from_serialized_value_untyped (serialized : Any ) -> Optional [ValueNode ]:
67+ """Given a serialized value, try our best to produce an AST.
68+
69+ Anything ressembling an array (instance of Mapping) will be converted
70+ to an ObjectFieldNode.
71+
72+ Anything ressembling a list (instance of Iterable - except str)
73+ will be converted to a ListNode.
74+
75+ In some cases, a custom scalar can be serialized differently in the query
76+ than in the variables. In that case, this function will not work."""
77+
78+ if serialized is None or serialized is Undefined :
79+ return NullValueNode ()
80+
81+ if isinstance (serialized , Mapping ):
82+ field_items = (
83+ (key , ast_from_serialized_value_untyped (value ))
84+ for key , value in serialized .items ()
85+ )
86+ field_nodes = (
87+ ObjectFieldNode (name = NameNode (value = field_name ), value = field_value )
88+ for field_name , field_value in field_items
89+ if field_value
90+ )
91+ return ObjectValueNode (fields = FrozenList (field_nodes ))
92+
93+ if isinstance (serialized , Iterable ) and not isinstance (serialized , str ):
94+ maybe_nodes = (ast_from_serialized_value_untyped (item ) for item in serialized )
95+ nodes = filter (None , maybe_nodes )
96+ return ListValueNode (values = FrozenList (nodes ))
97+
98+ if isinstance (serialized , bool ):
99+ return BooleanValueNode (value = serialized )
100+
101+ if isinstance (serialized , int ):
102+ return IntValueNode (value = f"{ serialized :d} " )
103+
104+ if isinstance (serialized , float ) and isfinite (serialized ):
105+ return FloatValueNode (value = f"{ serialized :g} " )
106+
107+ if isinstance (serialized , str ):
108+ return StringValueNode (value = serialized )
109+
110+ raise TypeError (f"Cannot convert value to AST: { inspect (serialized )} ." )
111+
53112
54113def ast_from_value (value : Any , type_ : GraphQLInputType ) -> Optional [ValueNode ]:
55114 """
@@ -60,15 +119,21 @@ def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
60119 VariableNode when value is a DSLVariable
61120
62121 Produce a GraphQL Value AST given a Python object.
122+
123+ Raises a GraphQLError instead of returning None if we receive an Undefined
124+ of if we receive a Null value for a Non-Null type.
63125 """
64126 if isinstance (value , DSLVariable ):
65127 return value .set_type (type_ ).ast_variable
66128
67129 if is_non_null_type (type_ ):
68130 type_ = cast (GraphQLNonNull , type_ )
69- ast_value = ast_from_value (value , type_ .of_type )
131+ inner_type = type_ .of_type
132+ ast_value = ast_from_value (value , inner_type )
70133 if isinstance (ast_value , NullValueNode ):
71- return None
134+ raise GraphQLError (
135+ "Received Null value for a Non-Null type " f"{ inspect (inner_type )} ."
136+ )
72137 return ast_value
73138
74139 # only explicit None, not Undefined or NaN
@@ -77,7 +142,7 @@ def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
77142
78143 # undefined
79144 if value is Undefined :
80- return None
145+ raise GraphQLError ( f"Received Undefined value for type { inspect ( type_ ) } ." )
81146
82147 # Convert Python list to GraphQL list. If the GraphQLType is a list, but the value
83148 # is not a list, convert the value using the list's item type.
@@ -108,7 +173,32 @@ def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
108173 )
109174 return ObjectValueNode (fields = FrozenList (field_nodes ))
110175
111- return default_ast_from_value (value , type_ )
176+ if is_leaf_type (type_ ):
177+ # Since value is an internally represented value, it must be serialized to an
178+ # externally represented value before converting into an AST.
179+ serialized = type_ .serialize (value ) # type: ignore
180+
181+ # if the serialized value is a string, then we should use the
182+ # type to determine if it is an enum, an ID or a normal string
183+ if isinstance (serialized , str ):
184+ # Enum types use Enum literals.
185+ if is_enum_type (type_ ):
186+ return EnumValueNode (value = serialized )
187+
188+ # ID types can use Int literals.
189+ if type_ is GraphQLID and _re_integer_string .match (serialized ):
190+ return IntValueNode (value = serialized )
191+
192+ return StringValueNode (value = serialized )
193+
194+ # Some custom scalars will serialize to dicts or lists
195+ # Providing here a default conversion to AST using our best judgment
196+ # until graphql-js issue #1817 is solved
197+ # https://github.com/graphql/graphql-js/issues/1817
198+ return ast_from_serialized_value_untyped (serialized )
199+
200+ # Not reachable. All possible input types have been considered.
201+ raise TypeError (f"Unexpected input type: { inspect (type_ )} ." )
112202
113203
114204def dsl_gql (
0 commit comments