Quellcode durchsuchen

Fix some warnings related to asyncio (#346)

This PR fixes asyncio warnings about "RuntimeError: Event loop is closed" and "ConnectionResetError: Connection lost".
Alexander Borzunov vor 4 Jahren
Ursprung
Commit
cf3f64a26d
2 geänderte Dateien mit 20 neuen und 1 gelöschten Zeilen
  1. 3 1
      hivemind/p2p/p2p_daemon.py
  2. 17 0
      tests/conftest.py

+ 3 - 1
hivemind/p2p/p2p_daemon.py

@@ -331,7 +331,9 @@ class P2P:
                         await P2P.send_protobuf(response, writer)
                 except Exception as e:
                     logger.warning("Exception while processing stream and sending responses:", exc_info=True)
-                    await P2P.send_protobuf(RPCError(message=str(e)), writer)
+                    # Sometimes `e` is a connection error, so we won't be able to report the error to the caller
+                    with suppress(Exception):
+                        await P2P.send_protobuf(RPCError(message=str(e)), writer)
 
             with closing(writer):
                 processing_task = asyncio.create_task(_process_stream())

+ 17 - 0
tests/conftest.py

@@ -1,3 +1,4 @@
+import asyncio
 import gc
 import multiprocessing as mp
 from contextlib import suppress
@@ -11,6 +12,22 @@ from hivemind.utils.mpfuture import MPFuture, SharedBytes
 logger = get_logger(__name__)
 
 
+@pytest.fixture
+def event_loop():
+    """
+    This overrides the ``event_loop`` fixture from pytest-asyncio
+    (e.g. to make it compatible with ``asyncio.subprocess``).
+
+    This fixture is identical to the original one but does not call ``loop.close()`` in the end.
+    Indeed, at this point, the loop is already stopped (i.e. next tests are free to create new loops).
+    However, finalizers of objects created in the current test may reference the current loop and fail if it is closed.
+    For example, this happens while using ``asyncio.subprocess`` (the ``asyncio.subprocess.Process`` finalizer
+    fails if the loop is closed, but works if the loop is only stopped).
+    """
+
+    yield asyncio.get_event_loop()
+
+
 @pytest.fixture(autouse=True, scope="session")
 def cleanup_children():
     yield