Skip to content

Support returning of promise-like objects. #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions graphql/execution/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import sys

from promise import Promise, promise_for_dict, promisify
from promise import Promise, promise_for_dict, promisify, is_thenable

from ..error import GraphQLError, GraphQLLocatedError
from ..pyutils.default_ordered_dict import DefaultOrderedDict
Expand Down Expand Up @@ -100,7 +100,7 @@ def execute_field_callback(results, response_name):
if result is Undefined:
return results

if is_promise(result):
if is_thenable(result):
def collect_result(resolved_result):
results[response_name] = resolved_result
return results
Expand Down Expand Up @@ -206,7 +206,7 @@ def complete_value_catching_error(exe_context, return_type, field_asts, info, re
# resolving a null value for this field if one is encountered.
try:
completed = complete_value(exe_context, return_type, field_asts, info, result)
if is_promise(completed):
if is_thenable(completed):
def handle_error(error):
exe_context.errors.append(error)
return Promise.fulfilled(None)
Expand Down Expand Up @@ -240,7 +240,7 @@ def complete_value(exe_context, return_type, field_asts, info, result):
"""
# If field type is NonNull, complete for inner type, and throw field error if result is null.

if is_promise(result):
if is_thenable(result):
return promisify(result).then(
lambda resolved: complete_value(
exe_context,
Expand Down
57 changes: 57 additions & 0 deletions graphql/execution/tests/test_resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,35 @@
GraphQLInputObjectField, GraphQLInputObjectType,
GraphQLInt, GraphQLList, GraphQLNonNull,
GraphQLObjectType, GraphQLSchema, GraphQLString)
from promise import Promise

class CustomPromise(object):
def __init__(self, fn=None, promise=None):
self._promise = promise or Promise(fn)

def get(self, _=None):
raise NotImplementedError("Blocking for results not allowed. Use 'then' if you want to "
"work with the result.")

def then(self, success=None, failure=None):
return self.__class__(promise=self._promise.then(success, failure))

def __getattr__(self, item):
return getattr(self._promise, item)

@classmethod
def fulfilled(cls, x):
p = cls()
p.fulfill(x)
return p

resolve = fulfilled

@classmethod
def rejected(cls, reason):
p = cls()
p.reject(reason)
return p


def _test_schema(test_field):
Expand Down Expand Up @@ -73,6 +102,34 @@ def resolver(source, args, *_):
]


def test_handles_resolved_promises():
def resolver(source, args, *_):
return Promise.resolve('foo')

schema = _test_schema(GraphQLField(
GraphQLString,
resolver=resolver
))

result = graphql(schema, '{ test }', None)
assert not result.errors
assert result.data == {'test': 'foo'}


def test_handles_resolved_custom_promises():
def resolver(source, args, *_):
return CustomPromise.resolve('custom_foo')

schema = _test_schema(GraphQLField(
GraphQLString,
resolver=resolver
))

result = graphql(schema, '{ test }', None)
assert not result.errors
assert result.data == {'test': 'custom_foo'}


def test_maps_argument_out_names_well():
def resolver(source, args, *_):
return json.dumps([source, args], separators=(',', ':'))
Expand Down