Skip to content

Regressions from Python 3.14.0a1 to 3.14.0a2 #384

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

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).

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants