Skip to content

Commit fcb885b

Browse files
committed
Support returning of promise-like objects.
1 parent 6660f92 commit fcb885b

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

graphql/execution/executor.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import sys
55

6-
from promise import Promise, promise_for_dict, promisify
6+
from promise import Promise, promise_for_dict, promisify, is_thenable
77

88
from ..error import GraphQLError, GraphQLLocatedError
99
from ..pyutils.default_ordered_dict import DefaultOrderedDict
@@ -100,7 +100,7 @@ def execute_field_callback(results, response_name):
100100
if result is Undefined:
101101
return results
102102

103-
if is_promise(result):
103+
if is_thenable(result):
104104
def collect_result(resolved_result):
105105
results[response_name] = resolved_result
106106
return results
@@ -206,7 +206,7 @@ def complete_value_catching_error(exe_context, return_type, field_asts, info, re
206206
# resolving a null value for this field if one is encountered.
207207
try:
208208
completed = complete_value(exe_context, return_type, field_asts, info, result)
209-
if is_promise(completed):
209+
if is_thenable(completed):
210210
def handle_error(error):
211211
exe_context.errors.append(error)
212212
return Promise.fulfilled(None)
@@ -240,7 +240,7 @@ def complete_value(exe_context, return_type, field_asts, info, result):
240240
"""
241241
# If field type is NonNull, complete for inner type, and throw field error if result is null.
242242

243-
if is_promise(result):
243+
if is_thenable(result):
244244
return promisify(result).then(
245245
lambda resolved: complete_value(
246246
exe_context,

graphql/execution/tests/test_resolve.py

+57
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,35 @@
66
GraphQLInputObjectField, GraphQLInputObjectType,
77
GraphQLInt, GraphQLList, GraphQLNonNull,
88
GraphQLObjectType, GraphQLSchema, GraphQLString)
9+
from promise import Promise
10+
11+
class CustomPromise(object):
12+
def __init__(self, fn=None, promise=None):
13+
self._promise = promise or Promise(fn)
14+
15+
def get(self, _=None):
16+
raise NotImplementedError("Blocking for results not allowed. Use 'then' if you want to "
17+
"work with the result.")
18+
19+
def then(self, success=None, failure=None):
20+
return self.__class__(promise=self._promise.then(success, failure))
21+
22+
def __getattr__(self, item):
23+
return getattr(self._promise, item)
24+
25+
@classmethod
26+
def fulfilled(cls, x):
27+
p = cls()
28+
p.fulfill(x)
29+
return p
30+
31+
resolve = fulfilled
32+
33+
@classmethod
34+
def rejected(cls, reason):
35+
p = cls()
36+
p.reject(reason)
37+
return p
938

1039

1140
def _test_schema(test_field):
@@ -73,6 +102,34 @@ def resolver(source, args, *_):
73102
]
74103

75104

105+
def test_handles_resolved_promises():
106+
def resolver(source, args, *_):
107+
return Promise.resolve('foo')
108+
109+
schema = _test_schema(GraphQLField(
110+
GraphQLString,
111+
resolver=resolver
112+
))
113+
114+
result = graphql(schema, '{ test }', None)
115+
assert not result.errors
116+
assert result.data == {'test': 'foo'}
117+
118+
119+
def test_handles_resolved_custom_promises():
120+
def resolver(source, args, *_):
121+
return CustomPromise.resolve('custom_foo')
122+
123+
schema = _test_schema(GraphQLField(
124+
GraphQLString,
125+
resolver=resolver
126+
))
127+
128+
result = graphql(schema, '{ test }', None)
129+
assert not result.errors
130+
assert result.data == {'test': 'custom_foo'}
131+
132+
76133
def test_maps_argument_out_names_well():
77134
def resolver(source, args, *_):
78135
return json.dumps([source, args], separators=(',', ':'))

0 commit comments

Comments
 (0)