Skip to content

Commit 9fbc42f

Browse files
committed
Catch unhandled exception in abstract resolution
Replicates graphql/graphql-js@a7db60be
1 parent fa3d7af commit 9fbc42f

2 files changed

Lines changed: 79 additions & 2 deletions

File tree

‎src/graphql/execution/execute.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from asyncio import ensure_future, gather
22
from collections.abc import Mapping
3+
from contextlib import suppress
34
from inspect import isawaitable
45
from typing import (
56
Any,
@@ -1219,6 +1220,16 @@ def default_type_resolver(
12191220
append_awaitable_results(cast(Awaitable, is_type_of_result))
12201221
append_awaitable_types(type_)
12211222
elif is_type_of_result:
1223+
if awaitable_is_type_of_results:
1224+
# noinspection PyShadowingNames
1225+
async def await_is_type_of_and_return_type(
1226+
resolved_type_name: str = type_.name,
1227+
) -> str:
1228+
with suppress(Exception):
1229+
await gather(*awaitable_is_type_of_results)
1230+
return resolved_type_name
1231+
1232+
return await_is_type_of_and_return_type()
12221233
return type_.name
12231234

12241235
if awaitable_is_type_of_results:

‎tests/execution/test_union_interface.py‎

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from typing import Optional, Union, List
22

3-
from graphql.execution import execute_sync
3+
from pytest import mark
4+
5+
from graphql.execution import execute, execute_sync, ExecutionResult
46
from graphql.language import parse
57
from graphql.type import (
68
GraphQLBoolean,
@@ -46,21 +48,32 @@ def __init__(self, name: str, meows: bool):
4648
self.progeny = []
4749

4850

51+
class Plant:
52+
53+
name: str
54+
55+
def __init__(self, name: str):
56+
self.name = name
57+
58+
4959
class Person:
5060

5161
name: str
5262
pets: Optional[List[Union[Dog, Cat]]]
5363
friends: Optional[List[Union[Dog, Cat, "Person"]]]
64+
responsibilities: Optional[List[Union[Dog, Cat, Plant]]]
5465

5566
def __init__(
5667
self,
5768
name: str,
5869
pets: Optional[List[Union[Dog, Cat]]] = None,
5970
friends: Optional[List[Union[Dog, Cat, "Person"]]] = None,
71+
responsibilities: Optional[List[Union[Dog, Cat, Plant]]] = None,
6072
):
6173
self.name = name
6274
self.pets = pets
6375
self.friends = friends
76+
self.responsibilities = responsibilities
6477

6578

6679
NamedType = GraphQLInterfaceType("Named", {"name": GraphQLField(GraphQLString)})
@@ -106,6 +119,18 @@ def __init__(
106119
)
107120

108121

122+
async def resolve_plant_type(_value, _info):
123+
raise RuntimeError("Not sure if this is a plant")
124+
125+
126+
PlantType = GraphQLObjectType(
127+
"Plant",
128+
lambda: {"name": GraphQLField(GraphQLString)},
129+
interfaces=[NamedType],
130+
is_type_of=resolve_plant_type,
131+
)
132+
133+
109134
def resolve_pet_type(value, _info, _type):
110135
if isinstance(value, Dog):
111136
return DogType.name
@@ -118,12 +143,15 @@ def resolve_pet_type(value, _info, _type):
118143

119144
PetType = GraphQLUnionType("Pet", [DogType, CatType], resolve_type=resolve_pet_type)
120145

146+
PetOrPlantType = GraphQLUnionType("PetOrPlantType", [PlantType, DogType, CatType])
147+
121148
PersonType = GraphQLObjectType(
122149
"Person",
123150
lambda: {
124151
"name": GraphQLField(GraphQLString),
125152
"pets": GraphQLField(GraphQLList(PetType)),
126153
"friends": GraphQLField(GraphQLList(NamedType)),
154+
"responsibilities": GraphQLField(GraphQLList(PetOrPlantType)),
127155
"progeny": GraphQLField(GraphQLList(PersonType)), # type: ignore
128156
"mother": GraphQLField(PersonType), # type: ignore
129157
"father": GraphQLField(PersonType), # type: ignore
@@ -142,8 +170,9 @@ def resolve_pet_type(value, _info, _type):
142170
odie.mother = Dog("Odie's Mom", True)
143171
odie.mother.progeny = [odie]
144172

173+
fern = Plant("Fern")
145174
liz = Person("Liz", [], [])
146-
john = Person("John", [garfield, odie], [liz, odie])
175+
john = Person("John", [garfield, odie], [liz, odie], [garfield, fern])
147176

148177

149178
def describe_execute_union_and_intersection_types():
@@ -191,6 +220,7 @@ def can_introspect_on_union_and_intersection_types():
191220
{"name": "Dog"},
192221
{"name": "Cat"},
193222
{"name": "Person"},
223+
{"name": "Plant"},
194224
],
195225
"enumValues": None,
196226
"inputFields": None,
@@ -514,3 +544,39 @@ def resolve_type(_source, info, _type):
514544
"root_value": root_value,
515545
"context": context_value,
516546
}
547+
548+
@mark.asyncio
549+
@mark.filterwarnings("error:.*was never awaited:RuntimeWarning")
550+
async def handles_rejections_from_is_type_of_after_an_is_type_of_returns_true():
551+
document = parse("""
552+
{
553+
responsibilities {
554+
__typename
555+
... on Dog {
556+
name
557+
barks
558+
}
559+
... on Cat {
560+
name
561+
meows
562+
}
563+
}
564+
}
565+
""")
566+
567+
root_value = Person("John", [], [liz], [garfield])
568+
context_value = {"authToken": "123abc"}
569+
570+
result = execute(schema, document, root_value, context_value)
571+
assert not isinstance(result, ExecutionResult)
572+
result = await result
573+
assert isinstance(result, ExecutionResult)
574+
575+
assert result == (
576+
{
577+
"responsibilities": [
578+
{"__typename": "Cat", "name": "Garfield", "meows": False},
579+
],
580+
},
581+
None,
582+
)

0 commit comments

Comments
 (0)