1from enum import Enum
2from typing import (
3    Any,
4    Callable,
5    Collection,
6    Dict,
7    Generic,
8    List,
9    NamedTuple,
10    Optional,
11    TYPE_CHECKING,
12    Type,
13    TypeVar,
14    Union,
15    cast,
16    overload,
17)
18
19from ..error import GraphQLError
20from ..language import (
21    EnumTypeDefinitionNode,
22    EnumValueDefinitionNode,
23    EnumTypeExtensionNode,
24    EnumValueNode,
25    FieldDefinitionNode,
26    FieldNode,
27    FragmentDefinitionNode,
28    InputObjectTypeDefinitionNode,
29    InputObjectTypeExtensionNode,
30    InputValueDefinitionNode,
31    InterfaceTypeDefinitionNode,
32    InterfaceTypeExtensionNode,
33    ObjectTypeDefinitionNode,
34    ObjectTypeExtensionNode,
35    OperationDefinitionNode,
36    ScalarTypeDefinitionNode,
37    ScalarTypeExtensionNode,
38    TypeDefinitionNode,
39    TypeExtensionNode,
40    UnionTypeDefinitionNode,
41    UnionTypeExtensionNode,
42    ValueNode,
43    print_ast,
44)
45from ..pyutils import (
46    AwaitableOrValue,
47    FrozenList,
48    Path,
49    cached_property,
50    did_you_mean,
51    inspect,
52    is_collection,
53    is_description,
54    suggestion_list,
55    Undefined,
56)
57from ..utilities.value_from_ast_untyped import value_from_ast_untyped
58
59if TYPE_CHECKING:
60    from .schema import GraphQLSchema  # noqa: F401
61
62__all__ = [
63    "is_type",
64    "is_scalar_type",
65    "is_object_type",
66    "is_interface_type",
67    "is_union_type",
68    "is_enum_type",
69    "is_input_object_type",
70    "is_list_type",
71    "is_non_null_type",
72    "is_input_type",
73    "is_output_type",
74    "is_leaf_type",
75    "is_composite_type",
76    "is_abstract_type",
77    "is_wrapping_type",
78    "is_nullable_type",
79    "is_named_type",
80    "is_required_argument",
81    "is_required_input_field",
82    "assert_type",
83    "assert_scalar_type",
84    "assert_object_type",
85    "assert_interface_type",
86    "assert_union_type",
87    "assert_enum_type",
88    "assert_input_object_type",
89    "assert_list_type",
90    "assert_non_null_type",
91    "assert_input_type",
92    "assert_output_type",
93    "assert_leaf_type",
94    "assert_composite_type",
95    "assert_abstract_type",
96    "assert_wrapping_type",
97    "assert_nullable_type",
98    "assert_named_type",
99    "get_nullable_type",
100    "get_named_type",
101    "GraphQLAbstractType",
102    "GraphQLArgument",
103    "GraphQLArgumentMap",
104    "GraphQLCompositeType",
105    "GraphQLEnumType",
106    "GraphQLEnumValue",
107    "GraphQLEnumValueMap",
108    "GraphQLField",
109    "GraphQLFieldMap",
110    "GraphQLFieldResolver",
111    "GraphQLInputField",
112    "GraphQLInputFieldMap",
113    "GraphQLInputObjectType",
114    "GraphQLInputType",
115    "GraphQLInterfaceType",
116    "GraphQLIsTypeOfFn",
117    "GraphQLLeafType",
118    "GraphQLList",
119    "GraphQLNamedType",
120    "GraphQLNullableType",
121    "GraphQLNonNull",
122    "GraphQLResolveInfo",
123    "GraphQLScalarType",
124    "GraphQLScalarSerializer",
125    "GraphQLScalarValueParser",
126    "GraphQLScalarLiteralParser",
127    "GraphQLObjectType",
128    "GraphQLOutputType",
129    "GraphQLType",
130    "GraphQLTypeResolver",
131    "GraphQLUnionType",
132    "GraphQLWrappingType",
133    "Thunk",
134]
135
136
137class GraphQLType:
138    """Base class for all GraphQL types"""
139
140    # Note: We don't use slots for GraphQLType objects because memory considerations
141    # are not really important for the schema definition and it would make caching
142    # properties slower or more complicated.
143
144
145# There are predicates for each kind of GraphQL type.
146
147
148def is_type(type_: Any) -> bool:
149    return isinstance(type_, GraphQLType)
150
151
152def assert_type(type_: Any) -> GraphQLType:
153    if not is_type(type_):
154        raise TypeError(f"Expected {type_} to be a GraphQL type.")
155    return cast(GraphQLType, type_)
156
157
158# These types wrap and modify other types
159
160GT = TypeVar("GT", bound=GraphQLType)
161
162
163class GraphQLWrappingType(GraphQLType, Generic[GT]):
164    """Base class for all GraphQL wrapping types"""
165
166    of_type: GT
167
168    def __init__(self, type_: GT) -> None:
169        if not is_type(type_):
170            raise TypeError(
171                f"Can only create a wrapper for a GraphQLType, but got: {type_}."
172            )
173        self.of_type = type_
174
175    def __repr__(self) -> str:
176        return f"<{self.__class__.__name__} {self.of_type!r}>"
177
178
179def is_wrapping_type(type_: Any) -> bool:
180    return isinstance(type_, GraphQLWrappingType)
181
182
183def assert_wrapping_type(type_: Any) -> GraphQLWrappingType:
184    if not is_wrapping_type(type_):
185        raise TypeError(f"Expected {type_} to be a GraphQL wrapping type.")
186    return cast(GraphQLWrappingType, type_)
187
188
189# These named types do not include modifiers like List or NonNull.
190
191
192class GraphQLNamedType(GraphQLType):
193    """Base class for all GraphQL named types"""
194
195    name: str
196    description: Optional[str]
197    extensions: Optional[Dict[str, Any]]
198    ast_node: Optional[TypeDefinitionNode]
199    extension_ast_nodes: Optional[FrozenList[TypeExtensionNode]]
200
201    def __init__(
202        self,
203        name: str,
204        description: Optional[str] = None,
205        extensions: Optional[Dict[str, Any]] = None,
206        ast_node: Optional[TypeDefinitionNode] = None,
207        extension_ast_nodes: Optional[Collection[TypeExtensionNode]] = None,
208    ) -> None:
209        if not name:
210            raise TypeError("Must provide name.")
211        if not isinstance(name, str):
212            raise TypeError("The name must be a string.")
213        if description is not None and not is_description(description):
214            raise TypeError("The description must be a string.")
215        if extensions is not None and (
216            not isinstance(extensions, dict)
217            or not all(isinstance(key, str) for key in extensions)
218        ):
219            raise TypeError(f"{name} extensions must be a dictionary with string keys.")
220        if ast_node and not isinstance(ast_node, TypeDefinitionNode):
221            raise TypeError(f"{name} AST node must be a TypeDefinitionNode.")
222        if extension_ast_nodes:
223            if not is_collection(extension_ast_nodes) or not all(
224                isinstance(node, TypeExtensionNode) for node in extension_ast_nodes
225            ):
226                raise TypeError(
227                    f"{name} extension AST nodes must be specified"
228                    " as a collection of TypeExtensionNode instances."
229                )
230            if not isinstance(extension_ast_nodes, FrozenList):
231                extension_ast_nodes = FrozenList(extension_ast_nodes)
232        else:
233            extension_ast_nodes = None
234        self.name = name
235        self.description = description
236        self.extensions = extensions
237        self.ast_node = ast_node
238        self.extension_ast_nodes = extension_ast_nodes
239
240    def __repr__(self) -> str:
241        return f"<{self.__class__.__name__} {self.name!r}>"
242
243    def __str__(self) -> str:
244        return self.name
245
246    def to_kwargs(self) -> Dict[str, Any]:
247        return dict(
248            name=self.name,
249            description=self.description,
250            extensions=self.extensions,
251            ast_node=self.ast_node,
252            extension_ast_nodes=self.extension_ast_nodes or FrozenList(),
253        )
254
255    def __copy__(self) -> "GraphQLNamedType":  # pragma: no cover
256        return self.__class__(**self.to_kwargs())
257
258
259def is_named_type(type_: Any) -> bool:
260    return isinstance(type_, GraphQLNamedType)
261
262
263def assert_named_type(type_: Any) -> GraphQLNamedType:
264    if not is_named_type(type_):
265        raise TypeError(f"Expected {type_} to be a GraphQL named type.")
266    return cast(GraphQLNamedType, type_)
267
268
269@overload
270def get_named_type(type_: None) -> None:
271    ...
272
273
274@overload
275def get_named_type(type_: GraphQLType) -> GraphQLNamedType:
276    ...
277
278
279def get_named_type(type_: Optional[GraphQLType]) -> Optional[GraphQLNamedType]:
280    """Unwrap possible wrapping type"""
281    if type_:
282        unwrapped_type = type_
283        while is_wrapping_type(unwrapped_type):
284            unwrapped_type = cast(GraphQLWrappingType, unwrapped_type)
285            unwrapped_type = unwrapped_type.of_type
286        return cast(GraphQLNamedType, unwrapped_type)
287    return None
288
289
290def resolve_thunk(thunk: Any) -> Any:
291    """Resolve the given thunk.
292
293    Used while defining GraphQL types to allow for circular references in otherwise
294    immutable type definitions.
295    """
296    return thunk() if callable(thunk) else thunk
297
298
299GraphQLScalarSerializer = Callable[[Any], Any]
300GraphQLScalarValueParser = Callable[[Any], Any]
301GraphQLScalarLiteralParser = Callable[[ValueNode, Optional[Dict[str, Any]]], Any]
302
303
304class GraphQLScalarType(GraphQLNamedType):
305    """Scalar Type Definition
306
307    The leaf values of any request and input values to arguments are Scalars (or Enums)
308    and are defined with a name and a series of functions used to parse input from ast
309    or variables and to ensure validity.
310
311    If a type's serialize function does not return a value (i.e. it returns ``None``),
312    then no error will be included in the response.
313
314    Example::
315
316        def serialize_odd(value):
317            if value % 2 == 1:
318                return value
319
320        odd_type = GraphQLScalarType('Odd', serialize=serialize_odd)
321
322    """
323
324    specified_by_url: Optional[str]
325    ast_node: Optional[ScalarTypeDefinitionNode]
326    extension_ast_nodes: Optional[FrozenList[ScalarTypeExtensionNode]]
327
328    def __init__(
329        self,
330        name: str,
331        serialize: Optional[GraphQLScalarSerializer] = None,
332        parse_value: Optional[GraphQLScalarValueParser] = None,
333        parse_literal: Optional[GraphQLScalarLiteralParser] = None,
334        description: Optional[str] = None,
335        specified_by_url: Optional[str] = None,
336        extensions: Optional[Dict[str, Any]] = None,
337        ast_node: Optional[ScalarTypeDefinitionNode] = None,
338        extension_ast_nodes: Optional[Collection[ScalarTypeExtensionNode]] = None,
339    ) -> None:
340        super().__init__(
341            name=name,
342            description=description,
343            extensions=extensions,
344            ast_node=ast_node,
345            extension_ast_nodes=extension_ast_nodes,
346        )
347        if specified_by_url is not None and not isinstance(specified_by_url, str):
348            raise TypeError(
349                f"{name} must provide 'specified_by_url' as a string,"
350                f" but got: {inspect(specified_by_url)}."
351            )
352        if serialize is not None and not callable(serialize):
353            raise TypeError(
354                f"{name} must provide 'serialize' as a function."
355                " If this custom Scalar is also used as an input type,"
356                " ensure 'parse_value' and 'parse_literal' functions"
357                " are also provided."
358            )
359        if parse_literal is not None and (
360            not callable(parse_literal)
361            or (parse_value is None or not callable(parse_value))
362        ):
363            raise TypeError(
364                f"{name} must provide"
365                " both 'parse_value' and 'parse_literal' as functions."
366            )
367        if ast_node and not isinstance(ast_node, ScalarTypeDefinitionNode):
368            raise TypeError(f"{name} AST node must be a ScalarTypeDefinitionNode.")
369        if extension_ast_nodes and not all(
370            isinstance(node, ScalarTypeExtensionNode) for node in extension_ast_nodes
371        ):
372            raise TypeError(
373                f"{name} extension AST nodes must be specified"
374                " as a collection of ScalarTypeExtensionNode instances."
375            )
376        if serialize is not None:
377            self.serialize = serialize  # type: ignore
378        if parse_value is not None:
379            self.parse_value = parse_value  # type: ignore
380        if parse_literal is not None:
381            self.parse_literal = parse_literal  # type: ignore
382        self.specified_by_url = specified_by_url
383
384    def __repr__(self) -> str:
385        return f"<{self.__class__.__name__} {self.name!r}>"
386
387    def __str__(self) -> str:
388        return self.name
389
390    @staticmethod
391    def serialize(value: Any) -> Any:
392        """Serializes an internal value to include in a response.
393
394        This default method just passes the value through and should be replaced
395        with a more specific version when creating a scalar type.
396        """
397        return value
398
399    @staticmethod
400    def parse_value(value: Any) -> Any:
401        """Parses an externally provided value to use as an input.
402
403        This default method just passes the value through and should be replaced
404        with a more specific version when creating a scalar type.
405        """
406        return value
407
408    def parse_literal(
409        self, node: ValueNode, variables: Optional[Dict[str, Any]] = None
410    ) -> Any:
411        """Parses an externally provided literal value to use as an input.
412
413        This default method uses the parse_value method and should be replaced
414        with a more specific version when creating a scalar type.
415        """
416        return self.parse_value(value_from_ast_untyped(node, variables))
417
418    def to_kwargs(self) -> Dict[str, Any]:
419        return dict(
420            **super().to_kwargs(),
421            serialize=None
422            if self.serialize is GraphQLScalarType.serialize
423            else self.serialize,
424            parse_value=None
425            if self.parse_value is GraphQLScalarType.parse_value
426            else self.parse_value,
427            parse_literal=None
428            if getattr(self.parse_literal, "__func__", None)
429            is GraphQLScalarType.parse_literal
430            else self.parse_literal,
431            specified_by_url=self.specified_by_url,
432        )
433
434    def __copy__(self) -> "GraphQLScalarType":  # pragma: no cover
435        return self.__class__(**self.to_kwargs())
436
437
438def is_scalar_type(type_: Any) -> bool:
439    return isinstance(type_, GraphQLScalarType)
440
441
442def assert_scalar_type(type_: Any) -> GraphQLScalarType:
443    if not is_scalar_type(type_):
444        raise TypeError(f"Expected {type_} to be a GraphQL Scalar type.")
445    return cast(GraphQLScalarType, type_)
446
447
448GraphQLArgumentMap = Dict[str, "GraphQLArgument"]
449
450
451class GraphQLField:
452    """Definition of a GraphQL field"""
453
454    type: "GraphQLOutputType"
455    args: GraphQLArgumentMap
456    resolve: Optional["GraphQLFieldResolver"]
457    subscribe: Optional["GraphQLFieldResolver"]
458    description: Optional[str]
459    deprecation_reason: Optional[str]
460    extensions: Optional[Dict[str, Any]]
461    ast_node: Optional[FieldDefinitionNode]
462
463    def __init__(
464        self,
465        type_: "GraphQLOutputType",
466        args: Optional[GraphQLArgumentMap] = None,
467        resolve: Optional["GraphQLFieldResolver"] = None,
468        subscribe: Optional["GraphQLFieldResolver"] = None,
469        description: Optional[str] = None,
470        deprecation_reason: Optional[str] = None,
471        extensions: Optional[Dict[str, Any]] = None,
472        ast_node: Optional[FieldDefinitionNode] = None,
473    ) -> None:
474        if not is_output_type(type_):
475            raise TypeError("Field type must be an output type.")
476        if args is None:
477            args = {}
478        elif not isinstance(args, dict):
479            raise TypeError("Field args must be a dict with argument names as keys.")
480        elif not all(
481            isinstance(value, GraphQLArgument) or is_input_type(value)
482            for value in args.values()
483        ):
484            raise TypeError(
485                "Field args must be GraphQLArguments or input type objects."
486            )
487        else:
488            args = {
489                name: value
490                if isinstance(value, GraphQLArgument)
491                else GraphQLArgument(cast(GraphQLInputType, value))
492                for name, value in args.items()
493            }
494        if resolve is not None and not callable(resolve):
495            raise TypeError(
496                "Field resolver must be a function if provided, "
497                f" but got: {inspect(resolve)}."
498            )
499        if description is not None and not is_description(description):
500            raise TypeError("The description must be a string.")
501        if deprecation_reason is not None and not is_description(deprecation_reason):
502            raise TypeError("The deprecation reason must be a string.")
503        if extensions is not None and (
504            not isinstance(extensions, dict)
505            or not all(isinstance(key, str) for key in extensions)
506        ):
507            raise TypeError("Field extensions must be a dictionary with string keys.")
508        if ast_node and not isinstance(ast_node, FieldDefinitionNode):
509            raise TypeError("Field AST node must be a FieldDefinitionNode.")
510        self.type = type_
511        self.args = args or {}
512        self.resolve = resolve
513        self.subscribe = subscribe
514        self.description = description
515        self.deprecation_reason = deprecation_reason
516        self.extensions = extensions
517        self.ast_node = ast_node
518
519    def __repr__(self) -> str:
520        return f"<{self.__class__.__name__} {self.type!r}>"
521
522    def __str__(self) -> str:
523        return f"Field: {self.type}"
524
525    def __eq__(self, other: Any) -> bool:
526        return self is other or (
527            isinstance(other, GraphQLField)
528            and self.type == other.type
529            and self.args == other.args
530            and self.resolve == other.resolve
531            and self.description == other.description
532            and self.deprecation_reason == other.deprecation_reason
533            and self.extensions == other.extensions
534        )
535
536    def to_kwargs(self) -> Dict[str, Any]:
537        return dict(
538            type_=self.type,
539            args=self.args.copy() if self.args else None,
540            resolve=self.resolve,
541            subscribe=self.subscribe,
542            deprecation_reason=self.deprecation_reason,
543            description=self.description,
544            extensions=self.extensions,
545            ast_node=self.ast_node,
546        )
547
548    def __copy__(self) -> "GraphQLField":  # pragma: no cover
549        return self.__class__(**self.to_kwargs())
550
551    @property
552    def is_deprecated(self) -> bool:
553        # this property is officially deprecated, but we still keep it here
554        return self.deprecation_reason is not None
555
556
557class GraphQLResolveInfo(NamedTuple):
558    """Collection of information passed to the resolvers.
559
560    This is always passed as the first argument to the resolvers.
561
562    Note that contrary to the JavaScript implementation, the context (commonly used to
563    represent an authenticated user, or request-specific caches) is included here and
564    not passed as an additional argument.
565    """
566
567    field_name: str
568    field_nodes: List[FieldNode]
569    return_type: "GraphQLOutputType"
570    parent_type: "GraphQLObjectType"
571    path: Path
572    schema: "GraphQLSchema"
573    fragments: Dict[str, FragmentDefinitionNode]
574    root_value: Any
575    operation: OperationDefinitionNode
576    variable_values: Dict[str, Any]
577    context: Any
578    is_awaitable: Callable[[Any], bool]
579
580
581# Note: Contrary to the Javascript implementation of GraphQLFieldResolver,
582# the context is passed as part of the GraphQLResolveInfo and any arguments
583# are passed individually as keyword arguments.
584GraphQLFieldResolverWithoutArgs = Callable[[Any, GraphQLResolveInfo], Any]
585# Unfortunately there is currently no syntax to indicate optional or keyword
586# arguments in Python, so we also allow any other Callable as a workaround:
587GraphQLFieldResolver = Callable[..., Any]
588
589# Note: Contrary to the Javascript implementation of GraphQLTypeResolver,
590# the context is passed as part of the GraphQLResolveInfo:
591# Note: returning GraphQLObjectType is deprecated and will be removed.
592GraphQLTypeResolver = Callable[
593    [Any, GraphQLResolveInfo, "GraphQLAbstractType"],
594    AwaitableOrValue[Optional[Union["GraphQLObjectType", str]]],
595]
596
597# Note: Contrary to the Javascript implementation of GraphQLIsTypeOfFn,
598# the context is passed as part of the GraphQLResolveInfo:
599GraphQLIsTypeOfFn = Callable[[Any, GraphQLResolveInfo], AwaitableOrValue[bool]]
600
601
602class GraphQLArgument:
603    """Definition of a GraphQL argument"""
604
605    type: "GraphQLInputType"
606    default_value: Any
607    description: Optional[str]
608    deprecation_reason: Optional[str]
609    out_name: Optional[str]  # for transforming names (extension of GraphQL.js)
610    extensions: Optional[Dict[str, Any]]
611    ast_node: Optional[InputValueDefinitionNode]
612
613    def __init__(
614        self,
615        type_: "GraphQLInputType",
616        default_value: Any = Undefined,
617        description: Optional[str] = None,
618        deprecation_reason: Optional[str] = None,
619        out_name: Optional[str] = None,
620        extensions: Optional[Dict[str, Any]] = None,
621        ast_node: Optional[InputValueDefinitionNode] = None,
622    ) -> None:
623        if not is_input_type(type_):
624            raise TypeError("Argument type must be a GraphQL input type.")
625        if description is not None and not is_description(description):
626            raise TypeError("Argument description must be a string.")
627        if deprecation_reason is not None and not is_description(deprecation_reason):
628            raise TypeError("Argument deprecation reason must be a string.")
629        if out_name is not None and not isinstance(out_name, str):
630            raise TypeError("Argument out name must be a string.")
631        if extensions is not None and (
632            not isinstance(extensions, dict)
633            or not all(isinstance(key, str) for key in extensions)
634        ):
635            raise TypeError(
636                "Argument extensions must be a dictionary with string keys."
637            )
638        if ast_node and not isinstance(ast_node, InputValueDefinitionNode):
639            raise TypeError("Argument AST node must be an InputValueDefinitionNode.")
640        self.type = type_
641        self.default_value = default_value
642        self.description = description
643        self.deprecation_reason = deprecation_reason
644        self.out_name = out_name
645        self.extensions = extensions
646        self.ast_node = ast_node
647
648    def __eq__(self, other: Any) -> bool:
649        return self is other or (
650            isinstance(other, GraphQLArgument)
651            and self.type == other.type
652            and self.default_value == other.default_value
653            and self.description == other.description
654            and self.deprecation_reason == other.deprecation_reason
655            and self.out_name == other.out_name
656            and self.extensions == other.extensions
657        )
658
659    def to_kwargs(self) -> Dict[str, Any]:
660        return dict(
661            type_=self.type,
662            default_value=self.default_value,
663            description=self.description,
664            deprecation_reason=self.deprecation_reason,
665            out_name=self.out_name,
666            extensions=self.extensions,
667            ast_node=self.ast_node,
668        )
669
670    def __copy__(self) -> "GraphQLArgument":  # pragma: no cover
671        return self.__class__(**self.to_kwargs())
672
673
674def is_required_argument(arg: GraphQLArgument) -> bool:
675    return is_non_null_type(arg.type) and arg.default_value is Undefined
676
677
678T = TypeVar("T")
679Thunk = Union[Callable[[], T], T]
680
681GraphQLFieldMap = Dict[str, GraphQLField]
682
683
684class GraphQLObjectType(GraphQLNamedType):
685    """Object Type Definition
686
687    Almost all of the GraphQL types you define will be object types. Object types have
688    a name, but most importantly describe their fields.
689
690    Example::
691
692        AddressType = GraphQLObjectType('Address', {
693            'street': GraphQLField(GraphQLString),
694            'number': GraphQLField(GraphQLInt),
695            'formatted': GraphQLField(GraphQLString,
696                lambda obj, info, **args: f'{obj.number} {obj.street}')
697        })
698
699    When two types need to refer to each other, or a type needs to refer to itself in
700    a field, you can use a lambda function with no arguments (a so-called "thunk")
701    to supply the fields lazily.
702
703    Example::
704
705        PersonType = GraphQLObjectType('Person', lambda: {
706            'name': GraphQLField(GraphQLString),
707            'bestFriend': GraphQLField(PersonType)
708        })
709
710    """
711
712    is_type_of: Optional[GraphQLIsTypeOfFn]
713    ast_node: Optional[ObjectTypeDefinitionNode]
714    extension_ast_nodes: Optional[FrozenList[ObjectTypeExtensionNode]]
715
716    def __init__(
717        self,
718        name: str,
719        fields: Thunk[GraphQLFieldMap],
720        interfaces: Optional[Thunk[Collection["GraphQLInterfaceType"]]] = None,
721        is_type_of: Optional[GraphQLIsTypeOfFn] = None,
722        extensions: Optional[Dict[str, Any]] = None,
723        description: Optional[str] = None,
724        ast_node: Optional[ObjectTypeDefinitionNode] = None,
725        extension_ast_nodes: Optional[Collection[ObjectTypeExtensionNode]] = None,
726    ) -> None:
727        super().__init__(
728            name=name,
729            description=description,
730            extensions=extensions,
731            ast_node=ast_node,
732            extension_ast_nodes=extension_ast_nodes,
733        )
734        if is_type_of is not None and not callable(is_type_of):
735            raise TypeError(
736                f"{name} must provide 'is_type_of' as a function,"
737                f" but got: {inspect(is_type_of)}."
738            )
739        if ast_node and not isinstance(ast_node, ObjectTypeDefinitionNode):
740            raise TypeError(f"{name} AST node must be an ObjectTypeDefinitionNode.")
741        if extension_ast_nodes and not all(
742            isinstance(node, ObjectTypeExtensionNode) for node in extension_ast_nodes
743        ):
744            raise TypeError(
745                f"{name} extension AST nodes must be specified"
746                " as a collection of ObjectTypeExtensionNode instances."
747            )
748        self._fields = fields
749        self._interfaces = interfaces
750        self.is_type_of = is_type_of
751
752    def to_kwargs(self) -> Dict[str, Any]:
753        return dict(
754            **super().to_kwargs(),
755            fields=self.fields.copy(),
756            interfaces=self.interfaces,
757            is_type_of=self.is_type_of,
758        )
759
760    def __copy__(self) -> "GraphQLObjectType":  # pragma: no cover
761        return self.__class__(**self.to_kwargs())
762
763    @cached_property
764    def fields(self) -> GraphQLFieldMap:
765        """Get provided fields, wrapping them as GraphQLFields if needed."""
766        try:
767            fields = resolve_thunk(self._fields)
768        except Exception as error:
769            raise TypeError(f"{self.name} fields cannot be resolved. {error}")
770        if not isinstance(fields, dict) or not all(
771            isinstance(key, str) for key in fields
772        ):
773            raise TypeError(
774                f"{self.name} fields must be specified"
775                " as a dict with field names as keys."
776            )
777        if not all(
778            isinstance(value, GraphQLField) or is_output_type(value)
779            for value in fields.values()
780        ):
781            raise TypeError(
782                f"{self.name} fields must be GraphQLField or output type objects."
783            )
784        return {
785            name: value if isinstance(value, GraphQLField) else GraphQLField(value)
786            for name, value in fields.items()
787        }
788
789    @cached_property
790    def interfaces(self) -> List["GraphQLInterfaceType"]:
791        """Get provided interfaces."""
792        try:
793            interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk(
794                self._interfaces
795            )
796        except Exception as error:
797            raise TypeError(f"{self.name} interfaces cannot be resolved. {error}")
798        if interfaces is None:
799            interfaces = []
800        elif not is_collection(interfaces) or not all(
801            isinstance(value, GraphQLInterfaceType) for value in interfaces
802        ):
803            raise TypeError(
804                f"{self.name} interfaces must be specified"
805                " as a collection of GraphQLInterfaceType instances."
806            )
807        return list(interfaces)
808
809
810def is_object_type(type_: Any) -> bool:
811    return isinstance(type_, GraphQLObjectType)
812
813
814def assert_object_type(type_: Any) -> GraphQLObjectType:
815    if not is_object_type(type_):
816        raise TypeError(f"Expected {type_} to be a GraphQL Object type.")
817    return cast(GraphQLObjectType, type_)
818
819
820class GraphQLInterfaceType(GraphQLNamedType):
821    """Interface Type Definition
822
823    When a field can return one of a heterogeneous set of types, an Interface type
824    is used to describe what types are possible, what fields are in common across
825    all types, as well as a function to determine which type is actually used when
826    the field is resolved.
827
828    Example::
829
830        EntityType = GraphQLInterfaceType('Entity', {
831                'name': GraphQLField(GraphQLString),
832            })
833    """
834
835    resolve_type: Optional[GraphQLTypeResolver]
836    ast_node: Optional[InterfaceTypeDefinitionNode]
837    extension_ast_nodes: Optional[FrozenList[InterfaceTypeExtensionNode]]
838
839    def __init__(
840        self,
841        name: str,
842        fields: Optional[Thunk[GraphQLFieldMap]] = None,
843        interfaces: Optional[Thunk[Collection["GraphQLInterfaceType"]]] = None,
844        resolve_type: Optional[GraphQLTypeResolver] = None,
845        description: Optional[str] = None,
846        extensions: Optional[Dict[str, Any]] = None,
847        ast_node: Optional[InterfaceTypeDefinitionNode] = None,
848        extension_ast_nodes: Optional[Collection[InterfaceTypeExtensionNode]] = None,
849    ) -> None:
850        super().__init__(
851            name=name,
852            description=description,
853            extensions=extensions,
854            ast_node=ast_node,
855            extension_ast_nodes=extension_ast_nodes,
856        )
857        if resolve_type is not None and not callable(resolve_type):
858            raise TypeError(
859                f"{name} must provide 'resolve_type' as a function,"
860                f" but got: {inspect(resolve_type)}."
861            )
862        if ast_node and not isinstance(ast_node, InterfaceTypeDefinitionNode):
863            raise TypeError(f"{name} AST node must be an InterfaceTypeDefinitionNode.")
864        if extension_ast_nodes and not all(
865            isinstance(node, InterfaceTypeExtensionNode) for node in extension_ast_nodes
866        ):
867            raise TypeError(
868                f"{name} extension AST nodes must be specified"
869                " as a collection of InterfaceTypeExtensionNode instances."
870            )
871        self._fields = fields
872        self._interfaces = interfaces
873        self.resolve_type = resolve_type
874
875    def to_kwargs(self) -> Dict[str, Any]:
876        return dict(
877            **super().to_kwargs(),
878            fields=self.fields.copy(),
879            interfaces=self.interfaces.copy(),
880            resolve_type=self.resolve_type,
881        )
882
883    def __copy__(self) -> "GraphQLInterfaceType":  # pragma: no cover
884        return self.__class__(**self.to_kwargs())
885
886    @cached_property
887    def fields(self) -> GraphQLFieldMap:
888        """Get provided fields, wrapping them as GraphQLFields if needed."""
889        try:
890            fields = resolve_thunk(self._fields)
891        except Exception as error:
892            raise TypeError(f"{self.name} fields cannot be resolved. {error}")
893        if not isinstance(fields, dict) or not all(
894            isinstance(key, str) for key in fields
895        ):
896            raise TypeError(
897                f"{self.name} fields must be specified"
898                " as a dict with field names as keys."
899            )
900        if not all(
901            isinstance(value, GraphQLField) or is_output_type(value)
902            for value in fields.values()
903        ):
904            raise TypeError(
905                f"{self.name} fields must be GraphQLField or output type objects."
906            )
907        return {
908            name: value if isinstance(value, GraphQLField) else GraphQLField(value)
909            for name, value in fields.items()
910        }
911
912    @cached_property
913    def interfaces(self) -> List["GraphQLInterfaceType"]:
914        """Get provided interfaces."""
915        try:
916            interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk(
917                self._interfaces
918            )
919        except Exception as error:
920            raise TypeError(f"{self.name} interfaces cannot be resolved. {error}")
921        if interfaces is None:
922            interfaces = []
923        elif not is_collection(interfaces) or not all(
924            isinstance(value, GraphQLInterfaceType) for value in interfaces
925        ):
926            raise TypeError(
927                f"{self.name} interfaces must be specified"
928                " as a collection of GraphQLInterfaceType instances."
929            )
930        return list(interfaces)
931
932
933def is_interface_type(type_: Any) -> bool:
934    return isinstance(type_, GraphQLInterfaceType)
935
936
937def assert_interface_type(type_: Any) -> GraphQLInterfaceType:
938    if not is_interface_type(type_):
939        raise TypeError(f"Expected {type_} to be a GraphQL Interface type.")
940    return cast(GraphQLInterfaceType, type_)
941
942
943class GraphQLUnionType(GraphQLNamedType):
944    """Union Type Definition
945
946    When a field can return one of a heterogeneous set of types, a Union type is used
947    to describe what types are possible as well as providing a function to determine
948    which type is actually used when the field is resolved.
949
950    Example::
951
952        def resolve_type(obj, _info, _type):
953            if isinstance(obj, Dog):
954                return DogType()
955            if isinstance(obj, Cat):
956                return CatType()
957
958        PetType = GraphQLUnionType('Pet', [DogType, CatType], resolve_type)
959    """
960
961    resolve_type: Optional[GraphQLTypeResolver]
962    ast_node: Optional[UnionTypeDefinitionNode]
963    extension_ast_nodes: Optional[FrozenList[UnionTypeExtensionNode]]
964
965    def __init__(
966        self,
967        name: str,
968        types: Thunk[Collection[GraphQLObjectType]],
969        resolve_type: Optional[GraphQLTypeResolver] = None,
970        description: Optional[str] = None,
971        extensions: Optional[Dict[str, Any]] = None,
972        ast_node: Optional[UnionTypeDefinitionNode] = None,
973        extension_ast_nodes: Optional[Collection[UnionTypeExtensionNode]] = None,
974    ) -> None:
975        super().__init__(
976            name=name,
977            description=description,
978            extensions=extensions,
979            ast_node=ast_node,
980            extension_ast_nodes=extension_ast_nodes,
981        )
982        if resolve_type is not None and not callable(resolve_type):
983            raise TypeError(
984                f"{name} must provide 'resolve_type' as a function,"
985                f" but got: {inspect(resolve_type)}."
986            )
987        if ast_node and not isinstance(ast_node, UnionTypeDefinitionNode):
988            raise TypeError(f"{name} AST node must be a UnionTypeDefinitionNode.")
989        if extension_ast_nodes and not all(
990            isinstance(node, UnionTypeExtensionNode) for node in extension_ast_nodes
991        ):
992            raise TypeError(
993                f"{name} extension AST nodes must be specified"
994                " as a collection of UnionTypeExtensionNode instances."
995            )
996        self._types = types
997        self.resolve_type = resolve_type
998
999    def to_kwargs(self) -> Dict[str, Any]:
1000        return dict(
1001            **super().to_kwargs(), types=self.types, resolve_type=self.resolve_type
1002        )
1003
1004    def __copy__(self) -> "GraphQLUnionType":  # pragma: no cover
1005        return self.__class__(**self.to_kwargs())
1006
1007    @cached_property
1008    def types(self) -> List[GraphQLObjectType]:
1009        """Get provided types."""
1010        try:
1011            types: Collection[GraphQLObjectType] = resolve_thunk(self._types)
1012        except Exception as error:
1013            raise TypeError(f"{self.name} types cannot be resolved. {error}")
1014        if types is None:
1015            types = []
1016        elif not is_collection(types) or not all(
1017            isinstance(value, GraphQLObjectType) for value in types
1018        ):
1019            raise TypeError(
1020                f"{self.name} types must be specified"
1021                " as a collection of GraphQLObjectType instances."
1022            )
1023        return list(types)
1024
1025
1026def is_union_type(type_: Any) -> bool:
1027    return isinstance(type_, GraphQLUnionType)
1028
1029
1030def assert_union_type(type_: Any) -> GraphQLUnionType:
1031    if not is_union_type(type_):
1032        raise TypeError(f"Expected {type_} to be a GraphQL Union type.")
1033    return cast(GraphQLUnionType, type_)
1034
1035
1036GraphQLEnumValueMap = Dict[str, "GraphQLEnumValue"]
1037
1038
1039class GraphQLEnumType(GraphQLNamedType):
1040    """Enum Type Definition
1041
1042    Some leaf values of requests and input values are Enums. GraphQL serializes Enum
1043    values as strings, however internally Enums can be represented by any kind of type,
1044    often integers. They can also be provided as a Python Enum.
1045
1046    Example::
1047
1048        RGBType = GraphQLEnumType('RGB', {
1049            'RED': 0,
1050            'GREEN': 1,
1051            'BLUE': 2
1052        })
1053
1054    Example using a Python Enum::
1055
1056        class RGBEnum(enum.Enum):
1057            RED = 0
1058            GREEN = 1
1059            BLUE = 2
1060
1061        RGBType = GraphQLEnumType('RGB', enum.Enum)
1062
1063    Instead of raw values, you can also specify GraphQLEnumValue objects with more
1064    detail like description or deprecation information.
1065
1066    Note: If a value is not provided in a definition, the name of the enum value will
1067    be used as its internal value when the value is serialized.
1068    """
1069
1070    values: GraphQLEnumValueMap
1071    ast_node: Optional[EnumTypeDefinitionNode]
1072    extension_ast_nodes: Optional[FrozenList[EnumTypeExtensionNode]]
1073
1074    def __init__(
1075        self,
1076        name: str,
1077        values: Union[GraphQLEnumValueMap, Dict[str, Any], Type[Enum]],
1078        description: Optional[str] = None,
1079        extensions: Optional[Dict[str, Any]] = None,
1080        ast_node: Optional[EnumTypeDefinitionNode] = None,
1081        extension_ast_nodes: Optional[Collection[EnumTypeExtensionNode]] = None,
1082    ) -> None:
1083        super().__init__(
1084            name=name,
1085            description=description,
1086            extensions=extensions,
1087            ast_node=ast_node,
1088            extension_ast_nodes=extension_ast_nodes,
1089        )
1090        try:  # check for enum
1091            values = cast(Enum, values).__members__  # type: ignore
1092        except AttributeError:
1093            if not isinstance(values, dict) or not all(
1094                isinstance(name, str) for name in values
1095            ):
1096                try:
1097                    # noinspection PyTypeChecker
1098                    values = dict(values)  # type: ignore
1099                except (TypeError, ValueError):
1100                    raise TypeError(
1101                        f"{name} values must be an Enum or a dict"
1102                        " with value names as keys."
1103                    )
1104            values = cast(Dict, values)
1105        else:
1106            values = cast(Dict, values)
1107            values = {key: value.value for key, value in values.items()}
1108        values = {
1109            key: value
1110            if isinstance(value, GraphQLEnumValue)
1111            else GraphQLEnumValue(value)
1112            for key, value in values.items()
1113        }
1114        if ast_node and not isinstance(ast_node, EnumTypeDefinitionNode):
1115            raise TypeError(f"{name} AST node must be an EnumTypeDefinitionNode.")
1116        if extension_ast_nodes and not all(
1117            isinstance(node, EnumTypeExtensionNode) for node in extension_ast_nodes
1118        ):
1119            raise TypeError(
1120                f"{name} extension AST nodes must be specified"
1121                " as a collection of EnumTypeExtensionNode instances."
1122            )
1123        self.values = values
1124
1125    def to_kwargs(self) -> Dict[str, Any]:
1126        return dict(**super().to_kwargs(), values=self.values.copy())
1127
1128    def __copy__(self) -> "GraphQLEnumType":  # pragma: no cover
1129        return self.__class__(**self.to_kwargs())
1130
1131    @cached_property
1132    def _value_lookup(self) -> Dict[Any, str]:
1133        # use first value or name as lookup
1134        lookup: Dict[Any, str] = {}
1135        for name, enum_value in self.values.items():
1136            value = enum_value.value
1137            if value is None or value is Undefined:
1138                value = name
1139            try:
1140                if value not in lookup:
1141                    lookup[value] = name
1142            except TypeError:
1143                pass  # ignore unhashable values
1144        return lookup
1145
1146    def serialize(self, output_value: Any) -> str:
1147        try:
1148            return self._value_lookup[output_value]
1149        except KeyError:  # hashable value not found
1150            pass
1151        except TypeError:  # unhashable value, we need to scan all values
1152            for enum_name, enum_value in self.values.items():
1153                if enum_value.value == output_value:
1154                    return enum_name
1155        raise GraphQLError(
1156            f"Enum '{self.name}' cannot represent value: {inspect(output_value)}"
1157        )
1158
1159    def parse_value(self, input_value: str) -> Any:
1160        if isinstance(input_value, str):
1161            try:
1162                enum_value = self.values[input_value]
1163            except KeyError:
1164                raise GraphQLError(
1165                    f"Value '{input_value}' does not exist in '{self.name}' enum."
1166                    + did_you_mean_enum_value(self, input_value)
1167                )
1168            return enum_value.value
1169        value_str = inspect(input_value)
1170        raise GraphQLError(
1171            f"Enum '{self.name}' cannot represent non-string value: {value_str}."
1172            + did_you_mean_enum_value(self, value_str)
1173        )
1174
1175    def parse_literal(
1176        self, value_node: ValueNode, _variables: Optional[Dict[str, Any]] = None
1177    ) -> Any:
1178        # Note: variables will be resolved before calling this method.
1179        if isinstance(value_node, EnumValueNode):
1180            try:
1181                enum_value = self.values[value_node.value]
1182            except KeyError:
1183                value_str = print_ast(value_node)
1184                raise GraphQLError(
1185                    f"Value '{value_str}' does not exist in '{self.name}' enum."
1186                    + did_you_mean_enum_value(self, value_str),
1187                    value_node,
1188                )
1189            return enum_value.value
1190        value_str = print_ast(value_node)
1191        raise GraphQLError(
1192            f"Enum '{self.name}' cannot represent non-enum value: {value_str}."
1193            + did_you_mean_enum_value(self, value_str),
1194            value_node,
1195        )
1196
1197
1198def is_enum_type(type_: Any) -> bool:
1199    return isinstance(type_, GraphQLEnumType)
1200
1201
1202def assert_enum_type(type_: Any) -> GraphQLEnumType:
1203    if not is_enum_type(type_):
1204        raise TypeError(f"Expected {type_} to be a GraphQL Enum type.")
1205    return cast(GraphQLEnumType, type_)
1206
1207
1208def did_you_mean_enum_value(enum_type: GraphQLEnumType, unknown_value_str: str) -> str:
1209    suggested_values = suggestion_list(unknown_value_str, enum_type.values)
1210    return did_you_mean(suggested_values, "the enum value")
1211
1212
1213class GraphQLEnumValue:
1214
1215    value: Any
1216    description: Optional[str]
1217    deprecation_reason: Optional[str]
1218    extensions: Optional[Dict[str, Any]]
1219    ast_node: Optional[EnumValueDefinitionNode]
1220
1221    def __init__(
1222        self,
1223        value: Any = None,
1224        description: Optional[str] = None,
1225        deprecation_reason: Optional[str] = None,
1226        extensions: Optional[Dict[str, Any]] = None,
1227        ast_node: Optional[EnumValueDefinitionNode] = None,
1228    ) -> None:
1229        if description is not None and not is_description(description):
1230            raise TypeError("The description of the enum value must be a string.")
1231        if deprecation_reason is not None and not is_description(deprecation_reason):
1232            raise TypeError(
1233                "The deprecation reason for the enum value must be a string."
1234            )
1235        if extensions is not None and (
1236            not isinstance(extensions, dict)
1237            or not all(isinstance(key, str) for key in extensions)
1238        ):
1239            raise TypeError(
1240                "Enum value extensions must be a dictionary with string keys."
1241            )
1242        if ast_node and not isinstance(ast_node, EnumValueDefinitionNode):
1243            raise TypeError("AST node must be an EnumValueDefinitionNode.")
1244        self.value = value
1245        self.description = description
1246        self.deprecation_reason = deprecation_reason
1247        self.extensions = extensions
1248        self.ast_node = ast_node
1249
1250    def __eq__(self, other: Any) -> bool:
1251        return self is other or (
1252            isinstance(other, GraphQLEnumValue)
1253            and self.value == other.value
1254            and self.description == other.description
1255            and self.deprecation_reason == other.deprecation_reason
1256            and self.extensions == other.extensions
1257        )
1258
1259    def to_kwargs(self) -> Dict[str, Any]:
1260        return dict(
1261            value=self.value,
1262            description=self.description,
1263            deprecation_reason=self.deprecation_reason,
1264            extensions=self.extensions,
1265            ast_node=self.ast_node,
1266        )
1267
1268    def __copy__(self) -> "GraphQLEnumValue":  # pragma: no cover
1269        return self.__class__(**self.to_kwargs())
1270
1271    @property
1272    def is_deprecated(self) -> bool:
1273        # this property is officially deprecated, but we still keep it here
1274        return self.deprecation_reason is not None
1275
1276
1277GraphQLInputFieldMap = Dict[str, "GraphQLInputField"]
1278GraphQLInputFieldOutType = Callable[[Dict[str, Any]], Any]
1279
1280
1281class GraphQLInputObjectType(GraphQLNamedType):
1282    """Input Object Type Definition
1283
1284    An input object defines a structured collection of fields which may be supplied
1285    to a field argument.
1286
1287    Using ``NonNull`` will ensure that a value must be provided by the query.
1288
1289    Example::
1290
1291        NonNullFloat = GraphQLNonNull(GraphQLFloat())
1292
1293        class GeoPoint(GraphQLInputObjectType):
1294            name = 'GeoPoint'
1295            fields = {
1296                'lat': GraphQLInputField(NonNullFloat),
1297                'lon': GraphQLInputField(NonNullFloat),
1298                'alt': GraphQLInputField(
1299                          GraphQLFloat(), default_value=0)
1300            }
1301
1302    The outbound values will be Python dictionaries by default, but you can have them
1303    converted to other types by specifying an ``out_type`` function or class.
1304    """
1305
1306    ast_node: Optional[InputObjectTypeDefinitionNode]
1307    extension_ast_nodes: Optional[FrozenList[InputObjectTypeExtensionNode]]
1308
1309    def __init__(
1310        self,
1311        name: str,
1312        fields: Thunk[GraphQLInputFieldMap],
1313        description: Optional[str] = None,
1314        out_type: Optional[GraphQLInputFieldOutType] = None,
1315        extensions: Optional[Dict[str, Any]] = None,
1316        ast_node: Optional[InputObjectTypeDefinitionNode] = None,
1317        extension_ast_nodes: Optional[Collection[InputObjectTypeExtensionNode]] = None,
1318    ) -> None:
1319        super().__init__(
1320            name=name,
1321            description=description,
1322            extensions=extensions,
1323            ast_node=ast_node,
1324            extension_ast_nodes=extension_ast_nodes,
1325        )
1326        if out_type is not None and not callable(out_type):
1327            raise TypeError(f"The out type for {name} must be a function or a class.")
1328        if ast_node and not isinstance(ast_node, InputObjectTypeDefinitionNode):
1329            raise TypeError(
1330                f"{name} AST node must be an InputObjectTypeDefinitionNode."
1331            )
1332        if extension_ast_nodes and not all(
1333            isinstance(node, InputObjectTypeExtensionNode)
1334            for node in extension_ast_nodes
1335        ):
1336            raise TypeError(
1337                f"{name} extension AST nodes must be specified"
1338                " as a collection of InputObjectTypeExtensionNode instances."
1339            )
1340        self._fields = fields
1341        if out_type is not None:
1342            self.out_type = out_type  # type: ignore
1343
1344    @staticmethod
1345    def out_type(value: Dict[str, Any]) -> Any:
1346        """Transform outbound values (this is an extension of GraphQL.js).
1347
1348        This default implementation passes values unaltered as dictionaries.
1349        """
1350        return value
1351
1352    def to_kwargs(self) -> Dict[str, Any]:
1353        return dict(
1354            **super().to_kwargs(),
1355            fields=self.fields.copy(),
1356            out_type=None
1357            if self.out_type is GraphQLInputObjectType.out_type
1358            else self.out_type,
1359        )
1360
1361    def __copy__(self) -> "GraphQLInputObjectType":  # pragma: no cover
1362        return self.__class__(**self.to_kwargs())
1363
1364    @cached_property
1365    def fields(self) -> GraphQLInputFieldMap:
1366        """Get provided fields, wrap them as GraphQLInputField if needed."""
1367        try:
1368            fields = resolve_thunk(self._fields)
1369        except Exception as error:
1370            raise TypeError(f"{self.name} fields cannot be resolved. {error}")
1371        if not isinstance(fields, dict) or not all(
1372            isinstance(key, str) for key in fields
1373        ):
1374            raise TypeError(
1375                f"{self.name} fields must be specified"
1376                " as a dict with field names as keys."
1377            )
1378        if not all(
1379            isinstance(value, GraphQLInputField) or is_input_type(value)
1380            for value in fields.values()
1381        ):
1382            raise TypeError(
1383                f"{self.name} fields must be"
1384                " GraphQLInputField or input type objects."
1385            )
1386        return {
1387            name: value
1388            if isinstance(value, GraphQLInputField)
1389            else GraphQLInputField(value)
1390            for name, value in fields.items()
1391        }
1392
1393
1394def is_input_object_type(type_: Any) -> bool:
1395    return isinstance(type_, GraphQLInputObjectType)
1396
1397
1398def assert_input_object_type(type_: Any) -> GraphQLInputObjectType:
1399    if not is_input_object_type(type_):
1400        raise TypeError(f"Expected {type_} to be a GraphQL Input Object type.")
1401    return cast(GraphQLInputObjectType, type_)
1402
1403
1404class GraphQLInputField:
1405    """Definition of a GraphQL input field"""
1406
1407    type: "GraphQLInputType"
1408    default_value: Any
1409    description: Optional[str]
1410    deprecation_reason: Optional[str]
1411    out_name: Optional[str]  # for transforming names (extension of GraphQL.js)
1412    extensions: Optional[Dict[str, Any]]
1413    ast_node: Optional[InputValueDefinitionNode]
1414
1415    def __init__(
1416        self,
1417        type_: "GraphQLInputType",
1418        default_value: Any = Undefined,
1419        description: Optional[str] = None,
1420        deprecation_reason: Optional[str] = None,
1421        out_name: Optional[str] = None,
1422        extensions: Optional[Dict[str, Any]] = None,
1423        ast_node: Optional[InputValueDefinitionNode] = None,
1424    ) -> None:
1425        if not is_input_type(type_):
1426            raise TypeError("Input field type must be a GraphQL input type.")
1427        if description is not None and not is_description(description):
1428            raise TypeError("Input field description must be a string.")
1429        if deprecation_reason is not None and not is_description(deprecation_reason):
1430            raise TypeError("Input field deprecation reason must be a string.")
1431        if out_name is not None and not isinstance(out_name, str):
1432            raise TypeError("Input field out name must be a string.")
1433        if extensions is not None and (
1434            not isinstance(extensions, dict)
1435            or not all(isinstance(key, str) for key in extensions)
1436        ):
1437            raise TypeError(
1438                "Input field extensions must be a dictionary with string keys."
1439            )
1440        if ast_node and not isinstance(ast_node, InputValueDefinitionNode):
1441            raise TypeError("Input field AST node must be an InputValueDefinitionNode.")
1442        self.type = type_
1443        self.default_value = default_value
1444        self.description = description
1445        self.deprecation_reason = deprecation_reason
1446        self.out_name = out_name
1447        self.extensions = extensions
1448        self.ast_node = ast_node
1449
1450    def __eq__(self, other: Any) -> bool:
1451        return self is other or (
1452            isinstance(other, GraphQLInputField)
1453            and self.type == other.type
1454            and self.default_value == other.default_value
1455            and self.description == other.description
1456            and self.deprecation_reason == other.deprecation_reason
1457            and self.extensions == other.extensions
1458            and self.out_name == other.out_name
1459        )
1460
1461    def to_kwargs(self) -> Dict[str, Any]:
1462        return dict(
1463            type_=self.type,
1464            default_value=self.default_value,
1465            description=self.description,
1466            deprecation_reason=self.deprecation_reason,
1467            out_name=self.out_name,
1468            extensions=self.extensions,
1469            ast_node=self.ast_node,
1470        )
1471
1472    def __copy__(self) -> "GraphQLInputField":  # pragma: no cover
1473        return self.__class__(**self.to_kwargs())
1474
1475
1476def is_required_input_field(field: GraphQLInputField) -> bool:
1477    return is_non_null_type(field.type) and field.default_value is Undefined
1478
1479
1480# Wrapper types
1481
1482
1483class GraphQLList(Generic[GT], GraphQLWrappingType[GT]):
1484    """List Type Wrapper
1485
1486    A list is a wrapping type which points to another type. Lists are often created
1487    within the context of defining the fields of an object type.
1488
1489    Example::
1490
1491        class PersonType(GraphQLObjectType):
1492            name = 'Person'
1493
1494            @property
1495            def fields(self):
1496                return {
1497                    'parents': GraphQLField(GraphQLList(PersonType())),
1498                    'children': GraphQLField(GraphQLList(PersonType())),
1499                }
1500    """
1501
1502    def __init__(self, type_: GT) -> None:
1503        super().__init__(type_=type_)
1504
1505    def __str__(self) -> str:
1506        return f"[{self.of_type}]"
1507
1508
1509def is_list_type(type_: Any) -> bool:
1510    return isinstance(type_, GraphQLList)
1511
1512
1513def assert_list_type(type_: Any) -> GraphQLList:
1514    if not is_list_type(type_):
1515        raise TypeError(f"Expected {type_} to be a GraphQL List type.")
1516    return cast(GraphQLList, type_)
1517
1518
1519GNT = TypeVar("GNT", bound="GraphQLNullableType")
1520
1521
1522class GraphQLNonNull(GraphQLWrappingType[GNT], Generic[GNT]):
1523    """Non-Null Type Wrapper
1524
1525    A non-null is a wrapping type which points to another type. Non-null types enforce
1526    that their values are never null and can ensure an error is raised if this ever
1527    occurs during a request. It is useful for fields which you can make a strong
1528    guarantee on non-nullability, for example usually the id field of a database row
1529    will never be null.
1530
1531    Example::
1532
1533        class RowType(GraphQLObjectType):
1534            name = 'Row'
1535            fields = {
1536                'id': GraphQLField(GraphQLNonNull(GraphQLString()))
1537            }
1538
1539    Note: the enforcement of non-nullability occurs within the executor.
1540    """
1541
1542    def __init__(self, type_: GNT):
1543        super().__init__(type_=type_)
1544        if isinstance(type_, GraphQLNonNull):
1545            raise TypeError(
1546                "Can only create NonNull of a Nullable GraphQLType but got:"
1547                f" {type_}."
1548            )
1549
1550    def __str__(self) -> str:
1551        return f"{self.of_type}!"
1552
1553
1554def is_non_null_type(type_: Any) -> bool:
1555    return isinstance(type_, GraphQLNonNull)
1556
1557
1558def assert_non_null_type(type_: Any) -> GraphQLNonNull:
1559    if not is_non_null_type(type_):
1560        raise TypeError(f"Expected {type_} to be a GraphQL Non-Null type.")
1561    return cast(GraphQLNonNull, type_)
1562
1563
1564# These types can all accept null as a value.
1565
1566graphql_nullable_types = (
1567    GraphQLScalarType,
1568    GraphQLObjectType,
1569    GraphQLInterfaceType,
1570    GraphQLUnionType,
1571    GraphQLEnumType,
1572    GraphQLInputObjectType,
1573    GraphQLList,
1574)
1575
1576GraphQLNullableType = Union[
1577    GraphQLScalarType,
1578    GraphQLObjectType,
1579    GraphQLInterfaceType,
1580    GraphQLUnionType,
1581    GraphQLEnumType,
1582    GraphQLInputObjectType,
1583    GraphQLList,
1584]
1585
1586
1587def is_nullable_type(type_: Any) -> bool:
1588    return isinstance(type_, graphql_nullable_types)
1589
1590
1591def assert_nullable_type(type_: Any) -> GraphQLNullableType:
1592    if not is_nullable_type(type_):
1593        raise TypeError(f"Expected {type_} to be a GraphQL nullable type.")
1594    return cast(GraphQLNullableType, type_)
1595
1596
1597@overload
1598def get_nullable_type(type_: None) -> None:
1599    ...
1600
1601
1602@overload
1603def get_nullable_type(type_: GraphQLNullableType) -> GraphQLNullableType:
1604    ...
1605
1606
1607@overload
1608def get_nullable_type(type_: GraphQLNonNull) -> GraphQLNullableType:
1609    ...
1610
1611
1612def get_nullable_type(
1613    type_: Optional[Union[GraphQLNullableType, GraphQLNonNull]]
1614) -> Optional[GraphQLNullableType]:
1615    """Unwrap possible non-null type"""
1616    if is_non_null_type(type_):
1617        type_ = cast(GraphQLNonNull, type_)
1618        type_ = type_.of_type
1619    return cast(Optional[GraphQLNullableType], type_)
1620
1621
1622# These types may be used as input types for arguments and directives.
1623
1624graphql_input_types = (GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType)
1625
1626GraphQLInputType = Union[
1627    GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType, GraphQLWrappingType
1628]
1629
1630
1631def is_input_type(type_: Any) -> bool:
1632    return isinstance(type_, graphql_input_types) or (
1633        isinstance(type_, GraphQLWrappingType) and is_input_type(type_.of_type)
1634    )
1635
1636
1637def assert_input_type(type_: Any) -> GraphQLInputType:
1638    if not is_input_type(type_):
1639        raise TypeError(f"Expected {type_} to be a GraphQL input type.")
1640    return cast(GraphQLInputType, type_)
1641
1642
1643# These types may be used as output types as the result of fields.
1644
1645graphql_output_types = (
1646    GraphQLScalarType,
1647    GraphQLObjectType,
1648    GraphQLInterfaceType,
1649    GraphQLUnionType,
1650    GraphQLEnumType,
1651)
1652
1653GraphQLOutputType = Union[
1654    GraphQLScalarType,
1655    GraphQLObjectType,
1656    GraphQLInterfaceType,
1657    GraphQLUnionType,
1658    GraphQLEnumType,
1659    GraphQLWrappingType,
1660]
1661
1662
1663def is_output_type(type_: Any) -> bool:
1664    return isinstance(type_, graphql_output_types) or (
1665        isinstance(type_, GraphQLWrappingType) and is_output_type(type_.of_type)
1666    )
1667
1668
1669def assert_output_type(type_: Any) -> GraphQLOutputType:
1670    if not is_output_type(type_):
1671        raise TypeError(f"Expected {type_} to be a GraphQL output type.")
1672    return cast(GraphQLOutputType, type_)
1673
1674
1675# These types may describe types which may be leaf values.
1676
1677graphql_leaf_types = (GraphQLScalarType, GraphQLEnumType)
1678
1679GraphQLLeafType = Union[GraphQLScalarType, GraphQLEnumType]
1680
1681
1682def is_leaf_type(type_: Any) -> bool:
1683    return isinstance(type_, graphql_leaf_types)
1684
1685
1686def assert_leaf_type(type_: Any) -> GraphQLLeafType:
1687    if not is_leaf_type(type_):
1688        raise TypeError(f"Expected {type_} to be a GraphQL leaf type.")
1689    return cast(GraphQLLeafType, type_)
1690
1691
1692# These types may describe the parent context of a selection set.
1693
1694graphql_composite_types = (GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType)
1695
1696GraphQLCompositeType = Union[GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType]
1697
1698
1699def is_composite_type(type_: Any) -> bool:
1700    return isinstance(type_, graphql_composite_types)
1701
1702
1703def assert_composite_type(type_: Any) -> GraphQLType:
1704    if not is_composite_type(type_):
1705        raise TypeError(f"Expected {type_} to be a GraphQL composite type.")
1706    return cast(GraphQLType, type_)
1707
1708
1709# These types may describe abstract types.
1710
1711graphql_abstract_types = (GraphQLInterfaceType, GraphQLUnionType)
1712
1713GraphQLAbstractType = Union[GraphQLInterfaceType, GraphQLUnionType]
1714
1715
1716def is_abstract_type(type_: Any) -> bool:
1717    return isinstance(type_, graphql_abstract_types)
1718
1719
1720def assert_abstract_type(type_: Any) -> GraphQLAbstractType:
1721    if not is_abstract_type(type_):
1722        raise TypeError(f"Expected {type_} to be a GraphQL composite type.")
1723    return cast(GraphQLAbstractType, type_)
1724