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

Regressions from Python 3.14.0a1 to 3.14.0a2 #384

Closed
musicinmybrain opened this issue Nov 23, 2024 · 3 comments
Closed

Regressions from Python 3.14.0a1 to 3.14.0a2 #384

musicinmybrain opened this issue Nov 23, 2024 · 3 comments

Comments

@musicinmybrain
Copy link
Contributor

Describe the bug

Since Python 3.14.0a2, asyncio.get_event_loop() no longer implicitly creates an event loop when there is none. It now raises a RuntimeError if there is no current event loop. See https://docs.python.org/dev/whatsnew/3.14.html#id3 and python/cpython#126353.

The result is a large number of test failures in python-engineio on Python 3.14.0a2.

To Reproduce
Steps to reproduce the behavior:

$ gh repo clone miguelgrinberg/python-engineio
$ cd python-engineio
$ python3.14 --version
Python 3.14.0a2
$ tox -e py314

Expected behavior
All tests pass, as they do on py313 and as they did in Python 3.14.0a1.

Logs

============================================================================================ test session starts ============================================================================================
platform linux -- Python 3.14.0a2, pytest-8.3.3, pluggy-1.5.0
cachedir: .tox/py314/.pytest_cache
rootdir: /home/ben/src/forks/python-engineio
configfile: pyproject.toml
plugins: cov-6.0.0
collected 488 items                                                                                                                                                                                         

