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

184 lines
6.2 KiB
Python

from typing import Any, Callable, Dict, List, Optional, Union, cast
from ..error import GraphQLError
from ..pyutils import (
Path,
did_you_mean,
inspect,
is_iterable,
print_path_list,
suggestion_list,
Undefined,
)
from ..type import (
GraphQLInputObjectType,
GraphQLInputType,
GraphQLList,
GraphQLScalarType,
is_leaf_type,
is_input_object_type,
is_list_type,
is_non_null_type,
GraphQLNonNull,
)
__all__ = ["coerce_input_value"]
OnErrorCB = Callable[[List[Union[str, int]], Any, GraphQLError], None]
def default_on_error(
path: List[Union[str, int]], invalid_value: Any, error: GraphQLError
) -> None:
error_prefix = "Invalid value " + inspect(invalid_value)
if path:
error_prefix += f" at 'value{print_path_list(path)}'"
error.message = error_prefix + ": " + error.message
raise error
def coerce_input_value(
input_value: Any,
type_: GraphQLInputType,
on_error: OnErrorCB = default_on_error,
path: Optional[Path] = None,
) -> Any:
"""Coerce a Python value given a GraphQL Input Type."""
if is_non_null_type(type_):
if input_value is not None and input_value is not Undefined:
type_ = cast(GraphQLNonNull, type_)
return coerce_input_value(input_value, type_.of_type, on_error, path)
on_error(
path.as_list() if path else [],
input_value,
GraphQLError(
f"Expected non-nullable type '{inspect(type_)}' not to be None."
),
)
return Undefined
if input_value is None or input_value is Undefined:
# Explicitly return the value null.
return None
if is_list_type(type_):
type_ = cast(GraphQLList, type_)
item_type = type_.of_type
if is_iterable(input_value):
coerced_list: List[Any] = []
append_item = coerced_list.append
for index, item_value in enumerate(input_value):
append_item(
coerce_input_value(
item_value, item_type, on_error, Path(path, index, None)
)
)
return coerced_list
# Lists accept a non-list value as a list of one.
return [coerce_input_value(input_value, item_type, on_error, path)]
if is_input_object_type(type_):
type_ = cast(GraphQLInputObjectType, type_)
if not isinstance(input_value, dict):
on_error(
path.as_list() if path else [],
input_value,
GraphQLError(f"Expected type '{type_.name}' to be a mapping."),
)
return Undefined
coerced_dict: Dict[str, Any] = {}
fields = type_.fields
for field_name, field in fields.items():
field_value = input_value.get(field_name, Undefined)
if field_value is Undefined:
if field.default_value is not Undefined:
# Use out name as name if it exists (extension of GraphQL.js).
coerced_dict[field.out_name or field_name] = field.default_value
elif is_non_null_type(field.type): # pragma: no cover else
type_str = inspect(field.type)
on_error(
path.as_list() if path else [],
input_value,
GraphQLError(
f"Field '{field_name}' of required type '{type_str}'"
" was not provided."
),
)
continue
coerced_dict[field.out_name or field_name] = coerce_input_value(
field_value, field.type, on_error, Path(path, field_name, type_.name)
)
# Ensure every provided field is defined.
for field_name in input_value:
if field_name not in fields:
suggestions = suggestion_list(field_name, fields)
on_error(
path.as_list() if path else [],
input_value,
GraphQLError(
f"Field '{field_name}' is not defined by type '{type_.name}'."
+ did_you_mean(suggestions)
),
)
if type_.is_one_of:
keys = list(coerced_dict)
if len(keys) != 1:
on_error(
path.as_list() if path else [],
input_value,
GraphQLError(
"Exactly one key must be specified"
f" for OneOf type '{type_.name}'.",
),
)
else:
key = keys[0]
value = coerced_dict[key]
if value is None:
on_error(
(path.as_list() if path else []) + [key],
value,
GraphQLError(
f"Field '{key}' must be non-null.",
),
)
return type_.out_type(coerced_dict)
if is_leaf_type(type_):
# Scalars determine if a value is valid via `parse_value()`, which can throw to
# indicate failure. If it throws, maintain a reference to the original error.
type_ = cast(GraphQLScalarType, type_)
try:
parse_result = type_.parse_value(input_value)
except GraphQLError as error:
on_error(path.as_list() if path else [], input_value, error)
return Undefined
except Exception as error:
on_error(
path.as_list() if path else [],
input_value,
GraphQLError(
f"Expected type '{type_.name}'. {error}", original_error=error
),
)
return Undefined
if parse_result is Undefined:
on_error(
path.as_list() if path else [],
input_value,
GraphQLError(f"Expected type '{type_.name}'."),
)
return parse_result
# Not reachable. All possible input types have been considered.
raise TypeError(f"Unexpected input type: {inspect(type_)}.")