Skip to content

Commit 8f4e333

Browse files
committed
Convert Thunk to ThunkCollection and ThunkMapping
Replicates graphql/graphql-js@3e916ef
1 parent a7d5ab3 commit 8f4e333

File tree

5 files changed

+76
-52
lines changed

5 files changed

+76
-52
lines changed

docs/modules/type.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ Types
9292
.. autoclass:: GraphQLType
9393
.. autoclass:: GraphQLWrappingType
9494

95-
.. autoclass:: Thunk
95+
.. autoclass:: ThunkCollection
96+
.. autoclass:: ThunkMapping
9697

9798
Resolvers
9899
^^^^^^^^^

src/graphql/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@
142142
GraphQLWrappingType,
143143
GraphQLNullableType,
144144
GraphQLNamedType,
145-
Thunk,
145+
ThunkCollection,
146+
ThunkMapping,
146147
GraphQLArgument,
147148
GraphQLArgumentMap,
148149
GraphQLEnumValue,
@@ -504,7 +505,8 @@
504505
"GraphQLWrappingType",
505506
"GraphQLNullableType",
506507
"GraphQLNamedType",
507-
"Thunk",
508+
"ThunkCollection",
509+
"ThunkMapping",
508510
"GraphQLArgument",
509511
"GraphQLArgumentMap",
510512
"GraphQLEnumValue",

src/graphql/type/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
GraphQLWrappingType,
7777
GraphQLNullableType,
7878
GraphQLNamedType,
79-
Thunk,
79+
ThunkCollection,
80+
ThunkMapping,
8081
GraphQLArgument,
8182
GraphQLArgumentMap,
8283
GraphQLEnumValue,
@@ -203,7 +204,8 @@
203204
"GraphQLWrappingType",
204205
"GraphQLNullableType",
205206
"GraphQLNamedType",
206-
"Thunk",
207+
"ThunkCollection",
208+
"ThunkMapping",
207209
"GraphQLArgument",
208210
"GraphQLArgumentMap",
209211
"GraphQLEnumValue",

src/graphql/type/definition.py

+52-35
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
Dict,
77
Generic,
88
List,
9+
Mapping,
910
NamedTuple,
1011
Optional,
1112
TYPE_CHECKING,
@@ -130,7 +131,8 @@
130131
"GraphQLTypeResolver",
131132
"GraphQLUnionType",
132133
"GraphQLWrappingType",
133-
"Thunk",
134+
"ThunkCollection",
135+
"ThunkMapping",
134136
]
135137

136138

@@ -287,15 +289,30 @@ def get_named_type(type_: Optional[GraphQLType]) -> Optional[GraphQLNamedType]:
287289
return None
288290

289291

