diff --git a/graphql_server/__init__.py b/graphql_server/__init__.py index c208f3b..439d6f5 100644 --- a/graphql_server/__init__.py +++ b/graphql_server/__init__.py @@ -122,20 +122,32 @@ def run_http_query( all_params = [get_graphql_params(entry, extra_data) for entry in data] - executor = execute_options.get("executor") - response_executor = executor if executor else SyncExecutor() - - response_promises = [ - response_executor.execute( - get_response, schema, params, catch_exc, allow_only_query, **execute_options - ) - for params in all_params - ] - response_executor.wait_until_finished() - - results = [ - result.get() if is_thenable(result) else result for result in response_promises - ] + if execute_options.get("return_promise"): + results = [ + get_response(schema, params, catch_exc, allow_only_query, **execute_options) + for params in all_params + ] + else: + executor = execute_options.get("executor") + response_executor = executor if executor else SyncExecutor() + + response_promises = [ + response_executor.execute( + get_response, + schema, + params, + catch_exc, + allow_only_query, + **execute_options + ) + for params in all_params + ] + response_executor.wait_until_finished() + + results = [ + result.get() if is_thenable(result) else result + for result in response_promises + ] return ServerResults(results, all_params) diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py new file mode 100644 index 0000000..d3b5426 --- /dev/null +++ b/tests/test_asyncio.py @@ -0,0 +1,99 @@ +# flake8: noqa + +import pytest + +asyncio = pytest.importorskip("asyncio") + +from graphql.execution.executors.asyncio import AsyncioExecutor +from graphql.type.definition import ( + GraphQLField, + GraphQLNonNull, + GraphQLObjectType, +) +from graphql.type.scalars import GraphQLString +from graphql.type.schema import GraphQLSchema +from graphql_server import RequestParams, run_http_query +from promise import Promise + +from .utils import as_dicts + + +def resolve_error_sync(_obj, _info): + raise ValueError("error sync") + + +@asyncio.coroutine +def resolve_error_async(_obj, _info): + yield from asyncio.sleep(0.001) + raise ValueError("error async") + + +def resolve_field_sync(_obj, _info): + return "sync" + + +@asyncio.coroutine +def resolve_field_async(_obj, info): + yield from asyncio.sleep(0.001) + return "async" + + +NonNullString = GraphQLNonNull(GraphQLString) + +QueryRootType = GraphQLObjectType( + name="QueryRoot", + fields={ + "errorSync": GraphQLField(NonNullString, resolver=resolve_error_sync), + "errorAsync": GraphQLField(NonNullString, resolver=resolve_error_async), + "fieldSync": GraphQLField(NonNullString, resolver=resolve_field_sync), + "fieldAsync": GraphQLField(NonNullString, resolver=resolve_field_async), + }, +) + +schema = GraphQLSchema(QueryRootType) + + +def test_get_reponses_using_asyncioexecutor(): + class TestExecutor(AsyncioExecutor): + called = False + waited = False + cleaned = False + + def wait_until_finished(self): + TestExecutor.waited = True + super().wait_until_finished() + + def clean(self): + TestExecutor.cleaned = True + super().clean() + + def execute(self, fn, *args, **kwargs): + TestExecutor.called = True + return super().execute(fn, *args, **kwargs) + + query = "{fieldSync fieldAsync}" + + loop = asyncio.get_event_loop() + + @asyncio.coroutine + def get_results(): + result_promises, params = run_http_query( + schema, + "get", + {}, + dict(query=query), + executor=TestExecutor(loop=loop), + return_promise=True, + ) + results = yield from Promise.all(result_promises) + return results, params + + results, params = loop.run_until_complete(get_results()) + + expected_results = [{"data": {"fieldSync": "sync", "fieldAsync": "async"}}] + + assert as_dicts(results) == expected_results + assert params == [RequestParams(query=query, variables=None, operation_name=None)] + assert TestExecutor.called + assert not TestExecutor.waited + assert TestExecutor.cleaned diff --git a/tests/test_query.py b/tests/test_query.py index 7a1857e..d1739e8 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -1,6 +1,7 @@ import json from graphql.error import GraphQLError +from promise import Promise from graphql_server import ( HttpQueryError, @@ -15,11 +16,7 @@ from pytest import raises from .schema import schema - - -def as_dicts(results): - """Convert execution results to a list of tuples of dicts for better comparison.""" - return [result.to_dict(dict_class=dict) for result in results] +from .utils import as_dicts def test_request_params(): @@ -550,6 +547,34 @@ def execute(self, fn, *args, **kwargs): query = "{test}" results, params = run_http_query( + schema, "get", {}, dict(query=query), executor=TestExecutor(), + ) + + assert as_dicts(results) == [{"data": {"test": "Hello World"}}] + assert params == [RequestParams(query=query, variables=None, operation_name=None)] + assert TestExecutor.called + assert TestExecutor.waited + assert not TestExecutor.cleaned + + +def test_get_reponses_using_executor_return_promise(): + class TestExecutor(object): + called = False + waited = False + cleaned = False + + def wait_until_finished(self): + TestExecutor.waited = True + + def clean(self): + TestExecutor.cleaned = True + + def execute(self, fn, *args, **kwargs): + TestExecutor.called = True + return fn(*args, **kwargs) + + query = "{test}" + result_promises, params = run_http_query( schema, "get", {}, @@ -558,8 +583,10 @@ def execute(self, fn, *args, **kwargs): return_promise=True, ) + results = Promise.all(result_promises).get() + assert as_dicts(results) == [{"data": {"test": "Hello World"}}] assert params == [RequestParams(query=query, variables=None, operation_name=None)] assert TestExecutor.called - assert TestExecutor.waited + assert not TestExecutor.waited assert TestExecutor.cleaned diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..136f09f --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,3 @@ +def as_dicts(results): + """Convert execution results to a list of tuples of dicts for better comparison.""" + return [result.to_dict(dict_class=dict) for result in results] diff --git a/tox.ini b/tox.ini index 54ee181..ee8ae09 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,8 @@ deps = graphql-core>=2.1,<3 pytest-cov>=2.7 commands = - py{py,27,33,34,35,36,37}: py.test tests {posargs} + py{py,27}: py.test tests {posargs} --ignore=tests/test_asyncio.py + py{py3,33,34,35,36,37,38}: py.test tests {posargs} [testenv:black] basepython=python3.7