Skip to content

Conversation

@dwskoog
Copy link
Contributor

@dwskoog dwskoog commented Dec 18, 2025

Tornado deprecated and changed semantics around their IOLoop in major ways in 5.0, 6.2 and 6.3:

  • in 5.0, they removed the distinction between "current" and "instance" loops and deprecated IOLoop.*_instance as they are just aliases for the analogous _current call.
  • in 6.2, they deprecated the IOLoop methods for manipulating the active loop and recommended using the builtin asyncio.set_event_loop call to establish the operant loop. Note that in Python 3.13+ this is bad advice as written because asyncio.set_event_loop requires the loop instance to be an asyncio.AbstractEventLoop and Tornado's IOLoop is not. If you want to manually install it, the correct call seems to be asyncio.set_event_loop(my_io_loop.asyncio_loop) but this largely unnecessary as IOLoop is mostly just a wrapper around the default loops and gets things in the right place when its API is used.
  • In 6.3, they changed the default value of make_current on the IOLoop constructor to True instead of False so calling IOLoop() greedily tries to establish itself as the loop of the current thread. This invalidates the intended semantics behind core.py:get_io_loop for binding to the new thread.

Once I removed the deprecated calls to Tornado APIs and fixed the default parameters (all test plumbing except the noted get_io_loop), this change in asyncio itself popped up:

  • Python 3.12 deprecated summoning a new loop via get_event_loop and in 3.14 that is a runtime error. This required making test_core.py:test_timed_window_timedelta into a coroutine and running it via pytest-asyncio explicitly to get a loop in place as calling get_io_loop(asynchronous=True) calls IOLoop.current() which is just a wrapper around asyncio.get_event_loop()

https://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.clear_instance
clear_instance is now an alias for clear_current and has been deprecated
since Tornado 5.0
Tornado 4.2 add an optional keyword to IOLoop(make_current=False)
Tornado 6.3 changes that to make_current=True so the default behavior is
to install the loop as the current thread's loop.
IOLoop.clear_current is deprecated since Tornado 6.2
If proactive manipulation of the running loop is required, the API docs
say to use asyncio.set_event_loop()
* Go back to IOLoop(make_current=False) to avoid proactively starting
  the loop when the underlying asyncio system is not ready.
* Turn streamz/tests/test_core.py::test_timed_window_timedelta into a
  coroutine as the underlying event loop needs to be live.
@martindurant
Copy link
Member

Thank you for the deep digging here! From your description, I was worried it would be more involved code changes than it actually is. One thing that you noticed here, and I think it indeed in the documentation, is that asynchronous=True should only be used from within asynchronous code; and we could enforce this. fsspec faces a similar situation.

I had spent some effort in the past converting tornado->asyncio, but there has been a decent amount of leftover legacy code left in the package. There is no reason to have tornado in here at all these days.

@martindurant martindurant merged commit 3677648 into python-streamz:master Dec 18, 2025
7 checks passed
@dwskoog dwskoog deleted the python_312_event_loop_warnings branch December 18, 2025 17:41
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

Successfully merging this pull request may close these issues.

2 participants