tests/async/test_aiohttp.py ...                                                                                                                                                                       [  0%]
tests/async/test_asgi.py .FFFFFFFFFFFFFFFFFFFFFFFF                                                                                                                                                    [  5%]
tests/async/test_client.py FFFFFF..FFFFFF.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF                                                                                           [ 22%]
tests/async/test_server.py .....FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF...FFFFFFFFFFFFF.FF.FFFFFFFFFFFFFFFFFFFF                                                                                            [ 38%]
tests/async/test_socket.py FFFF.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF                                                                                                                                        [ 45%]
tests/async/test_tornado.py ..F                                                                                                                                                                       [ 46%]
tests/common/test_client.py ..........................................................................................                                                                                [ 64%]
tests/common/test_middleware.py ..........                                                                                                                                                            [ 66%]
tests/common/test_packet.py ......................                                                                                                                                                    [ 71%]
tests/common/test_payload.py ...........                                                                                                                                                              [ 73%]
tests/common/test_server.py ..............................................................................................                                                                            [ 92%]
tests/common/test_socket.py ..................................../usr/lib64/python3.14/ast.py:53: RuntimeWarning: coroutine 'AsyncSocket._upgrade_websocket' was never awaited
  return compile(source, filename, mode, flags,
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
/usr/lib64/python3.14/ast.py:53: RuntimeWarning: coroutine 'AsyncSocket.receive' was never awaited
  return compile(source, filename, mode, flags,
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
/usr/lib64/python3.14/ast.py:53: RuntimeWarning: coroutine 'AsyncSocket._websocket_handler' was never awaited
  return compile(source, filename, mode, flags,
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
/usr/lib64/python3.14/xml/dom/minidom.py:395: RuntimeWarning: coroutine 'AsyncSocket._websocket_handler' was never awaited
  def _get_name(self):
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
/usr/lib64/python3.14/xml/dom/minidom.py:395: RuntimeWarning: coroutine 'AsyncSocket.send' was never awaited
  def _get_name(self):
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
/usr/lib64/python3.14/ast.py:53: RuntimeWarning: coroutine 'translate_request.<locals>.AwaitablePayload.read' was never awaited
  return compile(source, filename, mode, flags,
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
                                                                                                                                      [100%]

================================================================================================= FAILURES ==================================================================================================
______________________________________________________________________________________ AsgiTests.test_engineio_routing ______________________________________________________________________________________

self = <tests.async.test_asgi.AsgiTests testMethod=test_engineio_routing>

    def test_engineio_routing(self):
        mock_server = mock.MagicMock()
        mock_server.handle_request = AsyncMock()

        app = async_asgi.ASGIApp(mock_server)
        scope = {'type': 'http', 'path': '/engine.io/'}
>       _run(app(scope, 'receive', 'send'))

tests/async/test_asgi.py:44:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/async/test_asgi.py:22: in _run
    return asyncio.get_event_loop().run_until_complete(coro)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <asyncio.unix_events._UnixDefaultEventLoopPolicy object at 0x7f8a173d3230>

    def get_event_loop(self):
        """Get the event loop for the current context.

        Returns an instance of EventLoop or raises an exception.
        """
        if self._local._loop is None:
>           raise RuntimeError('There is no current event loop in thread %r.'
                               % threading.current_thread().name)
E           RuntimeError: There is no current event loop in thread 'MainThread'.

/usr/lib64/python3.14/asyncio/events.py:681: RuntimeError
______________________________________________________________________________________ AsgiTests.test_lifespan_invalid ______________________________________________________________________________________

self = <tests.async.test_asgi.AsgiTests testMethod=test_lifespan_invalid>

    def test_lifespan_invalid(self):
        app = async_asgi.ASGIApp('eio')   
        scope = {'type': 'lifespan'}
        receive = AsyncMock(side_effect=[{'type': 'lifespan.foo'},
                                         {'type': 'lifespan.shutdown'}])
        send = AsyncMock()
>       _run(app(scope, receive, send))   

tests/async/test_asgi.py:311:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/async/test_asgi.py:22: in _run
    return asyncio.get_event_loop().run_until_complete(coro)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <asyncio.unix_events._UnixDefaultEventLoopPolicy object at 0x7f8a173d3230>

    def get_event_loop(self):
        """Get the event loop for the current context.

        Returns an instance of EventLoop or raises an exception.
        """
        if self._local._loop is None:
>           raise RuntimeError('There is no current event loop in thread %r.'
                               % threading.current_thread().name)
E           RuntimeError: There is no current event loop in thread 'MainThread'.

/usr/lib64/python3.14/asyncio/events.py:681: RuntimeError

[… omitted many similar failures …]

========================================================================================== short test summary info ==========================================================================================
FAILED tests/async/test_asgi.py::AsgiTests::test_engineio_routing - RuntimeError: There is no current event loop in thread 'MainThread'.
FAILED tests/async/test_asgi.py::AsgiTests::test_lifespan_invalid - RuntimeError: There is no current event loop in thread 'MainThread'.

[… omitted many similar failures …]

FAILED tests/async/test_socket.py::TestSocket::test_websocket_upgrade_with_payload - RuntimeError: There is no current event loop in thread 'MainThread'.
FAILED tests/async/test_tornado.py::TornadoTests::test_translate_request - RuntimeError: There is no current event loop in thread 'MainThread'.
=============================================================================== 205 failed, 283 passed, 22 warnings in 14.07s ===============================================================================

Additional context

This was reported downstream in Fedora, where we are already checking compatibility of packages with Python 3.14.

@musicinmybrain musicinmybrain changed the title Regression from Python 3.14.0a1 to 3.14.0a2 Regressions from Python 3.14.0a1 to 3.14.0a2 Nov 23, 2024
@miguelgrinberg
Copy link
Owner

This is an excellent excuse to moderize the async tests using pytest-asyncio and AsyncMock.

@miguelgrinberg
Copy link
Owner

miguelgrinberg commented Nov 25, 2024

Not sure if the errors on the 3.14 are all gone, but I have cleaned up all usages of get_event_loop except one which I think continues to make sense even going into 3.14.

@musicinmybrain
Copy link
Contributor Author

musicinmybrain commented Nov 25, 2024

Thanks!

I can confirm that tox -e py314 now works for me with a6e5d92 (489 passed, 22966 warnings in 8.48s, with most of the warnings of the form DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead: I opened a follow-up PR for that, #387).

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

No branches or pull requests

2 participants