Skip to content
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

add support for yield from graphql_async(schema, query) #254

Closed
wants to merge 6 commits into from

Conversation

voith
Copy link

@voith voith commented Aug 11, 2019

What was wrong?

   def run_forever(self):
        """Run until stop() is called."""
        self._check_closed()
        if self.is_running():
>           raise RuntimeError('This event loop is already running')
E           RuntimeError: This event loop is already running

How was it fixed?

  • added support for await graphql_async(schema, query) syntax
  • This only works with AsyncioExecutor. A new coroutine wait_until_finished_async has been implemented. wait_until_finished_async raises NotImplementedError error for all other executors.
  • This syntax only works with GraphQLCoreBackend. This has been done by implementing a new
    coroutine document_from_string_async. document_from_string_async raises NotImplementedError for other backends although this support can be added if needed.

@voith
Copy link
Author

voith commented Aug 11, 2019

cc @syrusakbary

@voith voith changed the title add support for await graphql_async(schema, query) add support for yield from graphql_async(schema, query) Aug 12, 2019
@voith
Copy link
Author

voith commented Aug 12, 2019

tests are failing because yield from syntax is not supported on python2.7 and pypy

@voith
Copy link
Author

voith commented Aug 12, 2019

The inspiration for this feature came from here.
I will try to explain the problem again over here. Here's an MWE to explain the problem:

import asyncio

from graphql import graphql
from graphql.execution.executors.asyncio import AsyncioExecutor
from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString

async def resolver(context, *_):
    await asyncio.sleep(0.001)
    return "hey"

async def resolver_2(context, *_):
    await asyncio.sleep(0.003)
    return "hey2"

def resolver_3(contest, *_):
    return "hey3"


Type = GraphQLObjectType(
        "Type",
        {
            "a": GraphQLField(GraphQLString, resolver=resolver),
            "b": GraphQLField(GraphQLString, resolver=resolver_2),
            "c": GraphQLField(GraphQLString, resolver=resolver_3),
        },
    )

async def get_result(query):
	# AsyncioExecutor will try to execute `loop.run_until_complete`
	# But since trinity is already running `run_forever`, 
	# it will result in a error: `RuntimeError('This event loop is already running',)`
	result = graphql(GraphQLSchema(Type), query, executor=AsyncioExecutor())
	return result

if __name__ == "__main__":
	# think of this loop used by an async framework event loop
	loop = asyncio.get_event_loop()
	query = "{a, b, c}"
	# The async will have its own event_loop running
	result = loop.run_until_complete(get_result(query))
	print('result', result.data)
	print('error', result.errors)

This gives the following output:

result None
error [RuntimeError('Cannot run the event loop while another loop is running',)]

If I try to use a new_loop for AsyncioExecutor, It results in the following:
In order to do so, just modify the code and replace get_result with:

async def get_result(query):
	result = graphql(GraphQLSchema(Type), query, executor=AsyncioExecutor(loop=asyncio.new_event_loop()))
	return result

The output of this code is:

result None
error [RuntimeError('Cannot run the event loop while another loop is running',)]

So the only fix I could think of was supporting await graphql_async(schema, query, executor=AsyncioExecutor()). This was done by introducing a new coroutine wait_until_finished_async on AsyncioExecutor that waits for tasks to finish asynchronously.

@voith
Copy link
Author

voith commented Aug 12, 2019

looks like this PR is not needed. I didn't know about return_promise.

loop = asyncio.get_event_loop()

async def do_exec():
    result = await execute(
        GraphQLSchema(Type),
        ast,
        executor=AsyncioExecutor(loop),
        return_promise=True,
    )
    assert not result.errors
    assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}

loop.run_until_complete(do_exec())

@voith voith closed this Aug 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant