22
33from __future__ import annotations
44
5+ import re
56from math import isfinite
67from typing import TYPE_CHECKING , Any , TypeGuard
78
89from ..error import GraphQLError
910from ..language .ast import (
1011 BooleanValueNode ,
12+ ConstValueNode ,
1113 FloatValueNode ,
1214 IntValueNode ,
1315 StringValueNode ,
1416 ValueNode ,
1517)
1618from ..language .printer import print_ast
1719from ..pyutils import inspect
20+ from ..utilities .value_to_literal import default_scalar_value_to_literal
1821from .definition import GraphQLNamedType , GraphQLScalarType
1922
2023if TYPE_CHECKING :
2124 from collections .abc import Mapping
2225
26+ _re_integer_string = re .compile ("^-?(?:0|[1-9][0-9]*)$" )
27+
2328__all__ = [
2429 "GRAPHQL_MAX_INT" ,
2530 "GRAPHQL_MIN_INT" ,
@@ -104,14 +109,28 @@ def parse_int_literal(value_node: ValueNode, _variables: Any = None) -> int:
104109 return num
105110
106111
112+ def int_value_to_literal (value : Any ) -> ConstValueNode | None :
113+ """Convert an integer value to an Int literal in the AST."""
114+ if (
115+ isinstance (value , (int , float ))
116+ and not isinstance (value , bool )
117+ and isfinite (value )
118+ and value == int (value )
119+ and GRAPHQL_MIN_INT <= value <= GRAPHQL_MAX_INT
120+ ):
121+ return IntValueNode (value = str (int (value )))
122+ return None
123+
124+
107125GraphQLInt = GraphQLScalarType (
108126 name = "Int" ,
109127 description = "The `Int` scalar type represents"
110128 " non-fractional signed whole numeric values."
111129 " Int can represent values between -(2^31) and 2^31 - 1." ,
112130 serialize = serialize_int ,
113131 parse_value = coerce_int ,
114- parse_literal = parse_int_literal ,
132+ parse_const_literal = parse_int_literal ,
133+ value_to_literal = int_value_to_literal ,
115134)
116135
117136
@@ -151,6 +170,14 @@ def parse_float_literal(value_node: ValueNode, _variables: Any = None) -> float:
151170 return float (value_node .value )
152171
153172
173+ def float_value_to_literal (value : Any ) -> ConstValueNode | None :
174+ """Convert a float value to a Float literal in the AST."""
175+ literal = default_scalar_value_to_literal (value )
176+ if isinstance (literal , (FloatValueNode , IntValueNode )):
177+ return literal
178+ return None
179+
180+
154181GraphQLFloat = GraphQLScalarType (
155182 name = "Float" ,
156183 description = "The `Float` scalar type represents"
@@ -159,7 +186,8 @@ def parse_float_literal(value_node: ValueNode, _variables: Any = None) -> float:
159186 "(https://en.wikipedia.org/wiki/IEEE_floating_point)." ,
160187 serialize = serialize_float ,
161188 parse_value = coerce_float ,
162- parse_literal = parse_float_literal ,
189+ parse_const_literal = parse_float_literal ,
190+ value_to_literal = float_value_to_literal ,
163191)
164192
165193
@@ -197,6 +225,14 @@ def parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str:
197225 return value_node .value
198226
199227
228+ def string_value_to_literal (value : Any ) -> ConstValueNode | None :
229+ """Convert a string value to a String literal in the AST."""
230+ literal = default_scalar_value_to_literal (value )
231+ if isinstance (literal , StringValueNode ):
232+ return literal
233+ return None
234+
235+
200236GraphQLString = GraphQLScalarType (
201237 name = "String" ,
202238 description = "The `String` scalar type represents textual data,"
@@ -205,7 +241,8 @@ def parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str:
205241 " to represent free-form human-readable text." ,
206242 serialize = serialize_string ,
207243 parse_value = coerce_string ,
208- parse_literal = parse_string_literal ,
244+ parse_const_literal = parse_string_literal ,
245+ value_to_literal = string_value_to_literal ,
209246)
210247
211248
@@ -239,12 +276,21 @@ def parse_boolean_literal(value_node: ValueNode, _variables: Any = None) -> bool
239276 return value_node .value
240277
241278
279+ def boolean_value_to_literal (value : Any ) -> ConstValueNode | None :
280+ """Convert a boolean value to a Boolean literal in the AST."""
281+ literal = default_scalar_value_to_literal (value )
282+ if isinstance (literal , BooleanValueNode ):
283+ return literal
284+ return None
285+
286+
242287GraphQLBoolean = GraphQLScalarType (
243288 name = "Boolean" ,
244289 description = "The `Boolean` scalar type represents `true` or `false`." ,
245290 serialize = serialize_boolean ,
246291 parse_value = coerce_boolean ,
247- parse_literal = parse_boolean_literal ,
292+ parse_const_literal = parse_boolean_literal ,
293+ value_to_literal = boolean_value_to_literal ,
248294)
249295
250296
@@ -291,6 +337,27 @@ def parse_id_literal(value_node: ValueNode, _variables: Any = None) -> str:
291337 return value_node .value
292338
293339
340+ def id_value_to_literal (value : Any ) -> ConstValueNode | None :
341+ """Convert an ID value to an Int or String literal in the AST."""
342+ # ID types can use number values and Int literals.
343+ string_value : Any = (
344+ str (int (value ))
345+ if isinstance (value , (int , float ))
346+ and not isinstance (value , bool )
347+ and isfinite (value )
348+ and value == int (value )
349+ else value
350+ )
351+ if isinstance (string_value , str ):
352+ # Will parse as an IntValue if it consists only of integer digits.
353+ return (
354+ IntValueNode (value = string_value )
355+ if _re_integer_string .match (string_value )
356+ else StringValueNode (value = string_value , block = False )
357+ )
358+ return None
359+
360+
294361GraphQLID = GraphQLScalarType (
295362 name = "ID" ,
296363 description = "The `ID` scalar type represents a unique identifier,"
@@ -301,7 +368,8 @@ def parse_id_literal(value_node: ValueNode, _variables: Any = None) -> str:
301368 " `4`) input value will be accepted as an ID." ,
302369 serialize = serialize_id ,
303370 parse_value = coerce_id ,
304- parse_literal = parse_id_literal ,
371+ parse_const_literal = parse_id_literal ,
372+ value_to_literal = id_value_to_literal ,
305373)
306374
307375specified_scalar_types : Mapping [str , GraphQLScalarType ] = {
0 commit comments