|
| 1 | +from copy import copy, deepcopy |
1 | 2 | from typing import (
|
2 | 3 | Any,
|
3 | 4 | Collection,
|
|
9 | 10 | Union,
|
10 | 11 | cast,
|
11 | 12 | )
|
12 |
| -from warnings import warn |
13 | 13 |
|
14 | 14 | from ..error import GraphQLError
|
15 | 15 | from ..language import ast
|
|
22 | 22 | GraphQLObjectType,
|
23 | 23 | GraphQLUnionType,
|
24 | 24 | GraphQLType,
|
| 25 | + GraphQLWrappingType, |
25 | 26 | get_named_type,
|
26 | 27 | is_input_object_type,
|
27 | 28 | is_interface_type,
|
28 | 29 | is_object_type,
|
29 | 30 | is_union_type,
|
| 31 | + is_wrapping_type, |
30 | 32 | )
|
31 | 33 | from .directives import GraphQLDirective, specified_directives, is_directive
|
32 | 34 | from .introspection import introspection_types
|
@@ -289,9 +291,41 @@ def to_kwargs(self) -> Dict[str, Any]:
|
289 | 291 | def __copy__(self) -> "GraphQLSchema": # pragma: no cover
|
290 | 292 | return self.__class__(**self.to_kwargs())
|
291 | 293 |
|
292 |
| - def __deepcopy__(self, memo_: Dict) -> "GraphQLSchema": # pragma: no cover |
293 |
| - warn("Cannot deep copy a schema. Creating a flat copy instead.") |
294 |
| - return self.__copy__() |
| 294 | + def __deepcopy__(self, memo: Dict) -> "GraphQLSchema": |
| 295 | + from ..type import ( |
| 296 | + is_introspection_type, |
| 297 | + is_specified_scalar_type, |
| 298 | + is_specified_directive, |
| 299 | + ) |
| 300 | + |
| 301 | + type_map = { |
| 302 | + name: copy(type_) |
| 303 | + for name, type_ in self.type_map.items() |
| 304 | + if not is_introspection_type(type_) and not is_specified_scalar_type(type_) |
| 305 | + } |
| 306 | + remapped: Set[str] = set() |
| 307 | + types = [ |
| 308 | + cast(GraphQLNamedType, remap_type(type_, type_map, remapped)) |
| 309 | + for type_ in type_map.values() |
| 310 | + ] |
| 311 | + directives = [ |
| 312 | + directive if is_specified_directive(directive) else copy(directive) |
| 313 | + for directive in self.directives |
| 314 | + ] |
| 315 | + return self.__class__( |
| 316 | + self.query_type and cast(GraphQLObjectType, type_map[self.query_type.name]), |
| 317 | + self.mutation_type |
| 318 | + and cast(GraphQLObjectType, type_map[self.mutation_type.name]), |
| 319 | + self.subscription_type |
| 320 | + and cast(GraphQLObjectType, type_map[self.subscription_type.name]), |
| 321 | + types, |
| 322 | + directives, |
| 323 | + self.description, |
| 324 | + extensions=deepcopy(self.extensions, memo), |
| 325 | + ast_node=deepcopy(self.ast_node, memo), |
| 326 | + extension_ast_nodes=deepcopy(self.extension_ast_nodes, memo), |
| 327 | + assume_valid=True, |
| 328 | + ) |
295 | 329 |
|
296 | 330 | def get_type(self, name: str) -> Optional[GraphQLNamedType]:
|
297 | 331 | return self.type_map.get(name)
|
@@ -406,3 +440,52 @@ def assert_schema(schema: Any) -> GraphQLSchema:
|
406 | 440 | if not is_schema(schema):
|
407 | 441 | raise TypeError(f"Expected {inspect(schema)} to be a GraphQL schema.")
|
408 | 442 | return cast(GraphQLSchema, schema)
|
| 443 | + |
| 444 | + |
| 445 | +def remap_type( |
| 446 | + type_: GraphQLType, type_map: TypeMap, remapped: Set[str] |
| 447 | +) -> GraphQLType: |
| 448 | + """Change all references in the given type for a new type map.""" |
| 449 | + if is_wrapping_type(type_): |
| 450 | + wrapping_type = cast(GraphQLWrappingType, type_) |
| 451 | + return wrapping_type.__class__( |
| 452 | + remap_type(wrapping_type.of_type, type_map, remapped) |
| 453 | + ) |
| 454 | + named_type = cast(GraphQLNamedType, type_) |
| 455 | + name = named_type.name |
| 456 | + remapped_type = type_map.get(name) |
| 457 | + if not remapped_type: |
| 458 | + return named_type |
| 459 | + if name in remapped: |
| 460 | + return remapped_type |
| 461 | + remapped.add(name) |
| 462 | + if is_union_type(remapped_type): |
| 463 | + named_type = cast(GraphQLUnionType, named_type) |
| 464 | + named_type.types = [ |
| 465 | + remap_type(member_type, type_map, remapped) |
| 466 | + for member_type in named_type.types |
| 467 | + ] |
| 468 | + elif is_object_type(remapped_type) or is_interface_type(remapped_type): |
| 469 | + named_type = cast(Union[GraphQLObjectType, GraphQLInterfaceType], named_type) |
| 470 | + named_type.interfaces = [ |
| 471 | + remap_type(interface_type, type_map, remapped) |
| 472 | + for interface_type in named_type.interfaces |
| 473 | + ] |
| 474 | + fields = named_type.fields |
| 475 | + for field_name, field in fields.items(): |
| 476 | + field = copy(field) |
| 477 | + field.type = remap_type(field.type, type_map, remapped) |
| 478 | + args = field.args |
| 479 | + for arg_name, arg in args.items(): |
| 480 | + arg = copy(arg) |
| 481 | + arg.type = remap_type(arg.type, type_map, remapped) |
| 482 | + args[arg_name] = arg |
| 483 | + fields[field_name] = field |
| 484 | + elif is_input_object_type(remapped_type): |
| 485 | + named_type = cast(GraphQLInputObjectType, named_type) |
| 486 | + fields = named_type.fields |
| 487 | + for field_name, field in fields.items(): |
| 488 | + field = copy(field) |
| 489 | + field.type = remap_type(field.type, type_map, remapped) |
| 490 | + fields[field_name] = field |
| 491 | + return named_type |
0 commit comments