miguelgrinberg / greenletio

Asyncio integration with sync code using greenlets.
MIT License
150 stars 8 forks source link

Clean up event loop handling #11

Closed blink1073 closed 1 year ago

blink1073 commented 1 year ago

Thanks for this library!

I was trying it out and noticed it was raising DeprecationWarnings from the changing behavior of asyncio.get_event_loop.

This PR cleans up the handling of event loops as well as the deprecation of ssl.PROTOCOL_TLS. It also handles the RuntimeWarning raised in test_bad_await_with_external_loop, to get a clean pytest run with no warnings.

Note that Python 3.6 support was explicitly dropped, so we can rely on asyncio.get_running_loop and asyncio.create_task.

miguelgrinberg commented 1 year ago

I appreciate the PR, but this solution makes it more expensive to obtain the loop, even though for all the current releases of Python no warnings are shown, as far as I can see. The get_event_loop() function is actually not being recommended anymore, so at some point I'm going to look into migrating to get_running_loop() which I believe does not present strange behaviors.

Also, not sure if silencing warnings is something that other projects do, but to me it feels like a potentially dangerious thing to do. I would prefer to change the usage so that warnings are not generated.

blink1073 commented 1 year ago

I agree about not wanting the runtime cost, and I cannot repro the warning in my test code, but I do see it when running the test suite against Python 3.10. Would you accept a PR that removes warnings from the test suite but does not affect the library code, if I can get that working?

pytest
================================================= test session starts ==================================================
platform darwin -- Python 3.10.7, pytest-7.3.1, pluggy-1.0.0
rootdir: /private/tmp/greenletio
plugins: twisted-1.14.0
collected 34 items

tests/test_cli.py ..                                                                                             [  5%]
tests/test_core.py ............                                                                                  [ 41%]
tests/test_patcher.py ....                                                                                       [ 52%]
tests/test_select.py .                                                                                           [ 55%]
tests/test_socket.py ..                                                                                          [ 61%]
tests/test_ssl.py .                                                                                              [ 64%]
tests/test_threading.py ............                                                                             [100%]

=================================================== warnings summary ===================================================
tests/test_core.py::TestCore::test_async_await_exception
  /private/tmp/greenletio/tests/test_core.py:109: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(b(42))

tests/test_core.py::TestCore::test_async_await_exception2
  /private/tmp/greenletio/tests/test_core.py:125: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(c(42))

tests/test_core.py::TestCore::test_async_await_exception3
  /private/tmp/greenletio/tests/test_core.py:142: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(b())

tests/test_core.py::TestCore::test_async_await_with_context
  /private/tmp/greenletio/tests/test_core.py:96: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(b())

tests/test_core.py::TestCore::test_async_await_with_external_loop
  /private/tmp/greenletio/tests/test_core.py:73: DeprecationWarning: There is no current event loop
    ret = asyncio.get_event_loop().run_until_complete(d(42))

tests/test_core.py::TestCore::test_async_raises_exception
  /private/tmp/greenletio/tests/test_core.py:186: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(b())

tests/test_core.py::TestCore::test_await_after_exception
  /private/tmp/greenletio/tests/test_core.py:203: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(c())

tests/test_core.py: 3 warnings
tests/test_threading.py: 12 warnings
  /private/tmp/greenletio/src/greenletio/core.py:39: DeprecationWarning: There is no current event loop
    if asyncio.get_event_loop().is_running():

tests/test_core.py: 3 warnings
tests/test_threading.py: 12 warnings
  /private/tmp/greenletio/src/greenletio/core.py:28: DeprecationWarning: There is no current event loop
    loop = asyncio.get_event_loop()

tests/test_core.py::TestCore::test_bad_await_with_external_loop
  /private/tmp/greenletio/tests/test_core.py:219: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(c())

tests/test_core.py::TestCore::test_bad_await_with_external_loop
  /private/tmp/greenletio/tests/test_core.py:211: RuntimeWarning: coroutine 'sleep' was never awaited
    with pytest.raises(RuntimeError):
  Enable tracemalloc to get traceback where the object was allocated.
  See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.

tests/test_core.py::TestCore::test_gather_with_external_loop
  /private/tmp/greenletio/tests/test_core.py:165: DeprecationWarning: There is no current event loop
    ret = asyncio.get_event_loop().run_until_complete(c())

tests/test_select.py::TestSelect::test_send_recv
  /private/tmp/greenletio/tests/test_select.py:76: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(main())

tests/test_socket.py::TestSocket::test_sendall_recv
  /private/tmp/greenletio/tests/test_socket.py:52: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(main())

tests/test_socket.py::TestSocket::test_sendto_recvfrom
  /private/tmp/greenletio/tests/test_socket.py:85: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(main())

tests/test_ssl.py::TestSSL::test_sendall_recv
  /private/tmp/greenletio/tests/test_ssl.py:71: DeprecationWarning: There is no current event loop
    asyncio.get_event_loop().run_until_complete(main())

tests/test_ssl.py::TestSSL::test_sendall_recv
tests/test_ssl.py::TestSSL::test_sendall_recv
  /private/tmp/greenletio/src/greenletio/green/ssl.py:124: DeprecationWarning: ssl.PROTOCOL_TLS is deprecated
    context = SSLContext(PROTOCOL_TLS)

tests/test_threading.py: 35 warnings
  /private/tmp/greenletio/src/greenletio/green/threading.py:246: DeprecationWarning: There is no current event loop
    self.task = asyncio.ensure_future(bootstrap())

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================================== 34 passed, 81 warnings in 2.11s ============================================

FWIW they are softening their policy on get_event_loop to not raise a DeprecationWarning if there is an installed loop that is not running, in Python 3.12: https://github.com/python/cpython/pull/99949

miguelgrinberg commented 1 year ago

@blink1073 you are using an old release of Python 3.10. Version 3.10.11 and the just released 3.10.12 do not generate this warning. See https://github.com/miguelgrinberg/greenletio/actions/runs/4775124945/jobs/8489212146 for build results on Python 3.10.

blink1073 commented 1 year ago

Ah, right you are! Apologies for the noise, despite following and engaging on the discussion on get_event_loop, I continue to be confused about the state of play.