Files
zenml/venv/lib/python3.9/site-packages/graphql/utilities/ast_from_value.py
Christian Mantha 2ca0b9ef7c star
2026-03-02 19:10:52 -05:00

140 lines
4.7 KiB
Python

import re
from math import isfinite
from typing import Any, Mapping, Optional, cast
from ..language import (
BooleanValueNode,
EnumValueNode,
FloatValueNode,
IntValueNode,
ListValueNode,
NameNode,
NullValueNode,
ObjectFieldNode,
ObjectValueNode,
StringValueNode,
ValueNode,
)
from ..pyutils import inspect, is_iterable, Undefined
from ..type import (
GraphQLID,
GraphQLInputType,
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
is_enum_type,
is_input_object_type,
is_leaf_type,
is_list_type,
is_non_null_type,
)
__all__ = ["ast_from_value"]
_re_integer_string = re.compile("^-?(?:0|[1-9][0-9]*)$")
def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
"""Produce a GraphQL Value AST given a Python object.
This function will match Python/JSON values to GraphQL AST schema format by using
the suggested GraphQLInputType. For example::
ast_from_value('value', GraphQLString)
A GraphQL type must be provided, which will be used to interpret different Python
values.
================ =======================
JSON Value GraphQL Value
================ =======================
Object Input Object
Array List
Boolean Boolean
String String / Enum Value
Number Int / Float
Mixed Enum Value
null NullValue
================ =======================
"""
if is_non_null_type(type_):
type_ = cast(GraphQLNonNull, type_)
ast_value = ast_from_value(value, type_.of_type)
if isinstance(ast_value, NullValueNode):
return None
return ast_value
# only explicit None, not Undefined or NaN
if value is None:
return NullValueNode()
# undefined
if value is Undefined:
return None
# Convert Python list to GraphQL list. If the GraphQLType is a list, but the value
# is not a list, convert the value using the list's item type.
if is_list_type(type_):
type_ = cast(GraphQLList, type_)
item_type = type_.of_type
if is_iterable(value):
maybe_value_nodes = (ast_from_value(item, item_type) for item in value)
value_nodes = tuple(node for node in maybe_value_nodes if node)
return ListValueNode(values=value_nodes)
return ast_from_value(value, item_type)
# Populate the fields of the input object by creating ASTs from each value in the
# Python dict according to the fields in the input type.
if is_input_object_type(type_):
if value is None or not isinstance(value, Mapping):
return None
type_ = cast(GraphQLInputObjectType, type_)
field_items = (
(field_name, ast_from_value(value[field_name], field.type))
for field_name, field in type_.fields.items()
if field_name in value
)
field_nodes = tuple(
ObjectFieldNode(name=NameNode(value=field_name), value=field_value)
for field_name, field_value in field_items
if field_value
)
return ObjectValueNode(fields=field_nodes)
if is_leaf_type(type_):
# Since value is an internally represented value, it must be serialized to an
# externally represented value before converting into an AST.
serialized = type_.serialize(value) # type: ignore
if serialized is None or serialized is Undefined:
return None
# Others serialize based on their corresponding Python scalar types.
if isinstance(serialized, bool):
return BooleanValueNode(value=serialized)
# Python ints and floats correspond nicely to Int and Float values.
if isinstance(serialized, int):
return IntValueNode(value=str(serialized))
if isinstance(serialized, float) and isfinite(serialized):
value = str(serialized)
if value.endswith(".0"):
value = value[:-2]
return FloatValueNode(value=value)
if isinstance(serialized, str):
# Enum types use Enum literals.
if is_enum_type(type_):
return EnumValueNode(value=serialized)
# ID types can use Int literals.
if type_ is GraphQLID and _re_integer_string.match(serialized):
return IntValueNode(value=serialized)
return StringValueNode(value=serialized)
raise TypeError(f"Cannot convert value to AST: {inspect(serialized)}.")
# Not reachable. All possible input types have been considered.
raise TypeError(f"Unexpected input type: {inspect(type_)}.")