290-
def resolve_thunk(thunk: Any) -> Any:
291-
"""Resolve the given thunk.
292+
T = TypeVar("T")
293+
294+
ThunkCollection = Union[Callable[[], Collection[T]], Collection[T]]
295+
ThunkMapping = Union[Callable[[], Mapping[str, T]], Mapping[str, T]]
296+
297+
298+
def resolve_thunk_collection(thunk: ThunkCollection[T]) -> Collection[T]:
299+
"""Resolve the given thunk for a collection.
292300
293301
Used while defining GraphQL types to allow for circular references in otherwise
294302
immutable type definitions.
295303
"""
296304
return thunk() if callable(thunk) else thunk
297305

298306

307+
def resolve_thunk_mapping(thunk: ThunkMapping[T]) -> Mapping[str, T]:
308+
"""Resolve the given thunk for a mapping.
309+
310+
Used while defining GraphQL fields to allow for circular references in otherwise
311+
immutable field definitions.
312+
"""
313+
return thunk() if callable(thunk) else thunk
314+
315+
299316
GraphQLScalarSerializer = Callable[[Any], Any]
300317
GraphQLScalarValueParser = Callable[[Any], Any]
301318
GraphQLScalarLiteralParser = Callable[[ValueNode, Optional[Dict[str, Any]]], Any]
@@ -592,6 +609,8 @@ class GraphQLResolveInfo(NamedTuple):
592609
# the context is passed as part of the GraphQLResolveInfo:
593610
GraphQLIsTypeOfFn = Callable[[Any, GraphQLResolveInfo], AwaitableOrValue[bool]]
594611

612+
GraphQLFieldMap = Dict[str, GraphQLField]
613+
595614

596615
class GraphQLArgument:
597616
"""Definition of a GraphQL argument"""
@@ -669,12 +688,6 @@ def is_required_argument(arg: GraphQLArgument) -> bool:
669688
return is_non_null_type(arg.type) and arg.default_value is Undefined
670689

671690

672-
T = TypeVar("T", bound=Collection)
673-
Thunk = Union[Callable[[], T], T]
674-
675-
GraphQLFieldMap = Dict[str, GraphQLField]
676-
677-
678691
class GraphQLObjectType(GraphQLNamedType):
679692
"""Object Type Definition
680693
@@ -710,8 +723,8 @@ class GraphQLObjectType(GraphQLNamedType):
710723
def __init__(
711724
self,
712725
name: str,
713-
fields: Thunk[GraphQLFieldMap],
714-
interfaces: Optional[Thunk[Collection["GraphQLInterfaceType"]]] = None,
726+
fields: ThunkMapping[GraphQLField],
727+
interfaces: Optional[ThunkCollection["GraphQLInterfaceType"]] = None,
715728
is_type_of: Optional[GraphQLIsTypeOfFn] = None,
716729
extensions: Optional[Dict[str, Any]] = None,
717730
description: Optional[str] = None,
@@ -758,15 +771,15 @@ def __copy__(self) -> "GraphQLObjectType": # pragma: no cover
758771
def fields(self) -> GraphQLFieldMap:
759772
"""Get provided fields, wrapping them as GraphQLFields if needed."""
760773
try:
761-
fields = resolve_thunk(self._fields)
774+
fields = resolve_thunk_mapping(self._fields)
762775
except Exception as error:
763776
raise TypeError(f"{self.name} fields cannot be resolved. {error}")
764-
if not isinstance(fields, dict) or not all(
777+
if not isinstance(fields, Mapping) or not all(
765778
isinstance(key, str) for key in fields
766779
):
767780
raise TypeError(
768781
f"{self.name} fields must be specified"
769-
" as a dict with field names as keys."
782+
" as a mapping with field names as keys."
770783
)
771784
if not all(
772785
isinstance(value, GraphQLField) or is_output_type(value)
@@ -776,16 +789,18 @@ def fields(self) -> GraphQLFieldMap:
776789
f"{self.name} fields must be GraphQLField or output type objects."
777790
)
778791
return {
779-
name: value if isinstance(value, GraphQLField) else GraphQLField(value)
792+
name: value
793+
if isinstance(value, GraphQLField)
794+
else GraphQLField(value) # type: ignore
780795
for name, value in fields.items()
781796
}
782797

783798
@cached_property
784799
def interfaces(self) -> List["GraphQLInterfaceType"]:
785800
"""Get provided interfaces."""
786801
try:
787-
interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk(
788-
self._interfaces
802+
interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk_collection(
803+
self._interfaces # type: ignore
789804
)
790805
except Exception as error:
791806
raise TypeError(f"{self.name} interfaces cannot be resolved. {error}")
@@ -833,8 +848,8 @@ class GraphQLInterfaceType(GraphQLNamedType):
833848
def __init__(
834849
self,
835850
name: str,
836-
fields: Optional[Thunk[GraphQLFieldMap]] = None,
837-
interfaces: Optional[Thunk[Collection["GraphQLInterfaceType"]]] = None,
851+
fields: ThunkMapping[GraphQLField],
852+
interfaces: Optional[ThunkCollection["GraphQLInterfaceType"]] = None,
838853
resolve_type: Optional[GraphQLTypeResolver] = None,
839854
description: Optional[str] = None,
840855
extensions: Optional[Dict[str, Any]] = None,
@@ -881,15 +896,15 @@ def __copy__(self) -> "GraphQLInterfaceType": # pragma: no cover
881896
def fields(self) -> GraphQLFieldMap:
882897
"""Get provided fields, wrapping them as GraphQLFields if needed."""
883898
try:
884-
fields = resolve_thunk(self._fields)
899+
fields = resolve_thunk_mapping(self._fields)
885900
except Exception as error:
886901
raise TypeError(f"{self.name} fields cannot be resolved. {error}")
887-
if not isinstance(fields, dict) or not all(
902+
if not isinstance(fields, Mapping) or not all(
888903
isinstance(key, str) for key in fields
889904
):
890905
raise TypeError(
891906
f"{self.name} fields must be specified"
892-
" as a dict with field names as keys."
907+
" as a mapping with field names as keys."
893908
)
894909
if not all(
895910
isinstance(value, GraphQLField) or is_output_type(value)
@@ -899,16 +914,18 @@ def fields(self) -> GraphQLFieldMap:
899914
f"{self.name} fields must be GraphQLField or output type objects."
900915
)
901916
return {
902-
name: value if isinstance(value, GraphQLField) else GraphQLField(value)
917+
name: value
918+
if isinstance(value, GraphQLField)
919+
else GraphQLField(value) # type: ignore
903920
for name, value in fields.items()
904921
}
905922

906923
@cached_property
907924
def interfaces(self) -> List["GraphQLInterfaceType"]:
908925
"""Get provided interfaces."""
909926
try:
910-
interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk(
911-
self._interfaces
927+
interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk_collection(
928+
self._interfaces # type: ignore
912929
)
913930
except Exception as error:
914931
raise TypeError(f"{self.name} interfaces cannot be resolved. {error}")
@@ -959,7 +976,7 @@ def resolve_type(obj, _info, _type):
959976
def __init__(
960977
self,
961978
name: str,
962-
types: Thunk[Collection[GraphQLObjectType]],
979+
types: ThunkCollection[GraphQLObjectType],
963980
resolve_type: Optional[GraphQLTypeResolver] = None,
964981
description: Optional[str] = None,
965982
extensions: Optional[Dict[str, Any]] = None,
@@ -1002,7 +1019,7 @@ def __copy__(self) -> "GraphQLUnionType": # pragma: no cover
10021019
def types(self) -> List[GraphQLObjectType]:
10031020
"""Get provided types."""
10041021
try:
1005-
types: Collection[GraphQLObjectType] = resolve_thunk(self._types)
1022+
types: Collection[GraphQLObjectType] = resolve_thunk_collection(self._types)
10061023
except Exception as error:
10071024
raise TypeError(f"{self.name} types cannot be resolved. {error}")
10081025
if types is None:
@@ -1068,7 +1085,7 @@ class RGBEnum(enum.Enum):
10681085
def __init__(
10691086
self,
10701087
name: str,
1071-
values: Union[GraphQLEnumValueMap, Dict[str, Any], Type[Enum]],
1088+
values: Union[GraphQLEnumValueMap, Mapping[str, Any], Type[Enum]],
10721089
description: Optional[str] = None,
10731090
extensions: Optional[Dict[str, Any]] = None,
10741091
ast_node: Optional[EnumTypeDefinitionNode] = None,
@@ -1084,15 +1101,15 @@ def __init__(
10841101
try: # check for enum
10851102
values = cast(Enum, values).__members__ # type: ignore
10861103
except AttributeError:
1087-
if not isinstance(values, dict) or not all(
1104+
if not isinstance(values, Mapping) or not all(
10881105
isinstance(name, str) for name in values
10891106
):
10901107
try:
10911108
# noinspection PyTypeChecker
10921109
values = dict(values) # type: ignore
10931110
except (TypeError, ValueError):
10941111
raise TypeError(
1095-
f"{name} values must be an Enum or a dict"
1112+
f"{name} values must be an Enum or a mapping"
10961113
" with value names as keys."
10971114
)
10981115
values = cast(Dict, values)
@@ -1298,7 +1315,7 @@ class GeoPoint(GraphQLInputObjectType):
12981315
def __init__(
12991316
self,
13001317
name: str,
1301-
fields: Thunk[GraphQLInputFieldMap],
1318+
fields: ThunkMapping["GraphQLInputField"],
13021319
description: Optional[str] = None,
13031320
out_type: Optional[GraphQLInputFieldOutType] = None,
13041321
extensions: Optional[Dict[str, Any]] = None,
@@ -1354,15 +1371,15 @@ def __copy__(self) -> "GraphQLInputObjectType": # pragma: no cover
13541371
def fields(self) -> GraphQLInputFieldMap:
13551372
"""Get provided fields, wrap them as GraphQLInputField if needed."""
13561373
try:
1357-
fields = resolve_thunk(self._fields)
1374+
fields = resolve_thunk_mapping(self._fields)
13581375
except Exception as error:
13591376
raise TypeError(f"{self.name} fields cannot be resolved. {error}")
1360-
if not isinstance(fields, dict) or not all(
1377+
if not isinstance(fields, Mapping) or not all(
13611378
isinstance(key, str) for key in fields
13621379
):
13631380
raise TypeError(
13641381
f"{self.name} fields must be specified"
1365-
" as a dict with field names as keys."
1382+
" as a mapping with field names as keys."
13661383
)
13671384
if not all(
13681385
isinstance(value, GraphQLInputField) or is_input_type(value)
@@ -1375,7 +1392,7 @@ def fields(self) -> GraphQLInputFieldMap:
13751392
return {
13761393
name: value
13771394
if isinstance(value, GraphQLInputField)
1378-
else GraphQLInputField(value)
1395+
else GraphQLInputField(value) # type: ignore
13791396
for name, value in fields.items()
13801397
}
13811398

0 commit comments

Comments
 (0)