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