1

I am trying to make some API calls to firebase using the google.cloud.firestore.AsyncClient module. Fire2 is just a wrapper for this object.

The traceback suggests its a problem with asyncio, but if I make a dummy await it actually runs just fine. What is the problem and why does it occur?

Sample code:

import Fire2

class Test:
    def __init__(self):
        doc = asyncio.run(self.wait())

    async def wait(self):
        doc = await Fire2("test1").get(g1)  # gives the error
        # doc = await asyncio.sleep(1)  # runs without error
        return doc

    def test(self):
        x = Test2(p1)

class Test2:
    def __init__(self, p):
        doc = asyncio.run(self.run(p))
        print(doc.to_dict())

    async def run(self, p):
        doc = await Fire2('test2').get(p)
        return doc

p1 = 'foo'
g1 = 'bar'
h = Test()
h.test()

Traceback:

Traceback (most recent call last):
  File "<project path>\scratch.py", line 137, in <module>
    h.test()
  File "<project path>\scratch.py", line 123, in test
    x = Test2(p1)
  File "<project path>\scratch.py", line 127, in __init__
    doc = asyncio.run(self.run(p))
  File "<user AppData>\Local\Programs\Python\Python39\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "<user AppData>\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 642, in run_until_complete
    return future.result()
  File "<project path>\scratch.py", line 131, in run
    doc = await Fire2('test2').get(p)
  File "<project path>\<Fire2 file>", line 422, in get
    res = await self._collection.document(doc_id).get()
  File "<project path>\venv\lib\site-packages\google\cloud\firestore_v1\async_document.py", line 364, in get
    response_iter = await self._client._firestore_api.batch_get_documents(
  File "<project path>\venv\lib\site-packages\google\api_core\grpc_helpers_async.py", line 171, in error_remapped_callable
    call = callable_(*args, **kwargs)
  File "<project path>\venv\lib\site-packages\grpc\aio\_channel.py", line 165, in __call__
    call = UnaryStreamCall(request, deadline, metadata, credentials,
  File "<project path>\venv\lib\site-packages\grpc\aio\_call.py", line 553, in __init__
    self._send_unary_request_task = loop.create_task(
  File "<user AppData>\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 431, in create_task
    self._check_closed()
  File "<user AppData>\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 510, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
sys:1: RuntimeWarning: coroutine 'UnaryStreamCall._send_unary_request' was never awaited

Process finished with exit code 1

weasel
  • 534
  • 1
  • 5
  • 18

2 Answers2

0

You are making two separate calls to asyncio.run. We can see from the stack trace that the error is coming from inside the grpc library. I suspect that the same grpc connection is being used both times, and that during its initialization it integrates itself with the async event loop in order to maintain the connection in the background between calls.

The problem is that every time you call asyncio.run, you will create a new event loop, and every time it returns that event loop will be closed. So by the time you call this function again, and the grpc layer tries to do its work, it realizes that the original event loop has been closed and it returns this error.

To fix this, You can have a single call to asyncio.run, typically calling a "main" function, or similar. This would then make calls to async functions which perform the actual work.

If the problem persists then there might be some issues in the “Fire2”class.

For more reference you can check this link which outlines high-level asyncio APIs to work with coroutines and Tasks with the help of various examples.

Divyani Yadav
  • 1,030
  • 4
  • 9
  • However, if I just run a dummy await coroutine (see the comment in the sample code), it runs without any errors, so it doesn't seem the problem is with the event loops? Also does calling `asyncio.run` at different places cause a only a single loop to be created? – weasel Oct 28 '21 at 04:17
  • check out this once : https://stackoverflow.com/a/51116910/15774176 – Divyani Yadav Nov 02 '21 at 07:38
  • is it possible to create multiple even_loop and multiple initialization of firebase client? – Keisuke Nagakawa 永川 圭介 Nov 15 '21 at 03:54
0

Use pytest with using fixture with scope="session" in conftest.py

@pytest.fixture(scope="session")
def event_loop():
    loop = get_event_loop()
    yield loop
    loop.close()