from enum import Enum from typing import ( Any, Callable, Collection, Dict, Generic, List, Mapping, NamedTuple, Optional, Tuple, TYPE_CHECKING, Type, TypeVar, Union, cast, overload, ) from ..error import GraphQLError from ..language import ( EnumTypeDefinitionNode, EnumValueDefinitionNode, EnumTypeExtensionNode, EnumValueNode, FieldDefinitionNode, FieldNode, FragmentDefinitionNode, InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode, InputValueDefinitionNode, InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode, ObjectTypeDefinitionNode, ObjectTypeExtensionNode, OperationDefinitionNode, ScalarTypeDefinitionNode, ScalarTypeExtensionNode, TypeDefinitionNode, TypeExtensionNode, UnionTypeDefinitionNode, UnionTypeExtensionNode, ValueNode, print_ast, ) from ..pyutils import ( AwaitableOrValue, Path, cached_property, did_you_mean, inspect, is_collection, is_description, suggestion_list, Undefined, ) from ..utilities.value_from_ast_untyped import value_from_ast_untyped from .assert_name import assert_name, assert_enum_value_name try: from typing import TypedDict except ImportError: # Python < 3.8 from typing_extensions import TypedDict if TYPE_CHECKING: from .schema import GraphQLSchema # noqa: F401 __all__ = [ "is_type", "is_scalar_type", "is_object_type", "is_interface_type", "is_union_type", "is_enum_type", "is_input_object_type", "is_list_type", "is_non_null_type", "is_input_type", "is_output_type", "is_leaf_type", "is_composite_type", "is_abstract_type", "is_wrapping_type", "is_nullable_type", "is_named_type", "is_required_argument", "is_required_input_field", "assert_type", "assert_scalar_type", "assert_object_type", "assert_interface_type", "assert_union_type", "assert_enum_type", "assert_input_object_type", "assert_list_type", "assert_non_null_type", "assert_input_type", "assert_output_type", "assert_leaf_type", "assert_composite_type", "assert_abstract_type", "assert_wrapping_type", "assert_nullable_type", "assert_named_type", "get_nullable_type", "get_named_type", "resolve_thunk", "GraphQLAbstractType", "GraphQLArgument", "GraphQLArgumentKwargs", "GraphQLArgumentMap", "GraphQLCompositeType", "GraphQLEnumType", "GraphQLEnumTypeKwargs", "GraphQLEnumValue", "GraphQLEnumValueKwargs", "GraphQLEnumValueMap", "GraphQLField", "GraphQLFieldKwargs", "GraphQLFieldMap", "GraphQLFieldResolver", "GraphQLInputField", "GraphQLInputFieldKwargs", "GraphQLInputFieldMap", "GraphQLInputObjectType", "GraphQLInputObjectTypeKwargs", "GraphQLInputType", "GraphQLInterfaceType", "GraphQLInterfaceTypeKwargs", "GraphQLIsTypeOfFn", "GraphQLLeafType", "GraphQLList", "GraphQLNamedType", "GraphQLNamedTypeKwargs", "GraphQLNamedInputType", "GraphQLNamedOutputType", "GraphQLNullableType", "GraphQLNonNull", "GraphQLResolveInfo", "GraphQLScalarType", "GraphQLScalarTypeKwargs", "GraphQLScalarSerializer", "GraphQLScalarValueParser", "GraphQLScalarLiteralParser", "GraphQLObjectType", "GraphQLObjectTypeKwargs", "GraphQLOutputType", "GraphQLType", "GraphQLTypeResolver", "GraphQLUnionType", "GraphQLUnionTypeKwargs", "GraphQLWrappingType", "Thunk", "ThunkCollection", "ThunkMapping", ] class GraphQLType: """Base class for all GraphQL types""" # Note: We don't use slots for GraphQLType objects because memory considerations # are not really important for the schema definition and it would make caching # properties slower or more complicated. # There are predicates for each kind of GraphQL type. def is_type(type_: Any) -> bool: return isinstance(type_, GraphQLType) def assert_type(type_: Any) -> GraphQLType: if not is_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL type.") return cast(GraphQLType, type_) # These types wrap and modify other types GT = TypeVar("GT", bound=GraphQLType) class GraphQLWrappingType(GraphQLType, Generic[GT]): """Base class for all GraphQL wrapping types""" of_type: GT def __init__(self, type_: GT) -> None: if not is_type(type_): raise TypeError( f"Can only create a wrapper for a GraphQLType, but got: {type_}." ) self.of_type = type_ def __repr__(self) -> str: return f"<{self.__class__.__name__} {self.of_type!r}>" def is_wrapping_type(type_: Any) -> bool: return isinstance(type_, GraphQLWrappingType) def assert_wrapping_type(type_: Any) -> GraphQLWrappingType: if not is_wrapping_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL wrapping type.") return cast(GraphQLWrappingType, type_) class GraphQLNamedTypeKwargs(TypedDict, total=False): name: str description: Optional[str] extensions: Dict[str, Any] # unfortunately, we cannot make the following more specific, because they are # used by subclasses with different node types and typed dicts cannot be refined ast_node: Optional[Any] extension_ast_nodes: Tuple[Any, ...] class GraphQLNamedType(GraphQLType): """Base class for all GraphQL named types""" name: str description: Optional[str] extensions: Dict[str, Any] ast_node: Optional[TypeDefinitionNode] extension_ast_nodes: Tuple[TypeExtensionNode, ...] def __init__( self, name: str, description: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[TypeDefinitionNode] = None, extension_ast_nodes: Optional[Collection[TypeExtensionNode]] = None, ) -> None: assert_name(name) if description is not None and not is_description(description): raise TypeError("The description must be a string.") if extensions is None: extensions = {} elif not isinstance(extensions, dict) or not all( isinstance(key, str) for key in extensions ): raise TypeError(f"{name} extensions must be a dictionary with string keys.") if ast_node and not isinstance(ast_node, TypeDefinitionNode): raise TypeError(f"{name} AST node must be a TypeDefinitionNode.") if extension_ast_nodes: if not is_collection(extension_ast_nodes) or not all( isinstance(node, TypeExtensionNode) for node in extension_ast_nodes ): raise TypeError( f"{name} extension AST nodes must be specified" " as a collection of TypeExtensionNode instances." ) if not isinstance(extension_ast_nodes, tuple): extension_ast_nodes = tuple(extension_ast_nodes) else: extension_ast_nodes = () self.name = name self.description = description self.extensions = extensions self.ast_node = ast_node self.extension_ast_nodes = extension_ast_nodes def __repr__(self) -> str: return f"<{self.__class__.__name__} {self.name!r}>" def __str__(self) -> str: return self.name def to_kwargs(self) -> GraphQLNamedTypeKwargs: return GraphQLNamedTypeKwargs( name=self.name, description=self.description, extensions=self.extensions, ast_node=self.ast_node, extension_ast_nodes=self.extension_ast_nodes, ) def __copy__(self) -> "GraphQLNamedType": # pragma: no cover return self.__class__(**self.to_kwargs()) T = TypeVar("T") ThunkCollection = Union[Callable[[], Collection[T]], Collection[T]] ThunkMapping = Union[Callable[[], Mapping[str, T]], Mapping[str, T]] Thunk = Union[Callable[[], T], T] def resolve_thunk(thunk: Thunk[T]) -> T: """Resolve the given thunk. Used while defining GraphQL types to allow for circular references in otherwise immutable type definitions. """ return thunk() if callable(thunk) else thunk GraphQLScalarSerializer = Callable[[Any], Any] GraphQLScalarValueParser = Callable[[Any], Any] GraphQLScalarLiteralParser = Callable[[ValueNode, Optional[Dict[str, Any]]], Any] class GraphQLScalarTypeKwargs(GraphQLNamedTypeKwargs, total=False): serialize: Optional[GraphQLScalarSerializer] parse_value: Optional[GraphQLScalarValueParser] parse_literal: Optional[GraphQLScalarLiteralParser] specified_by_url: Optional[str] class GraphQLScalarType(GraphQLNamedType): """Scalar Type Definition The leaf values of any request and input values to arguments are Scalars (or Enums) and are defined with a name and a series of functions used to parse input from ast or variables and to ensure validity. If a type's serialize function returns ``None``, then an error will be raised and a ``None`` value will be returned in the response. It is always better to validate. Example:: def serialize_odd(value: Any) -> int: try: value = int(value) except ValueError: raise GraphQLError( f"Scalar 'Odd' cannot represent '{value}'" " since it is not an integer.") if not value % 2: raise GraphQLError( f"Scalar 'Odd' cannot represent '{value}' since it is even.") return value odd_type = GraphQLScalarType('Odd', serialize=serialize_odd) """ specified_by_url: Optional[str] ast_node: Optional[ScalarTypeDefinitionNode] extension_ast_nodes: Tuple[ScalarTypeExtensionNode, ...] def __init__( self, name: str, serialize: Optional[GraphQLScalarSerializer] = None, parse_value: Optional[GraphQLScalarValueParser] = None, parse_literal: Optional[GraphQLScalarLiteralParser] = None, description: Optional[str] = None, specified_by_url: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[ScalarTypeDefinitionNode] = None, extension_ast_nodes: Optional[Collection[ScalarTypeExtensionNode]] = None, ) -> None: super().__init__( name=name, description=description, extensions=extensions, ast_node=ast_node, extension_ast_nodes=extension_ast_nodes, ) if specified_by_url is not None and not isinstance(specified_by_url, str): raise TypeError( f"{name} must provide 'specified_by_url' as a string," f" but got: {inspect(specified_by_url)}." ) if serialize is not None and not callable(serialize): raise TypeError( f"{name} must provide 'serialize' as a function." " If this custom Scalar is also used as an input type," " ensure 'parse_value' and 'parse_literal' functions" " are also provided." ) if parse_literal is not None and ( not callable(parse_literal) or (parse_value is None or not callable(parse_value)) ): raise TypeError( f"{name} must provide" " both 'parse_value' and 'parse_literal' as functions." ) if ast_node and not isinstance(ast_node, ScalarTypeDefinitionNode): raise TypeError(f"{name} AST node must be a ScalarTypeDefinitionNode.") if extension_ast_nodes and not all( isinstance(node, ScalarTypeExtensionNode) for node in extension_ast_nodes ): raise TypeError( f"{name} extension AST nodes must be specified" " as a collection of ScalarTypeExtensionNode instances." ) if serialize is not None: self.serialize = serialize # type: ignore if parse_value is not None: self.parse_value = parse_value # type: ignore if parse_literal is not None: self.parse_literal = parse_literal # type: ignore self.specified_by_url = specified_by_url def __repr__(self) -> str: return f"<{self.__class__.__name__} {self.name!r}>" def __str__(self) -> str: return self.name @staticmethod def serialize(value: Any) -> Any: """Serializes an internal value to include in a response. This default method just passes the value through and should be replaced with a more specific version when creating a scalar type. """ return value @staticmethod def parse_value(value: Any) -> Any: """Parses an externally provided value to use as an input. This default method just passes the value through and should be replaced with a more specific version when creating a scalar type. """ return value def parse_literal( self, node: ValueNode, variables: Optional[Dict[str, Any]] = None ) -> Any: """Parses an externally provided literal value to use as an input. This default method uses the parse_value method and should be replaced with a more specific version when creating a scalar type. """ return self.parse_value(value_from_ast_untyped(node, variables)) def to_kwargs(self) -> GraphQLScalarTypeKwargs: # noinspection PyArgumentList return GraphQLScalarTypeKwargs( # type: ignore super().to_kwargs(), serialize=None if self.serialize is GraphQLScalarType.serialize else self.serialize, parse_value=None if self.parse_value is GraphQLScalarType.parse_value else self.parse_value, parse_literal=None if getattr(self.parse_literal, "__func__", None) is GraphQLScalarType.parse_literal else self.parse_literal, specified_by_url=self.specified_by_url, ) def __copy__(self) -> "GraphQLScalarType": # pragma: no cover return self.__class__(**self.to_kwargs()) def is_scalar_type(type_: Any) -> bool: return isinstance(type_, GraphQLScalarType) def assert_scalar_type(type_: Any) -> GraphQLScalarType: if not is_scalar_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL Scalar type.") return cast(GraphQLScalarType, type_) GraphQLArgumentMap = Dict[str, "GraphQLArgument"] class GraphQLFieldKwargs(TypedDict, total=False): type_: "GraphQLOutputType" args: Optional[GraphQLArgumentMap] resolve: Optional["GraphQLFieldResolver"] subscribe: Optional["GraphQLFieldResolver"] description: Optional[str] deprecation_reason: Optional[str] extensions: Dict[str, Any] ast_node: Optional[FieldDefinitionNode] class GraphQLField: """Definition of a GraphQL field""" type: "GraphQLOutputType" args: GraphQLArgumentMap resolve: Optional["GraphQLFieldResolver"] subscribe: Optional["GraphQLFieldResolver"] description: Optional[str] deprecation_reason: Optional[str] extensions: Dict[str, Any] ast_node: Optional[FieldDefinitionNode] def __init__( self, type_: "GraphQLOutputType", args: Optional[GraphQLArgumentMap] = None, resolve: Optional["GraphQLFieldResolver"] = None, subscribe: Optional["GraphQLFieldResolver"] = None, description: Optional[str] = None, deprecation_reason: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[FieldDefinitionNode] = None, ) -> None: if not is_output_type(type_): raise TypeError("Field type must be an output type.") if args is None: args = {} elif not isinstance(args, dict): raise TypeError("Field args must be a dict with argument names as keys.") elif not all( isinstance(value, GraphQLArgument) or is_input_type(value) for value in args.values() ): raise TypeError( "Field args must be GraphQLArguments or input type objects." ) else: args = { assert_name(name): value if isinstance(value, GraphQLArgument) else GraphQLArgument(cast(GraphQLInputType, value)) for name, value in args.items() } if resolve is not None and not callable(resolve): raise TypeError( "Field resolver must be a function if provided, " f" but got: {inspect(resolve)}." ) if description is not None and not is_description(description): raise TypeError("The description must be a string.") if deprecation_reason is not None and not is_description(deprecation_reason): raise TypeError("The deprecation reason must be a string.") if extensions is None: extensions = {} elif not isinstance(extensions, dict) or not all( isinstance(key, str) for key in extensions ): raise TypeError("Field extensions must be a dictionary with string keys.") if ast_node and not isinstance(ast_node, FieldDefinitionNode): raise TypeError("Field AST node must be a FieldDefinitionNode.") self.type = type_ self.args = args or {} self.resolve = resolve self.subscribe = subscribe self.description = description self.deprecation_reason = deprecation_reason self.extensions = extensions self.ast_node = ast_node def __repr__(self) -> str: return f"<{self.__class__.__name__} {self.type!r}>" def __str__(self) -> str: return f"Field: {self.type}" def __eq__(self, other: Any) -> bool: return self is other or ( isinstance(other, GraphQLField) and self.type == other.type and self.args == other.args and self.resolve == other.resolve and self.description == other.description and self.deprecation_reason == other.deprecation_reason and self.extensions == other.extensions ) def to_kwargs(self) -> GraphQLFieldKwargs: return GraphQLFieldKwargs( type_=self.type, args=self.args.copy() if self.args else None, resolve=self.resolve, subscribe=self.subscribe, deprecation_reason=self.deprecation_reason, description=self.description, extensions=self.extensions, ast_node=self.ast_node, ) def __copy__(self) -> "GraphQLField": # pragma: no cover return self.__class__(**self.to_kwargs()) class GraphQLResolveInfo(NamedTuple): """Collection of information passed to the resolvers. This is always passed as the first argument to the resolvers. Note that contrary to the JavaScript implementation, the context (commonly used to represent an authenticated user, or request-specific caches) is included here and not passed as an additional argument. """ field_name: str field_nodes: List[FieldNode] return_type: "GraphQLOutputType" parent_type: "GraphQLObjectType" path: Path schema: "GraphQLSchema" fragments: Dict[str, FragmentDefinitionNode] root_value: Any operation: OperationDefinitionNode variable_values: Dict[str, Any] context: Any is_awaitable: Callable[[Any], bool] # Note: Contrary to the Javascript implementation of GraphQLFieldResolver, # the context is passed as part of the GraphQLResolveInfo and any arguments # are passed individually as keyword arguments. GraphQLFieldResolverWithoutArgs = Callable[[Any, GraphQLResolveInfo], Any] # Unfortunately there is currently no syntax to indicate optional or keyword # arguments in Python, so we also allow any other Callable as a workaround: GraphQLFieldResolver = Callable[..., Any] # Note: Contrary to the Javascript implementation of GraphQLTypeResolver, # the context is passed as part of the GraphQLResolveInfo: GraphQLTypeResolver = Callable[ [Any, GraphQLResolveInfo, "GraphQLAbstractType"], AwaitableOrValue[Optional[str]], ] # Note: Contrary to the Javascript implementation of GraphQLIsTypeOfFn, # the context is passed as part of the GraphQLResolveInfo: GraphQLIsTypeOfFn = Callable[[Any, GraphQLResolveInfo], AwaitableOrValue[bool]] GraphQLFieldMap = Dict[str, GraphQLField] class GraphQLArgumentKwargs(TypedDict, total=False): type_: "GraphQLInputType" default_value: Any description: Optional[str] deprecation_reason: Optional[str] out_name: Optional[str] extensions: Dict[str, Any] ast_node: Optional[InputValueDefinitionNode] class GraphQLArgument: """Definition of a GraphQL argument""" type: "GraphQLInputType" default_value: Any description: Optional[str] deprecation_reason: Optional[str] out_name: Optional[str] # for transforming names (extension of GraphQL.js) extensions: Dict[str, Any] ast_node: Optional[InputValueDefinitionNode] def __init__( self, type_: "GraphQLInputType", default_value: Any = Undefined, description: Optional[str] = None, deprecation_reason: Optional[str] = None, out_name: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[InputValueDefinitionNode] = None, ) -> None: if not is_input_type(type_): raise TypeError("Argument type must be a GraphQL input type.") if description is not None and not is_description(description): raise TypeError("Argument description must be a string.") if deprecation_reason is not None and not is_description(deprecation_reason): raise TypeError("Argument deprecation reason must be a string.") if out_name is not None and not isinstance(out_name, str): raise TypeError("Argument out name must be a string.") if extensions is None: extensions = {} elif not isinstance(extensions, dict) or not all( isinstance(key, str) for key in extensions ): raise TypeError( "Argument extensions must be a dictionary with string keys." ) if ast_node and not isinstance(ast_node, InputValueDefinitionNode): raise TypeError("Argument AST node must be an InputValueDefinitionNode.") self.type = type_ self.default_value = default_value self.description = description self.deprecation_reason = deprecation_reason self.out_name = out_name self.extensions = extensions self.ast_node = ast_node def __eq__(self, other: Any) -> bool: return self is other or ( isinstance(other, GraphQLArgument) and self.type == other.type and self.default_value == other.default_value and self.description == other.description and self.deprecation_reason == other.deprecation_reason and self.out_name == other.out_name and self.extensions == other.extensions ) def to_kwargs(self) -> GraphQLArgumentKwargs: return GraphQLArgumentKwargs( type_=self.type, default_value=self.default_value, description=self.description, deprecation_reason=self.deprecation_reason, out_name=self.out_name, extensions=self.extensions, ast_node=self.ast_node, ) def __copy__(self) -> "GraphQLArgument": # pragma: no cover return self.__class__(**self.to_kwargs()) def is_required_argument(arg: GraphQLArgument) -> bool: return is_non_null_type(arg.type) and arg.default_value is Undefined class GraphQLObjectTypeKwargs(GraphQLNamedTypeKwargs, total=False): fields: GraphQLFieldMap interfaces: Tuple["GraphQLInterfaceType", ...] is_type_of: Optional[GraphQLIsTypeOfFn] class GraphQLObjectType(GraphQLNamedType): """Object Type Definition Almost all the GraphQL types you define will be object types. Object types have a name, but most importantly describe their fields. Example:: AddressType = GraphQLObjectType('Address', { 'street': GraphQLField(GraphQLString), 'number': GraphQLField(GraphQLInt), 'formatted': GraphQLField(GraphQLString, lambda obj, info, **args: f'{obj.number} {obj.street}') }) When two types need to refer to each other, or a type needs to refer to itself in a field, you can use a lambda function with no arguments (a so-called "thunk") to supply the fields lazily. Example:: PersonType = GraphQLObjectType('Person', lambda: { 'name': GraphQLField(GraphQLString), 'bestFriend': GraphQLField(PersonType) }) """ is_type_of: Optional[GraphQLIsTypeOfFn] ast_node: Optional[ObjectTypeDefinitionNode] extension_ast_nodes: Tuple[ObjectTypeExtensionNode, ...] def __init__( self, name: str, fields: ThunkMapping[GraphQLField], interfaces: Optional[ThunkCollection["GraphQLInterfaceType"]] = None, is_type_of: Optional[GraphQLIsTypeOfFn] = None, extensions: Optional[Dict[str, Any]] = None, description: Optional[str] = None, ast_node: Optional[ObjectTypeDefinitionNode] = None, extension_ast_nodes: Optional[Collection[ObjectTypeExtensionNode]] = None, ) -> None: super().__init__( name=name, description=description, extensions=extensions, ast_node=ast_node, extension_ast_nodes=extension_ast_nodes, ) if is_type_of is not None and not callable(is_type_of): raise TypeError( f"{name} must provide 'is_type_of' as a function," f" but got: {inspect(is_type_of)}." ) if ast_node and not isinstance(ast_node, ObjectTypeDefinitionNode): raise TypeError(f"{name} AST node must be an ObjectTypeDefinitionNode.") if extension_ast_nodes and not all( isinstance(node, ObjectTypeExtensionNode) for node in extension_ast_nodes ): raise TypeError( f"{name} extension AST nodes must be specified" " as a collection of ObjectTypeExtensionNode instances." ) self._fields = fields self._interfaces = interfaces self.is_type_of = is_type_of def to_kwargs(self) -> GraphQLObjectTypeKwargs: # noinspection PyArgumentList return GraphQLObjectTypeKwargs( # type: ignore super().to_kwargs(), fields=self.fields.copy(), interfaces=self.interfaces, is_type_of=self.is_type_of, ) def __copy__(self) -> "GraphQLObjectType": # pragma: no cover return self.__class__(**self.to_kwargs()) @cached_property def fields(self) -> GraphQLFieldMap: """Get provided fields, wrapping them as GraphQLFields if needed.""" try: fields = resolve_thunk(self._fields) except Exception as error: cls = GraphQLError if isinstance(error, GraphQLError) else TypeError raise cls(f"{self.name} fields cannot be resolved. {error}") from error if not isinstance(fields, Mapping) or not all( isinstance(key, str) for key in fields ): raise TypeError( f"{self.name} fields must be specified" " as a mapping with field names as keys." ) if not all( isinstance(value, GraphQLField) or is_output_type(value) for value in fields.values() ): raise TypeError( f"{self.name} fields must be GraphQLField or output type objects." ) return { assert_name(name): value if isinstance(value, GraphQLField) else GraphQLField(value) # type: ignore for name, value in fields.items() } @cached_property def interfaces(self) -> Tuple["GraphQLInterfaceType", ...]: """Get provided interfaces.""" try: interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk( self._interfaces # type: ignore ) except Exception as error: cls = GraphQLError if isinstance(error, GraphQLError) else TypeError raise cls(f"{self.name} interfaces cannot be resolved. {error}") from error if interfaces is None: interfaces = () elif not is_collection(interfaces) or not all( isinstance(value, GraphQLInterfaceType) for value in interfaces ): raise TypeError( f"{self.name} interfaces must be specified" " as a collection of GraphQLInterfaceType instances." ) return tuple(interfaces) def is_object_type(type_: Any) -> bool: return isinstance(type_, GraphQLObjectType) def assert_object_type(type_: Any) -> GraphQLObjectType: if not is_object_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL Object type.") return cast(GraphQLObjectType, type_) class GraphQLInterfaceTypeKwargs(GraphQLNamedTypeKwargs, total=False): fields: GraphQLFieldMap interfaces: Tuple["GraphQLInterfaceType", ...] resolve_type: Optional[GraphQLTypeResolver] class GraphQLInterfaceType(GraphQLNamedType): """Interface Type Definition When a field can return one of a heterogeneous set of types, an Interface type is used to describe what types are possible, what fields are in common across all types, as well as a function to determine which type is actually used when the field is resolved. Example:: EntityType = GraphQLInterfaceType('Entity', { 'name': GraphQLField(GraphQLString), }) """ resolve_type: Optional[GraphQLTypeResolver] ast_node: Optional[InterfaceTypeDefinitionNode] extension_ast_nodes: Tuple[InterfaceTypeExtensionNode, ...] def __init__( self, name: str, fields: ThunkMapping[GraphQLField], interfaces: Optional[ThunkCollection["GraphQLInterfaceType"]] = None, resolve_type: Optional[GraphQLTypeResolver] = None, description: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[InterfaceTypeDefinitionNode] = None, extension_ast_nodes: Optional[Collection[InterfaceTypeExtensionNode]] = None, ) -> None: super().__init__( name=name, description=description, extensions=extensions, ast_node=ast_node, extension_ast_nodes=extension_ast_nodes, ) if resolve_type is not None and not callable(resolve_type): raise TypeError( f"{name} must provide 'resolve_type' as a function," f" but got: {inspect(resolve_type)}." ) if ast_node and not isinstance(ast_node, InterfaceTypeDefinitionNode): raise TypeError(f"{name} AST node must be an InterfaceTypeDefinitionNode.") if extension_ast_nodes and not all( isinstance(node, InterfaceTypeExtensionNode) for node in extension_ast_nodes ): raise TypeError( f"{name} extension AST nodes must be specified" " as a collection of InterfaceTypeExtensionNode instances." ) self._fields = fields self._interfaces = interfaces self.resolve_type = resolve_type def to_kwargs(self) -> GraphQLInterfaceTypeKwargs: # noinspection PyArgumentList return GraphQLInterfaceTypeKwargs( # type: ignore super().to_kwargs(), fields=self.fields.copy(), interfaces=self.interfaces, resolve_type=self.resolve_type, ) def __copy__(self) -> "GraphQLInterfaceType": # pragma: no cover return self.__class__(**self.to_kwargs()) @cached_property def fields(self) -> GraphQLFieldMap: """Get provided fields, wrapping them as GraphQLFields if needed.""" try: fields = resolve_thunk(self._fields) except Exception as error: cls = GraphQLError if isinstance(error, GraphQLError) else TypeError raise cls(f"{self.name} fields cannot be resolved. {error}") from error if not isinstance(fields, Mapping) or not all( isinstance(key, str) for key in fields ): raise TypeError( f"{self.name} fields must be specified" " as a mapping with field names as keys." ) if not all( isinstance(value, GraphQLField) or is_output_type(value) for value in fields.values() ): raise TypeError( f"{self.name} fields must be GraphQLField or output type objects." ) return { assert_name(name): value if isinstance(value, GraphQLField) else GraphQLField(value) # type: ignore for name, value in fields.items() } @cached_property def interfaces(self) -> Tuple["GraphQLInterfaceType", ...]: """Get provided interfaces.""" try: interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk( self._interfaces # type: ignore ) except Exception as error: cls = GraphQLError if isinstance(error, GraphQLError) else TypeError raise cls(f"{self.name} interfaces cannot be resolved. {error}") from error if interfaces is None: interfaces = () elif not is_collection(interfaces) or not all( isinstance(value, GraphQLInterfaceType) for value in interfaces ): raise TypeError( f"{self.name} interfaces must be specified" " as a collection of GraphQLInterfaceType instances." ) return tuple(interfaces) def is_interface_type(type_: Any) -> bool: return isinstance(type_, GraphQLInterfaceType) def assert_interface_type(type_: Any) -> GraphQLInterfaceType: if not is_interface_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL Interface type.") return cast(GraphQLInterfaceType, type_) class GraphQLUnionTypeKwargs(GraphQLNamedTypeKwargs, total=False): types: Tuple[GraphQLObjectType, ...] resolve_type: Optional[GraphQLTypeResolver] class GraphQLUnionType(GraphQLNamedType): """Union Type Definition When a field can return one of a heterogeneous set of types, a Union type is used to describe what types are possible as well as providing a function to determine which type is actually used when the field is resolved. Example:: def resolve_type(obj, _info, _type): if isinstance(obj, Dog): return DogType() if isinstance(obj, Cat): return CatType() PetType = GraphQLUnionType('Pet', [DogType, CatType], resolve_type) """ resolve_type: Optional[GraphQLTypeResolver] ast_node: Optional[UnionTypeDefinitionNode] extension_ast_nodes: Tuple[UnionTypeExtensionNode, ...] def __init__( self, name: str, types: ThunkCollection[GraphQLObjectType], resolve_type: Optional[GraphQLTypeResolver] = None, description: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[UnionTypeDefinitionNode] = None, extension_ast_nodes: Optional[Collection[UnionTypeExtensionNode]] = None, ) -> None: super().__init__( name=name, description=description, extensions=extensions, ast_node=ast_node, extension_ast_nodes=extension_ast_nodes, ) if resolve_type is not None and not callable(resolve_type): raise TypeError( f"{name} must provide 'resolve_type' as a function," f" but got: {inspect(resolve_type)}." ) if ast_node and not isinstance(ast_node, UnionTypeDefinitionNode): raise TypeError(f"{name} AST node must be a UnionTypeDefinitionNode.") if extension_ast_nodes and not all( isinstance(node, UnionTypeExtensionNode) for node in extension_ast_nodes ): raise TypeError( f"{name} extension AST nodes must be specified" " as a collection of UnionTypeExtensionNode instances." ) self._types = types self.resolve_type = resolve_type def to_kwargs(self) -> GraphQLUnionTypeKwargs: # noinspection PyArgumentList return GraphQLUnionTypeKwargs( # type: ignore super().to_kwargs(), types=self.types, resolve_type=self.resolve_type ) def __copy__(self) -> "GraphQLUnionType": # pragma: no cover return self.__class__(**self.to_kwargs()) @cached_property def types(self) -> Tuple[GraphQLObjectType, ...]: """Get provided types.""" try: types: Collection[GraphQLObjectType] = resolve_thunk(self._types) except Exception as error: cls = GraphQLError if isinstance(error, GraphQLError) else TypeError raise cls(f"{self.name} types cannot be resolved. {error}") from error if types is None: types = () elif not is_collection(types) or not all( isinstance(value, GraphQLObjectType) for value in types ): raise TypeError( f"{self.name} types must be specified" " as a collection of GraphQLObjectType instances." ) return tuple(types) def is_union_type(type_: Any) -> bool: return isinstance(type_, GraphQLUnionType) def assert_union_type(type_: Any) -> GraphQLUnionType: if not is_union_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL Union type.") return cast(GraphQLUnionType, type_) GraphQLEnumValueMap = Dict[str, "GraphQLEnumValue"] class GraphQLEnumTypeKwargs(GraphQLNamedTypeKwargs, total=False): values: GraphQLEnumValueMap names_as_values: Optional[bool] class GraphQLEnumType(GraphQLNamedType): """Enum Type Definition Some leaf values of requests and input values are Enums. GraphQL serializes Enum values as strings, however internally Enums can be represented by any kind of type, often integers. They can also be provided as a Python Enum. In this case, the flag `names_as_values` determines what will be used as internal representation. The default value of `False` will use the enum values, the value `True` will use the enum names, and the value `None` will use the members themselves. Example:: RGBType = GraphQLEnumType('RGB', { 'RED': 0, 'GREEN': 1, 'BLUE': 2 }) Example using a Python Enum:: class RGBEnum(enum.Enum): RED = 0 GREEN = 1 BLUE = 2 RGBType = GraphQLEnumType('RGB', enum.Enum) Instead of raw values, you can also specify GraphQLEnumValue objects with more detail like description or deprecation information. Note: If a value is not provided in a definition, the name of the enum value will be used as its internal value when the value is serialized. """ values: GraphQLEnumValueMap ast_node: Optional[EnumTypeDefinitionNode] extension_ast_nodes: Tuple[EnumTypeExtensionNode, ...] def __init__( self, name: str, values: Union[GraphQLEnumValueMap, Mapping[str, Any], Type[Enum]], names_as_values: Optional[bool] = False, description: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[EnumTypeDefinitionNode] = None, extension_ast_nodes: Optional[Collection[EnumTypeExtensionNode]] = None, ) -> None: super().__init__( name=name, description=description, extensions=extensions, ast_node=ast_node, extension_ast_nodes=extension_ast_nodes, ) try: # check for enum values = cast(Enum, values).__members__ # type: ignore except AttributeError: if not isinstance(values, Mapping) or not all( isinstance(name, str) for name in values ): try: # noinspection PyTypeChecker values = dict(values) # type: ignore except (TypeError, ValueError): raise TypeError( f"{name} values must be an Enum or a mapping" " with value names as keys." ) values = cast(Dict[str, Any], values) else: values = cast(Dict[str, Enum], values) if names_as_values is False: values = {key: value.value for key, value in values.items()} elif names_as_values is True: values = {key: key for key in values} values = { assert_enum_value_name(key): value if isinstance(value, GraphQLEnumValue) else GraphQLEnumValue(value) for key, value in values.items() } if ast_node and not isinstance(ast_node, EnumTypeDefinitionNode): raise TypeError(f"{name} AST node must be an EnumTypeDefinitionNode.") if extension_ast_nodes and not all( isinstance(node, EnumTypeExtensionNode) for node in extension_ast_nodes ): raise TypeError( f"{name} extension AST nodes must be specified" " as a collection of EnumTypeExtensionNode instances." ) self.values = values def to_kwargs(self) -> GraphQLEnumTypeKwargs: # noinspection PyArgumentList return GraphQLEnumTypeKwargs( # type: ignore super().to_kwargs(), values=self.values.copy() ) def __copy__(self) -> "GraphQLEnumType": # pragma: no cover return self.__class__(**self.to_kwargs()) @cached_property def _value_lookup(self) -> Dict[Any, str]: # use first value or name as lookup lookup: Dict[Any, str] = {} for name, enum_value in self.values.items(): value = enum_value.value if value is None or value is Undefined: value = name try: if value not in lookup: lookup[value] = name except TypeError: pass # ignore unhashable values return lookup def serialize(self, output_value: Any) -> str: try: return self._value_lookup[output_value] except KeyError: # hashable value not found pass except TypeError: # unhashable value, we need to scan all values for enum_name, enum_value in self.values.items(): if enum_value.value == output_value: return enum_name raise GraphQLError( f"Enum '{self.name}' cannot represent value: {inspect(output_value)}" ) def parse_value(self, input_value: str) -> Any: if isinstance(input_value, str): try: enum_value = self.values[input_value] except KeyError: raise GraphQLError( f"Value '{input_value}' does not exist in '{self.name}' enum." + did_you_mean_enum_value(self, input_value) ) return enum_value.value value_str = inspect(input_value) raise GraphQLError( f"Enum '{self.name}' cannot represent non-string value: {value_str}." + did_you_mean_enum_value(self, value_str) ) def parse_literal( self, value_node: ValueNode, _variables: Optional[Dict[str, Any]] = None ) -> Any: # Note: variables will be resolved before calling this method. if isinstance(value_node, EnumValueNode): try: enum_value = self.values[value_node.value] except KeyError: value_str = print_ast(value_node) raise GraphQLError( f"Value '{value_str}' does not exist in '{self.name}' enum." + did_you_mean_enum_value(self, value_str), value_node, ) return enum_value.value value_str = print_ast(value_node) raise GraphQLError( f"Enum '{self.name}' cannot represent non-enum value: {value_str}." + did_you_mean_enum_value(self, value_str), value_node, ) def is_enum_type(type_: Any) -> bool: return isinstance(type_, GraphQLEnumType) def assert_enum_type(type_: Any) -> GraphQLEnumType: if not is_enum_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL Enum type.") return cast(GraphQLEnumType, type_) def did_you_mean_enum_value(enum_type: GraphQLEnumType, unknown_value_str: str) -> str: suggested_values = suggestion_list(unknown_value_str, enum_type.values) return did_you_mean(suggested_values, "the enum value") class GraphQLEnumValueKwargs(TypedDict, total=False): value: Any description: Optional[str] deprecation_reason: Optional[str] extensions: Dict[str, Any] ast_node: Optional[EnumValueDefinitionNode] class GraphQLEnumValue: value: Any description: Optional[str] deprecation_reason: Optional[str] extensions: Dict[str, Any] ast_node: Optional[EnumValueDefinitionNode] def __init__( self, value: Any = None, description: Optional[str] = None, deprecation_reason: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[EnumValueDefinitionNode] = None, ) -> None: if description is not None and not is_description(description): raise TypeError("The description of the enum value must be a string.") if deprecation_reason is not None and not is_description(deprecation_reason): raise TypeError( "The deprecation reason for the enum value must be a string." ) if extensions is None: extensions = {} elif not isinstance(extensions, dict) or not all( isinstance(key, str) for key in extensions ): raise TypeError( "Enum value extensions must be a dictionary with string keys." ) if ast_node and not isinstance(ast_node, EnumValueDefinitionNode): raise TypeError("AST node must be an EnumValueDefinitionNode.") self.value = value self.description = description self.deprecation_reason = deprecation_reason self.extensions = extensions self.ast_node = ast_node def __eq__(self, other: Any) -> bool: return self is other or ( isinstance(other, GraphQLEnumValue) and self.value == other.value and self.description == other.description and self.deprecation_reason == other.deprecation_reason and self.extensions == other.extensions ) def to_kwargs(self) -> GraphQLEnumValueKwargs: return GraphQLEnumValueKwargs( value=self.value, description=self.description, deprecation_reason=self.deprecation_reason, extensions=self.extensions, ast_node=self.ast_node, ) def __copy__(self) -> "GraphQLEnumValue": # pragma: no cover return self.__class__(**self.to_kwargs()) GraphQLInputFieldMap = Dict[str, "GraphQLInputField"] GraphQLInputFieldOutType = Callable[[Dict[str, Any]], Any] class GraphQLInputObjectTypeKwargs(GraphQLNamedTypeKwargs, total=False): fields: GraphQLInputFieldMap out_type: Optional[GraphQLInputFieldOutType] class GraphQLInputObjectType(GraphQLNamedType): """Input Object Type Definition An input object defines a structured collection of fields which may be supplied to a field argument. Using ``NonNull`` will ensure that a value must be provided by the query. Example:: NonNullFloat = GraphQLNonNull(GraphQLFloat()) class GeoPoint(GraphQLInputObjectType): name = 'GeoPoint' fields = { 'lat': GraphQLInputField(NonNullFloat), 'lon': GraphQLInputField(NonNullFloat), 'alt': GraphQLInputField( GraphQLFloat(), default_value=0) } The outbound values will be Python dictionaries by default, but you can have them converted to other types by specifying an ``out_type`` function or class. """ ast_node: Optional[InputObjectTypeDefinitionNode] extension_ast_nodes: Tuple[InputObjectTypeExtensionNode, ...] def __init__( self, name: str, fields: ThunkMapping["GraphQLInputField"], description: Optional[str] = None, out_type: Optional[GraphQLInputFieldOutType] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[InputObjectTypeDefinitionNode] = None, extension_ast_nodes: Optional[Collection[InputObjectTypeExtensionNode]] = None, ) -> None: super().__init__( name=name, description=description, extensions=extensions, ast_node=ast_node, extension_ast_nodes=extension_ast_nodes, ) if out_type is not None and not callable(out_type): raise TypeError(f"The out type for {name} must be a function or a class.") if ast_node and not isinstance(ast_node, InputObjectTypeDefinitionNode): raise TypeError( f"{name} AST node must be an InputObjectTypeDefinitionNode." ) if extension_ast_nodes and not all( isinstance(node, InputObjectTypeExtensionNode) for node in extension_ast_nodes ): raise TypeError( f"{name} extension AST nodes must be specified" " as a collection of InputObjectTypeExtensionNode instances." ) self._fields = fields if out_type is not None: self.out_type = out_type # type: ignore @staticmethod def out_type(value: Dict[str, Any]) -> Any: """Transform outbound values (this is an extension of GraphQL.js). This default implementation passes values unaltered as dictionaries. """ return value def to_kwargs(self) -> GraphQLInputObjectTypeKwargs: # noinspection PyArgumentList return GraphQLInputObjectTypeKwargs( # type: ignore super().to_kwargs(), fields=self.fields.copy(), out_type=None if self.out_type is GraphQLInputObjectType.out_type else self.out_type, ) def __copy__(self) -> "GraphQLInputObjectType": # pragma: no cover return self.__class__(**self.to_kwargs()) @cached_property def fields(self) -> GraphQLInputFieldMap: """Get provided fields, wrap them as GraphQLInputField if needed.""" try: fields = resolve_thunk(self._fields) except Exception as error: cls = GraphQLError if isinstance(error, GraphQLError) else TypeError raise cls(f"{self.name} fields cannot be resolved. {error}") from error if not isinstance(fields, Mapping) or not all( isinstance(key, str) for key in fields ): raise TypeError( f"{self.name} fields must be specified" " as a mapping with field names as keys." ) if not all( isinstance(value, GraphQLInputField) or is_input_type(value) for value in fields.values() ): raise TypeError( f"{self.name} fields must be" " GraphQLInputField or input type objects." ) return { assert_name(name): value if isinstance(value, GraphQLInputField) else GraphQLInputField(value) # type: ignore for name, value in fields.items() } def is_input_object_type(type_: Any) -> bool: return isinstance(type_, GraphQLInputObjectType) def assert_input_object_type(type_: Any) -> GraphQLInputObjectType: if not is_input_object_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL Input Object type.") return cast(GraphQLInputObjectType, type_) class GraphQLInputFieldKwargs(TypedDict, total=False): type_: "GraphQLInputType" default_value: Any description: Optional[str] deprecation_reason: Optional[str] out_name: Optional[str] extensions: Dict[str, Any] ast_node: Optional[InputValueDefinitionNode] class GraphQLInputField: """Definition of a GraphQL input field""" type: "GraphQLInputType" default_value: Any description: Optional[str] deprecation_reason: Optional[str] out_name: Optional[str] # for transforming names (extension of GraphQL.js) extensions: Dict[str, Any] ast_node: Optional[InputValueDefinitionNode] def __init__( self, type_: "GraphQLInputType", default_value: Any = Undefined, description: Optional[str] = None, deprecation_reason: Optional[str] = None, out_name: Optional[str] = None, extensions: Optional[Dict[str, Any]] = None, ast_node: Optional[InputValueDefinitionNode] = None, ) -> None: if not is_input_type(type_): raise TypeError("Input field type must be a GraphQL input type.") if description is not None and not is_description(description): raise TypeError("Input field description must be a string.") if deprecation_reason is not None and not is_description(deprecation_reason): raise TypeError("Input field deprecation reason must be a string.") if out_name is not None and not isinstance(out_name, str): raise TypeError("Input field out name must be a string.") if extensions is None: extensions = {} elif not isinstance(extensions, dict) or not all( isinstance(key, str) for key in extensions ): raise TypeError( "Input field extensions must be a dictionary with string keys." ) if ast_node and not isinstance(ast_node, InputValueDefinitionNode): raise TypeError("Input field AST node must be an InputValueDefinitionNode.") self.type = type_ self.default_value = default_value self.description = description self.deprecation_reason = deprecation_reason self.out_name = out_name self.extensions = extensions self.ast_node = ast_node def __eq__(self, other: Any) -> bool: return self is other or ( isinstance(other, GraphQLInputField) and self.type == other.type and self.default_value == other.default_value and self.description == other.description and self.deprecation_reason == other.deprecation_reason and self.extensions == other.extensions and self.out_name == other.out_name ) def to_kwargs(self) -> GraphQLInputFieldKwargs: return GraphQLInputFieldKwargs( type_=self.type, default_value=self.default_value, description=self.description, deprecation_reason=self.deprecation_reason, out_name=self.out_name, extensions=self.extensions, ast_node=self.ast_node, ) def __copy__(self) -> "GraphQLInputField": # pragma: no cover return self.__class__(**self.to_kwargs()) def is_required_input_field(field: GraphQLInputField) -> bool: return is_non_null_type(field.type) and field.default_value is Undefined # Wrapper types class GraphQLList(Generic[GT], GraphQLWrappingType[GT]): """List Type Wrapper A list is a wrapping type which points to another type. Lists are often created within the context of defining the fields of an object type. Example:: class PersonType(GraphQLObjectType): name = 'Person' @property def fields(self): return { 'parents': GraphQLField(GraphQLList(PersonType())), 'children': GraphQLField(GraphQLList(PersonType())), } """ def __init__(self, type_: GT) -> None: super().__init__(type_=type_) def __str__(self) -> str: return f"[{self.of_type}]" def is_list_type(type_: Any) -> bool: return isinstance(type_, GraphQLList) def assert_list_type(type_: Any) -> GraphQLList: if not is_list_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL List type.") return cast(GraphQLList, type_) GNT = TypeVar("GNT", bound="GraphQLNullableType") class GraphQLNonNull(GraphQLWrappingType[GNT], Generic[GNT]): """Non-Null Type Wrapper A non-null is a wrapping type which points to another type. Non-null types enforce that their values are never null and can ensure an error is raised if this ever occurs during a request. It is useful for fields which you can make a strong guarantee on non-nullability, for example usually the id field of a database row will never be null. Example:: class RowType(GraphQLObjectType): name = 'Row' fields = { 'id': GraphQLField(GraphQLNonNull(GraphQLString())) } Note: the enforcement of non-nullability occurs within the executor. """ def __init__(self, type_: GNT): super().__init__(type_=type_) if isinstance(type_, GraphQLNonNull): raise TypeError( "Can only create NonNull of a Nullable GraphQLType but got:" f" {type_}." ) def __str__(self) -> str: return f"{self.of_type}!" def is_non_null_type(type_: Any) -> bool: return isinstance(type_, GraphQLNonNull) def assert_non_null_type(type_: Any) -> GraphQLNonNull: if not is_non_null_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL Non-Null type.") return cast(GraphQLNonNull, type_) # These types can all accept null as a value. graphql_nullable_types = ( GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, ) GraphQLNullableType = Union[ GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, ] def is_nullable_type(type_: Any) -> bool: return isinstance(type_, graphql_nullable_types) def assert_nullable_type(type_: Any) -> GraphQLNullableType: if not is_nullable_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL nullable type.") return cast(GraphQLNullableType, type_) @overload def get_nullable_type(type_: None) -> None: ... @overload def get_nullable_type(type_: GraphQLNullableType) -> GraphQLNullableType: ... @overload def get_nullable_type(type_: GraphQLNonNull) -> GraphQLNullableType: ... def get_nullable_type( type_: Optional[Union[GraphQLNullableType, GraphQLNonNull]] ) -> Optional[GraphQLNullableType]: """Unwrap possible non-null type""" if is_non_null_type(type_): type_ = cast(GraphQLNonNull, type_) type_ = type_.of_type return cast(Optional[GraphQLNullableType], type_) # These types may be used as input types for arguments and directives. graphql_input_types = (GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType) GraphQLInputType = Union[ GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType, GraphQLWrappingType ] def is_input_type(type_: Any) -> bool: return isinstance(type_, graphql_input_types) or ( isinstance(type_, GraphQLWrappingType) and is_input_type(type_.of_type) ) def assert_input_type(type_: Any) -> GraphQLInputType: if not is_input_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL input type.") return cast(GraphQLInputType, type_) # These types may be used as output types as the result of fields. graphql_output_types = ( GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, ) GraphQLOutputType = Union[ GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLWrappingType, ] def is_output_type(type_: Any) -> bool: return isinstance(type_, graphql_output_types) or ( isinstance(type_, GraphQLWrappingType) and is_output_type(type_.of_type) ) def assert_output_type(type_: Any) -> GraphQLOutputType: if not is_output_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL output type.") return cast(GraphQLOutputType, type_) # These named types do not include modifiers like List or NonNull. GraphQLNamedInputType = Union[ GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType ] GraphQLNamedOutputType = Union[ GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, ] def is_named_type(type_: Any) -> bool: return isinstance(type_, GraphQLNamedType) def assert_named_type(type_: Any) -> GraphQLNamedType: if not is_named_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL named type.") return cast(GraphQLNamedType, type_) @overload def get_named_type(type_: None) -> None: ... @overload def get_named_type(type_: GraphQLType) -> GraphQLNamedType: ... def get_named_type(type_: Optional[GraphQLType]) -> Optional[GraphQLNamedType]: """Unwrap possible wrapping type""" if type_: unwrapped_type = type_ while is_wrapping_type(unwrapped_type): unwrapped_type = cast(GraphQLWrappingType, unwrapped_type) unwrapped_type = unwrapped_type.of_type return cast(GraphQLNamedType, unwrapped_type) return None # These types may describe types which may be leaf values. graphql_leaf_types = (GraphQLScalarType, GraphQLEnumType) GraphQLLeafType = Union[GraphQLScalarType, GraphQLEnumType] def is_leaf_type(type_: Any) -> bool: return isinstance(type_, graphql_leaf_types) def assert_leaf_type(type_: Any) -> GraphQLLeafType: if not is_leaf_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL leaf type.") return cast(GraphQLLeafType, type_) # These types may describe the parent context of a selection set. graphql_composite_types = (GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType) GraphQLCompositeType = Union[GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType] def is_composite_type(type_: Any) -> bool: return isinstance(type_, graphql_composite_types) def assert_composite_type(type_: Any) -> GraphQLType: if not is_composite_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL composite type.") return cast(GraphQLType, type_) # These types may describe abstract types. graphql_abstract_types = (GraphQLInterfaceType, GraphQLUnionType) GraphQLAbstractType = Union[GraphQLInterfaceType, GraphQLUnionType] def is_abstract_type(type_: Any) -> bool: return isinstance(type_, graphql_abstract_types) def assert_abstract_type(type_: Any) -> GraphQLAbstractType: if not is_abstract_type(type_): raise TypeError(f"Expected {type_} to be a GraphQL composite type.") return cast(GraphQLAbstractType, type_)