conftest.py 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. import asyncio
  2. import gc
  3. from contextlib import suppress
  4. import psutil
  5. import pytest
  6. from hivemind.utils.crypto import RSAPrivateKey
  7. from hivemind.utils.logging import get_logger
  8. from hivemind.utils.mpfuture import MPFuture
  9. logger = get_logger(__name__)
  10. @pytest.fixture
  11. def event_loop():
  12. """
  13. This overrides the ``event_loop`` fixture from pytest-asyncio
  14. (e.g. to make it compatible with ``asyncio.subprocess``).
  15. This fixture is identical to the original one but does not call ``loop.close()`` in the end.
  16. Indeed, at this point, the loop is already stopped (i.e. next tests are free to create new loops).
  17. However, finalizers of objects created in the current test may reference the current loop and fail if it is closed.
  18. For example, this happens while using ``asyncio.subprocess`` (the ``asyncio.subprocess.Process`` finalizer
  19. fails if the loop is closed, but works if the loop is only stopped).
  20. """
  21. yield asyncio.get_event_loop()
  22. @pytest.fixture(autouse=True, scope="session")
  23. def cleanup_children():
  24. yield
  25. with RSAPrivateKey._process_wide_key_lock:
  26. RSAPrivateKey._process_wide_key = None
  27. gc.collect() # Call .__del__() for removed objects
  28. children = psutil.Process().children(recursive=True)
  29. if children:
  30. logger.info(f"Cleaning up {len(children)} leftover child processes")
  31. for child in children:
  32. with suppress(psutil.NoSuchProcess):
  33. child.terminate()
  34. psutil.wait_procs(children, timeout=1)
  35. for child in children:
  36. with suppress(psutil.NoSuchProcess):
  37. child.kill()
  38. MPFuture.reset_backend()