TonIO, a Multi-threaded Async Runtime for Python
Episode Deep Dive
Guest Introduction and Background
Giovanni Barillari, who goes by Joe, is an Italian software engineer living in Vienna, Austria. He works as a programmer at Sentry, but his academic background is in physics, a fact he describes as "pretty peculiar" for his line of work. He has been contributing to open source, mostly in Python, for close to 20 years, and he started contributing to open source projects before he ever worked as a professional programmer. He now maintains what he admits are "too many packages" for the energy he has.
Joe is best known in the Python world as the creator of Granian, the Rust-based WSGI, ASGI, and RSGI application server that powers Talk Python's own web apps. He also built Emmett, a Python web framework, and rloop, a Rust-based asyncio event loop. His newest project, TonIO, is the subject of this episode: a complete asynchronous runtime for free-threaded Python, written from scratch and inspired by Rust's Tokio. He presented TonIO at EuroPython 2026. Joe also co-hosts a web development podcast on YouTube with Marcelo Trilesinski, the maintainer of Uvicorn and Starlette.
- github.com/gi0baro
- blog.baro.dev
- github.com/gi0baro/tonio
- github.com/emmett-framework/granian
- github.com/emmett-framework/emmett
- github.com/gi0baro/rloop
- youtube.com/@thewwwpod
- sentry.io
What to Know If You're New to Python
This episode assumes you have some sense of how Python runs code concurrently and why that has historically been limited. You do not need to have written async code yourself, but a few mental models will make the conversation much easier to follow.
- The GIL (Global Interpreter Lock): For most of Python's history, a single lock inside the interpreter allowed only one thread to execute Python bytecode at a time. That is why Python programs traditionally could not use more than one CPU core from a single process, and why this episode keeps coming back to running many copies of the same app.
- Free-threaded Python: Starting with Python 3.13 as an experiment, and considered stable in Python 3.14, there is a build of CPython without the GIL. Installing it is as simple as asking uv for Python 3.14t. TonIO only runs on this build, so understanding that it is a separate interpreter variant is essential.
- The asyncio event loop and async/await: Async Python lets one thread juggle many waiting operations, like network calls, by suspending at each await and resuming when data arrives. The event loop is the scheduler that does that juggling, and asyncio is the standard library implementation. Joe's whole argument is that no matter how fast that loop becomes, it is still one thread on one core.
- Threads versus processes: A process is a separate copy of your program with its own memory. A thread runs inside a process and shares memory with other threads. Python's answer to the GIL has been to run many processes, which multiplies memory usage. Free-threading makes real shared-memory threads viable.
- Application servers and WSGI/ASGI: In Python, the web framework, like Flask, Django, or FastAPI, is separate from the server that speaks HTTP. WSGI and ASGI are the protocols that let a server like Granian talk to your framework. Granian comes up early in the episode as the lead-in to TonIO.
Key Points and Takeaways
TonIO is an async runtime rebuilt from scratch for free-threaded Python
TonIO is Joe's answer to a question he asked himself while optimizing rloop: if asyncio had never existed, what would an asynchronous runtime in Python look like? Three or four weeks after starting from a blank whiteboard, he had a complete alternative runtime running on multiple real threads. The design is inspired by Tokio, Rust's dominant async runtime, and by default it starts a work pool with one thread per CPU core, so on Michael's 18-core Mac Studio it runs 18 threads. It only runs on the free-threaded interpreter. If the GIL is present, TonIO raises a RuntimeError and tells you to change your interpreter. The project is in alpha, at version 0.9.14 at the time of recording, but Joe believes the API is close to stabilizing and that few modules or primitives remain to be built. Windows support is the one big missing feature.
A faster event loop does not fix the one-core problem
Michael raised a common objection: doesn't uvloop already solve async performance? Joe's answer is that uvloop, rloop, and Marcelo Trilesinski's new Zig-based zuvloop all optimize hot parts of the cycle, and you should absolutely install one because that is free performance. But they do not change the shape of how things work. The best case is roughly a 20% gain on raw throughput in benchmarks that do nothing but TCP, and real programs do a lot more than that. Michael's framing was blunt: one thread on his 18-core machine is about 5% of the capacity, so it hardly matters whether that is 4.9% or 5.1%. Joe reached the same conclusion while working on rloop. Once the remaining CPU time was all spent in asyncio primitives on one thread, there was nothing left to optimize.
Free-threading is the biggest change in Python's history, and adoption is lagging
Joe calls free-threaded Python "the biggest thing ever happened into Python" in its 30 years, bigger than the Python 2 to 3 transition and its Unicode changes. Every Python room in the world has argued about the GIL, and after roughly 15 years of talk, including Larry Hastings' Gilectomy effort, it finally landed. It was experimental in 3.13 and is considered stable in 3.14. He does not understand why people are not more excited, though he knows the main blocker: every native C extension has to be made thread safe, and for older extensions that is not easy. Getting the interpreter itself is trivial with uv. Joe's one frustration is that there is still no official free-threaded 3.14 Docker image, which has been the case for a year and a half.
- docs.python.org/3/howto/free-threading-python.html
- peps.python.org/pep-0703
- peps.python.org/pep-0779
- py-free-threading.github.io
- docs.astral.sh/uv
- hub.docker.com/_/python
The real cost of the GIL is memory, not compute
Michael connected this to his recent blog post on cutting Talk Python's web app memory by 31%, where he took the Talk Python Training app, about 178,000 lines of Python, from about 1.28 gigabytes down to 450 megabytes using lazy imports, a switch to async, and other techniques. The root problem is that Granian, like every Python server, has to replicate your app across processes or interpreters to use more than one core. If a process takes 500 megabytes and you need four of them, that is two gigabytes just to run your code, and memory is now the most expensive resource on servers. Joe sees the same at Sentry, an 18-year-old Django monolith that needs four processes and four to six gigabytes per pod to use a single CPU. Multiplying processes also multiplies every in-memory cache and breaks observability, since a Prometheus SDK ends up tracking four separate instances.
asyncio is overcomplicated, and TonIO has far fewer primitives
Joe's first realization while building rloop was that asyncio is overcomplicated. At his PyCon Italy 2025 talk he asked the audience to explain the difference between a protocol and a transport. The official docs try to explain it three different ways, and in his view, needing three explanations for one concept means something is deeply wrong. Coroutines get wrapped in tasks, tasks become handles inside the loop, and there are events and futures on top. None of that surfaces when you write ordinary async code, but you need all of it the moment you debug something. He cites the Zen of Python: simple is better than complex. TonIO deliberately ships far fewer primitives, and code that relies heavily on asyncio tasks or futures will not port directly because those concepts do not exist in the runtime.
Spawn runs immediately, and there are two ways to write TonIO code
The core API difference from asyncio is spawn. In asyncio, launching an operation and waiting for its result are bound together unless you explicitly create a task. In TonIO, calling spawn starts the coroutine immediately, and you can fire and forget, await it right away, or park the result and join later. Joe's documentation example spawns two number-crunching functions, awaits a third, then awaits the first two, and the only truly parallel work is the part you are waiting on. There is also spawn_blocking for the separate blocking pool. For entry points, you can configure everything through tonio.run or just put the tonio.main decorator on your function and be done, which Joe contrasts with JavaScript where async is the default with no decisions. TonIO also supports two syntaxes. Because async/await is generators underneath, Joe found supporting a plain yield syntax was minimal extra work, so people who dislike writing async and await everywhere can use yield instead. Both are still "colored" in the sense that callers must also be coroutines or generators.
Two rules make multi-threaded Python manageable
Joe's advice for anyone moving to multi-threaded Python comes down to two rules. Rule one: never await inside a threading lock. If you take a threading lock and then await a coroutine inside it, you deadlock TonIO, because thread locks and async locks do not talk to each other. Since Python uses the with statement for locks, this mistake is easy to spot. Rule two: watch out for shared state. Dictionaries and lists are thread safe in free-threaded Python, but that does not mean your program is correct, since another thread can write to the same location between your write and your read. Use a TonIO async lock when you need one, and use locks whenever a sequence of operations on shared memory must be atomic. Michael added that people wrongly believed the GIL saved them from thread considerations. Joe's refinement is that asyncio's hidden feature was that only one coroutine ran at a time, which made threading locks effectively a no-op. TonIO removes that safety net.
Context variables are opt-in for now
Thread locals let you keep per-thread "global" state, such as the current request, without passing it through every function. Context variables do the same for async code, so when a coroutine suspends and resumes it gets the right object back rather than a mix of state from other coroutines. Michael pointed to Flask's request object as the familiar example, and Joe said Emmett copied that idea from Flask. In TonIO, contextvars support is a Boolean passed when starting the runtime, and it currently defaults to off. Joe made that choice because TonIO runs async code across multiple threads, which makes context vars more complicated, and he wants developers testing the alpha to state explicitly that they understand the threading model before relying on them. He expects the default may flip to true once the API stabilizes.
TonIO ships its own time, sync, network, and filesystem modules
TonIO is organized into modules. The time module has timeouts and timers. The sync module has locks, semaphores, and barriers. The network module is where TonIO differs most from asyncio, which has no async socket library of its own and makes you hand standard library sockets to the event loop. TonIO's socket module mirrors the standard library socket interface exactly, except that blocking methods are async, and a streams module offers a higher-level, Trio-like interface. The fs module has async versions of open and mirrors pathlib, so you can import Path from TonIO and get async versions of the methods that need them. There is also a pytest plugin, so tests can be marked to run under TonIO.
The blocking pool is for mixed workloads, and a CPU decorator is coming
Joe explained that the blocking thread pool only matters when your program mixes I/O-bound and CPU-bound work. If it is purely CPU bound, the standard pool already gives you exactly one thread per core, and if it is purely I/O bound the standard pool is fine too. When the two mix, the only way to keep the network side responsive is to spawn the CPU work with spawn_blocking. You do not create or size the pool yourself, and blocking threads are spawned on demand, with a configurable maximum. Concurrency inside the pool is controlled with barriers or semaphores. Michael suggested a decorator that marks a function as computational so that any caller, even one in another library, automatically gets it scheduled on the blocking pool without having to know. Joe liked the idea and said it will land in TonIO 0.10.
tonio-monkey and a growing ecosystem bridge the asyncio gap
Existing asyncio libraries such as httpx are not compatible with TonIO, so Joe published tonio-monkey, which monkey patches popular asyncio-native libraries. Today it covers psycopg, httpx, Redis, and FastAPI, with a Django patch in progress after Joe met Carlton Gibson at PyCon Italy. Joe is asking listeners who try TonIO on an existing codebase to open issues for any package they want patched. He has also started a contained ecosystem: httpunk, a low-level HTTP library that works with both asyncio and TonIO, punkrec, an HTTP client built on it, and a beta port of Uvicorn that runs on TonIO. Longer term, once the API stabilizes, he hopes to work with the AnyIO maintainers on a TonIO backend, though he is unsure how feasible that is given the missing asyncio primitives.
- github.com/gi0baro/tonio-monkey
- github.com/gi0baro/httpunk
- pypi.org/project/punkrec
- github.com/gi0baro/tonio/discussions
- python-httpx.org
- psycopg.org
- github.com/redis/redis-py
- github.com/encode/uvicorn
- anyio.readthedocs.io
Granian today, and the multi-runtime Granian 3 plan
Granian is an application server for Python, an alternative to Gunicorn, uWSGI, Uvicorn, and Hypercorn, with the HTTP and network layer running entirely outside the interpreter in Rust on top of Hyper. That is why it stabilizes latency under load: a Python-based server adds its own load to an interpreter that is already busy. Michael chose it for the p95 stability rather than raw speed, and it now serves roughly 15 of the 33 containers on the Talk Python server. Granian recently passed Hypercorn in downloads and is used at Microsoft, Google, and Sentry, where all three protocols including RSGI are in use. On free-threaded Python since version 2.0, Granian workers become threads instead of processes, which avoids the socket-sharing dance that generates most of its GitHub issues. The limitation is that each thread has its own event loop, so requests cannot cross thread boundaries and load can become unbalanced. Joe's plan for Granian 3 is a revision two of RSGI that is async-independent and callback based, so the server can support asyncio, Trio, TonIO, gevent, eventlet, and more.
Using AI to generate users before you have users
Joe describes his use of AI as peculiar. TonIO is perhaps 1% AI-written code, and the only piece Claude wrote was the pytest plugin. Everything else he wrote the old way in a text editor with no suggestions. Where AI did help was stress testing. He had Claude port Pi, Mario Zechner's TypeScript coding agent harness, to Python, mirroring every behavior and using TonIO for everything. That project caught what he estimates is 95% of TonIO's bugs, and he has started doing the same across the ecosystem. Michael summarized the pattern as needing users before you have users: ask for a web app with an async database, a terminal app, and so on. Joe was also surprised that frontier models get the TonIO API right most of the time with no training data on it.
Windows support might arrive, but only for deployment targets
Joe was frank that Windows is a pain. It has odd behaviors, including a bug dating to Windows NT 4 where a shared socket becomes blocking, and he no longer owns a Windows machine, having moved fully to macOS and Linux in 2026. The mio library TonIO uses for the event loop supports Windows only in a weird way. A few months ago his answer was never, but after working through it with Claude he thinks there is a way to make mio treat certain file descriptors as TCP sockets, so partial Windows support might arrive by the end of the year. He made no promise. His reason is not to win over Windows developers, who can use Windows Subsystem for Linux. He wants programs built with TonIO to run wherever they get deployed, and today that means Linux and macOS only.
Interesting Quotes and Stories
"This is the biggest thing ever happened into Python since the beginning of Python, like in the last 30 years. Do you remember Python 2 to Python 3 and all, oh, now strings are not bytes anymore and everything is Unicode? That's nothing compared to, hey, the GIL is not there anymore." -- Giovanni Barillari
"Finally I have the language I love the most, not because of the structure, not because of types or how it's designed, but because of this awesome community that only Python has. And so finally I can use this language with actual threads." -- Giovanni Barillari
"I just bought a Mac Studio Max, which has 18 cores. If I write one thread, that's about like 5% of the capacity of that machine. So what does it matter if it's 4.9% or 5.1%? I want 95% capacity." -- Michael Kennedy
"If you need three different ways to explain the same thing, something is deeply wrong." -- Giovanni Barillari, on asyncio's protocols and transports
"Let's just assume for a second asyncio never existed, and I want to do async stuff in Python. What does that look like? Is that hard? And three or four weeks after this, I just got a complete alternative asynchronous runtime running on Python with multiple threads." -- Giovanni Barillari
"Rule number one, never await inside a threading lock. Rule number two, you have multiple threads, so if you write state anywhere, you might have race conditions. With those two rules, multithreading is not that hard." -- Giovanni Barillari
"The only thing I'm sure about Granian is that the HTTP stack, that's super safe. I could bet everything I have on that. So if something is wrong in Granian, that's on me, not on Hyper." -- Giovanni Barillari
"It's just like, I need users before I have users. I need these three use cases covered, so give me a web app that uses an async database, give me this terminal app, and so on." -- Michael Kennedy, on Joe's AI stress-testing approach
"It's not about developers. It's about where the software runs at the end of the day." -- Giovanni Barillari, on why Windows support still matters
"We're living in the perfect moment to try stuff. We've been given this big new opportunity of free-threaded Python. We have AI agents. If for any reason you're not happy with asyncio, it's the perfect moment to try new stuff." -- Giovanni Barillari
Key Definitions and Terms
- Free-threaded Python: A build of CPython without the Global Interpreter Lock, so multiple threads can execute Python code truly in parallel. Experimental in 3.13, considered stable in 3.14, and installable with a "t" suffix such as 3.14t.
- Event loop: The scheduler at the heart of async Python that tracks suspended coroutines and resumes them when the I/O they are waiting on is ready. In asyncio it runs on a single thread.
- Coroutine: A function defined with async def, or in TonIO's alternate syntax a generator, that can suspend itself at an await or yield and be resumed later by the runtime.
- Colored functions: The idea that async functions are a different "color" from regular functions, so anything that calls one must itself become async, which is why async code is sometimes called viral. Joe notes TonIO's yield syntax is still colored.
- spawn and spawn_blocking: TonIO's two primitives for launching work. spawn starts a coroutine immediately on the standard thread pool. spawn_blocking runs a function on a separate pool intended for CPU-bound work in mixed workloads.
- Blocking thread pool: A separate, on-demand set of threads in TonIO for CPU-bound or otherwise blocking work, so it does not starve the I/O-handling threads.
- Thread locals: Per-thread storage that lets code keep "global" state, such as the current request, without mixing state between threads.
- Context variables: The async equivalent of thread locals, so a coroutine gets the correct state back after it suspends and resumes. Opt-in in TonIO today.
- Deadlock: A state where threads wait on each other forever. In TonIO this happens if you await a coroutine while holding a threading lock, because thread locks and async locks do not cooperate.
- Race condition: A bug where the outcome depends on the timing of multiple threads, for example writing a value and reading it back while another thread writes to the same place in between.
- WSGI, ASGI, and RSGI: Protocols that let a Python application server talk to a web framework. WSGI is the original synchronous interface from PEP 333, ASGI is the asynchronous one, and RSGI is Granian's own Rust-oriented interface.
- Hyper: The Rust HTTP library that Granian's network layer is built on, used by hundreds of thousands of projects.
- mio: The low-level Rust I/O polling library that TonIO uses to drive its event loop, and the source of the Windows support difficulty.
- Monkey patching: Replacing parts of a library at runtime. tonio-monkey uses it to make asyncio-native libraries run on TonIO.
- POSIX: The family of Unix-like operating system standards. TonIO currently works only on POSIX systems, meaning Linux and macOS.
- Windows Subsystem for Linux (WSL): A way to run a Linux environment inside Windows, which Michael suggested as the escape hatch for Windows developers who want to try TonIO.
Learning Resources
Here are a few places to go deeper on the concurrency, memory, and Rust topics from this episode.
- Async Techniques and Examples in Python: Michael's full course on Python's parallel APIs, covering async and await with asyncio, threads, multiprocessing, task coordination, and thread safety. It is the foundation you want before comparing asyncio to a runtime like TonIO.
- Up and Running with Rust: Granian, rloop, and TonIO are all Rust under the hood. This course takes Python developers from zero Rust to a working PyO3 and Maturin extension, the same stack Joe builds on.
- Python Memory Management and Tips: The multi-process memory multiplication problem is central to why free-threading matters. This course explains reference counting, garbage collection, and how to write code that uses less memory.
- Free-threaded Python HOWTO: The official guide to installing and using the free-threaded build.
- py-free-threading.github.io: Community tracker for free-threading compatibility across the Python ecosystem, useful for checking whether your C extensions are ready.
- TonIO on GitHub: The project itself, with documentation and examples including the spawn-and-await-later pattern Joe walked through.
- Granian on GitHub: The Rust-based server that powers Talk Python and is the lead-in to everything TonIO is trying to do.
Overall Takeaway
For years the Python community treated async performance as an event loop problem, and projects like uvloop and rloop did squeeze real gains out of the loop. While working on rloop, Joe hit the point where the remaining cost was the single thread underneath, and free-threaded Python removes that limit. TonIO is his attempt to build on it directly: a runtime that refuses to run under the GIL, uses every core by default, and swaps asyncio's tasks, futures, handles, transports, and protocols for a few primitives you can keep in your head.
Real threads bring real thread problems, which is why Joe keeps repeating his two rules about locks and shared state. What you get in return matters to anyone running Python web apps. One process can do the work that used to take four, caches no longer have to be duplicated per process, and metrics come from a single place. TonIO is still alpha, runs only on Linux and macOS, and has one maintainer. Even so, it shows what async Python can look like without the GIL, and as Joe said, this is the perfect moment to try stuff.
Links from the show
Giovanni Barillari: github.com
Granian: github.com
Hyper: github.com
Free threaded Python: docs.python.org
Sort of: labs.quansight.org
did a whole course: training.talkpython.fm
uvloop: github.com
rloop: github.com
TonIO: github.com
your EuroPython 2026 talk: www.youtube.com
Michael's Cutting Python Web App Memory Over 31% Article: mkennedy.codes
Watch this episode on YouTube: youtube.com
Episode #561 deep-dive: talkpython.fm/561
Episode transcripts: talkpython.fm
Theme Song: Developer Rap
🥁 Served in a Flask 🎸: talkpython.fm/flasksong
---== Don't be a stranger ==---
YouTube: youtube.com/@talkpython
Bluesky: @talkpython.fm
Mastodon: @talkpython@fosstodon.org
X.com: @talkpython
Michael on Bluesky: @mkennedy.codes
Michael on Mastodon: @mkennedy@fosstodon.org
Michael on X.com: @mkennedy
Episode Transcript
Collapse transcript
00:00 How many cores does your machine have?
00:02 10?
00:02 18?
00:03 Well, your async Python code uses just one of them.
00:07 That isn't a bug in asyncio.
00:09 That's the design.
00:10 And things like optimizing the event loops to be faster by maybe 20% with uv doesn't fundamentally
00:17 change that.
00:17 That's why Giovanni Bariliari started over.
00:21 Joe is the creator of Granian, the Rust-based server that powers Talk Python and all the
00:26 other things we run here.
00:28 His new project is Tone.io, an async runtime written from scratch specifically for free-threaded Python.
00:34 Real threads, a handful of primitives instead of async.io's pile of them,
00:38 and it flat out refuses to even start if the GIL is present.
00:43 This is Talk Python To Me, episode 561, recorded September 1st, 2026.
01:10 Welcome to Talk Python To Me, the number one Python podcast for developers and data scientists.
01:15 This is your host, Michael Kennedy.
01:17 I'm a PSF fellow who's been coding for over 25 years.
01:21 Let's connect on social media.
01:22 You'll find me and Talk Python on Mastodon, BlueSky, and X.
01:26 The social links are all in your show notes.
01:28 You can find over 10 years of past episodes at talkpython.fm.
01:32 And if you want to be part of the show, you can join our recording live streams.
01:36 That's right.
01:36 We live stream the raw uncut version of each episode on YouTube.
01:40 Just visit talkpython.fm/youtube to see the schedule of upcoming events.
01:45 Be sure to subscribe there and press the bell so you'll get notified anytime we're recording.
01:49 This episode is sponsored by Sentry's Seer.
01:52 If you're tired of debugging in the dark, give Seer a try.
01:55 There are plenty of AI tools that help you write code, but Sentry's Seer is built to
01:59 help you fix it when it breaks.
02:01 Visit talkpython.fm/sentry and use the code Talk Python26, all one word, no spaces,
02:07 for $100 in Sentry credits.
02:09 And it's brought to you by us.
02:12 Talk Python and Python Bytes both now have MCP servers.
02:16 Point your AI at 10 plus years of Python episodes, transcripts, and show notes.
02:20 Free. Click MCP in the nav at talkpython.fm and at Python Bytes.
02:25 Hello, hello. Welcome back to Talk Python and me.
02:29 Great to have you here, Joe.
02:30 Hey, thanks for having me. It's nice to get back to the show.
02:33 It's been a while.
02:35 It has been a while.
02:36 And we talked about Granian last time, the Rust-based web application server for Python.
02:42 And I got to just take a moment and say thank you.
02:44 We'll come back to this.
02:45 But thanks for powering Talk Python and all the different things.
02:49 I think across our web apps, I don't know, like 10 million requests a month.
02:52 And it's been flawless.
02:54 So thanks.
02:55 I appreciate you serving up all the stuff for everyone.
02:58 Oh, yeah.
02:59 Thanks.
03:00 It's been a pleasure.
03:00 Like, it's nice to see, you know, when something just works.
03:04 like when you do something and like at the end of the day it just works and people start using it
03:09 like of course like they they ask for a bunch of features you know um but it's nice to see yeah it
03:16 is really nice to see and i think it's i think it's good now it's been a while since you've been on the
03:21 show a couple years i don't i didn't check the exact date when i look back maybe people are new
03:26 to the show or haven't heard your prior episode or just don't remember like give us a quick
03:30 introduction who you are yeah sure so i'm giovanni but everybody can call me joe i'm italian but i
03:37 live in vienna i am working as a programmer in century but i'm a physicist so i think like
03:44 that's pretty peculiar i've been like in the open source ecosystem uh mainly in python um probably
03:53 for the last oh god I think like it's almost 20 years now um and fun fact like I actually started
04:00 like um doing open source or or um contributing to open source projects even before actually
04:07 working as a programmer so um yeah that's me I maintain like too many packages nowadays for like
04:16 the energy I have.
04:20 But yeah, that's pretty much.
04:22 Excellent.
04:22 Yeah, I know how that goes with the packages.
04:25 It's like, oh, here's a cool idea.
04:26 I want to put it out there.
04:27 If it gains no traction, you can shut it down.
04:30 If it gains a ton of traction, you're excited.
04:32 If it gains a little traction, but enough, then you got to keep working on it.
04:35 That's kind of not ideal, right?
04:37 Yeah.
04:37 And especially like nowadays with AI, like the amount of things you, oh, I have an idea.
04:43 Like the chances like to just throw a bunch of agents into that and see what happens.
04:49 It's very dangerous.
04:50 It's very dangerous.
04:51 I have an idea.
04:52 It'll probably take me an hour.
04:52 Let me see if I can validate it.
04:54 Thursday afternoon, you're still like, oh, I'm still almost there.
04:57 It's like, where did my week go?
04:59 What happened?
05:00 It's fun though, isn't it?
05:01 It's a wild time.
05:02 Yeah, it's weird, right?
05:05 Because in a way, we have this very powerful, not super constant tool.
05:14 but it's very powerful and and sometimes just amazing like sometimes like what do you what
05:19 you get back is just amazing and you say like oh okay that's nice that's unexpected sometimes is
05:27 the dumbest thing possible you can you you can get but i guess like the point the main point is that
05:35 i think it it shows up how as humans we have like a complicated relationship with tools
05:43 it can exploit a bunch of weird human behavior sometimes so yeah yeah if you like building things
05:52 you know create you've got ideas and you want to see them come to life it's an unprecedented time
05:56 if if you really your main joy was to be in the code working on the little bits of nuance i can
06:03 see that it's frustrating and uh there's there's a whole there's a whole thing that we go into there
06:07 but that's probably not.
06:08 But wow, what a crazy time, right?
06:09 Yeah, absolutely.
06:11 Yeah, so let's jump into the topics here.
06:14 I think the first thing I want to do is let's just do a little dive into Granian.
06:19 That's why I reached out to you to have you on the show the first time.
06:21 Like I said, it's powering.
06:23 I have on the Talk Python server, there's 33 different containers running a bunch of different things,
06:29 and probably about 15 of them are running Granian.
06:33 Nice.
06:33 Yeah.
06:34 So like I said, it's been really, really cool.
06:37 just tell people what is Granian because I think not only is it interesting as a thing that you've
06:41 done previously and been on the show before it's also an interesting lead-in to where we're going
06:45 with Tonio so what's great yeah sure uh so Granian is an application server for Python so in Python
06:53 for historical reasons um we separated like the application layer from the HTTP layer so when we
07:02 want to serve a web application in Python compared to other languages in which the server part is actually a part of the application.
07:12 In Python, we tend to have a separated package, which is a server, and that server's import your actual application.
07:22 And then we have application protocols to make the server talk with your application and back.
07:31 The two main protocols in Python are VUSGI or WSGI.
07:35 I never really understood how to say that, which is like the original PET333, if I'm not mistaken.
07:45 So it's quite ancient in the Python history.
07:49 And once we had like AsyncIO, a new protocol emerged.
07:54 That's called ASGI or ASGI because, of course, it's the asynchronous server gateway.
08:00 So, Granian is, again, an application server, so you can consider it as an alternative to MicroVoosgi,
08:13 Uvicorn, Hypercorn, and a bunch of other servers that happened during the years.
08:20 And, yeah, the peculiar fact is it's made with Rust.
08:26 So, compared to all the other servers you might use in Python, all the HTTP part or the network layer
08:35 is running completely out of the interpreter, which kind of alleviates the load on the interpreter,
08:46 like on the Python side.
08:48 And depending on the context, it might be more performant or it might stabilize your latency more.
08:58 You know, I think thinking back now, I'm pretty sure that that was the reason I chose Granian.
09:03 Not that it was a ton faster, but the P95 bad side of things.
09:09 Like how much might it slow down under certain weird circumstances?
09:13 It was way more stable than a lot of the other servers, right?
09:17 And that's kind of what you're referring to.
09:19 Yeah, because again, like a normal server which runs everything into Python,
09:23 Like it has this kind of side effects that when your application is starting becoming slow, it kind of becomes like an infinite loop in which like the server adds additional load to the interpreter, which is already like highly loaded from your application.
09:39 And so like Graniac kind of avoids all of that because like even if your application is fully loaded, like all the web and network layer is out of the interpreter.
09:50 It doesn't disturb, let's say the interpreter with that.
09:53 Yeah.
09:53 I do want to just give a little bit of a hat tip to WSGI or WSGI.
09:58 Because, yeah, you pointed out it is old, but it's really, really nice that if somebody is out there running on GUnicorn or they're running on some other server, MicroWSGI, which you should stop running MicroWSGI because it's not supported anymore.
10:14 You don't have to change your code at all.
10:16 You just take your flask or your Jenga or whatever, and you just say, now run here.
10:20 And they just, Grandin talks WizGey or Asggy to that thing, and it's transparent.
10:25 It's beautiful.
10:26 Yeah.
10:26 Yeah.
10:26 I mean, there are like still some, you know, sometimes weird things like, like, Vertzoic
10:34 has some subtleties, sometimes some nuances.
10:37 So I had to like add some environment key just for Verzoic because under some circumstances, if that key is not there, things don't work exactly good.
10:51 So yes, I agree that like having like the two protocols separated and coded like in a specific way gives you this ability to switch the server.
11:02 On the other hand, sometimes, you know, you can be a pain in some time.
11:08 Yeah, for sure.
11:09 Yeah, for sure.
11:11 But yeah, I'd say like we like recently it's getting more popular.
11:18 So I'd say if I'm not mistaken, like a few weeks ago, I think I surpassed like HyperCorn in terms of downloads.
11:28 Awesome.
11:29 And yeah, it's used like in a bunch of different big companies, Microsoft, Google, for sure.
11:38 We use it at Sentry, of course.
11:41 And I think like Sentry is the only company in which we use like all the three supported
11:46 protocols of Granian because Granian also supports like its own protocol, which is named
11:51 like RSGI, I don't know, like RSGI, which is a redesign.
11:56 Risky.
11:57 Yeah.
12:00 um so yeah i'd say it's going pretty good um i think like the last let's say this year was mostly
12:07 into um not not not not mostly about features i think features but mostly about you know making
12:16 the thing more stable and covering like some edge cases um especially again we're using it the
12:22 Sentry. So now like if something is wrong with Grinia, like I have people knocking on my door.
12:28 I bet you do. Well, I mean, with Sentry, Sentry gets an insane amount of like an unimaginable
12:35 amount of traffic. I'm sure with the error reporting side of things and probably a little
12:39 more so these days on the MCP and API side as well with all the agents. I know that I use the MCP
12:46 and absolutely love it. It's been incredible. It's really, really good. I could just say on my
12:50 project hey there's a century error i got what's up like literally that's all i gotta say in clause
12:54 like hold on we're on it and it's like yeah you know five minutes later it's like here's what's
12:57 going on let's work on it you know yeah really good stuff uh i do want to give a quick shout out as
13:02 well uh on this regard to gradient like people are like oh it's it's not as popular as server x or
13:08 whatever right although i think that's starting to fade it's it's got 5 000 stars and a ton of
13:12 people using it and so on yeah but it's also based on hyper from rust which itself has uh where where's
13:19 my numbers, 410,000 projects using it. So it's really tested, right? And obviously the stuff at
13:25 Sentry as well. Yeah. Yeah. Like the only thing I'm sure about Granian is that like the HTTP stack,
13:32 that's super safe. Like that's like, I could bet like everything I have on that. So if something
13:38 is wrong in Granian, like that's on me, not on Hyper, like to be clear. This portion of Talk
13:44 Python to me is brought to you by Sentry and Sear AI. There are plenty of AI tools that help you
13:50 write code, but Sentry Sear is built to help you fix it when it breaks. The difference is context.
13:55 Sear isn't just guessing based on syntax. It's analyzing your actual Sentry data,
14:00 your stack traces, logs, and failure patterns. Because it has the full context, it can
14:05 A, spot buggy code in review and help prevent issues before they happen,
14:10 and B, identify the root cause of production errors.
14:13 It can even draft a fix and hand the work off to an agent-like cursor to open a PR for you.
14:19 Seer turns Sentry into a complete loop.
14:21 You have your traces, errors, logs, and replays to see the problem, and now AI to help solve it.
14:26 Join millions of devs at companies like Claude, Disney+, and even Talk Python,
14:31 who use Sentry to move faster.
14:33 Check them out at talkpython.fm/sentry and use code talkpython26, all one word, for $100 in Sentry credits.
14:42 Thank you to Sentry for supporting Talk Python.
14:45 I recently did a post over on my personal blog calling cutting Python web app memory by 31%.
14:52 And I did a ton of analysis and stuff on how I was running Granian and different things,
14:59 just how I was running Python.
15:00 And I think this is going to be a good lead-in to where we're going as well.
15:03 I got it to where I was using 1.3 gigs, let's say, for Talk Python training the courses specifically.
15:13 And people are like, oh, it's just a little courses thing.
15:15 Like, well, it has 178,000 lines of Python, so it's not a totally small project.
15:20 But down by the end, I did a bunch of stuff on it.
15:24 Lazy imports.
15:25 I switched to async, which is why I brought this up.
15:28 But somewhere down here, I don't know what the final number is.
15:31 Here we go.
15:31 In the end, I got it down to 450 megs from 0.28 gigs.
15:37 One of the main reasons I did that as a way I want to kind of want to lead into like this,
15:41 where we're going is I said, well, the way grading works is for each, either you can make multi-processes that are single threaded or it could do multiple threads
15:49 and then it'll create a separate interpreter for each one.
15:51 Either way, it kind of like replicates out the running.
15:55 It'll create copies in one way or another of your code.
15:59 And that, if you could run one of them, that would be great.
16:01 But why do we have so many?
16:03 We have so many because of the GIL.
16:05 We have poor concurrency.
16:06 It's really hard to handle a lot of load and like actually access, you know, my servers,
16:10 eight cores.
16:11 Like I could only access one core like normally, right?
16:14 And so we do all these things to like explode them out.
16:17 But what is the most expensive?
16:19 Like five years ago, this was true on servers, but really is true now.
16:22 What is most expensive?
16:23 Memory, not compute, right?
16:25 Yeah.
16:25 Yeah.
16:26 Thanks AI.
16:27 Thanks AI.
16:27 Thanks Altman.
16:28 We really appreciate that.
16:32 But we've free-threaded Python now.
16:34 That's a thing.
16:35 And so we're kind of moving into this world where we can take better advantage of the hardware and we don't have to multiply out.
16:44 I know your process takes 500 megs of RAM, but we need four of them.
16:47 So now you need two gigs just to run your code.
16:50 We don't necessarily need that anymore.
16:52 And grading would be a really good foundation for that.
16:54 But let's just think broadly about free-threading and async as it is and stuff.
16:59 I know you've thought a lot about this because of this project that you built.
17:03 Well, what do you think?
17:05 First thing, can I say free threading to me?
17:09 It's super exciting.
17:11 And I don't know why people are not as much excited as me on this.
17:16 Because this is, I'd say, the biggest thing that ever happened into Python since the beginning of Python.
17:25 In the last 30 years.
17:27 do you remember like Python 2 to Python 3 and all like, oh, now strings are not bytes anymore and everything is Unicode?
17:36 Yeah.
17:37 That's nothing compared to, hey, the gil is not there anymore.
17:41 Like we talk about like every Python room everywhere in the world, like at some point talked about the gil and why we have to do to have the gil, right?
17:53 Right.
17:54 The Gilectomy that Larry Hastings had, it seemed to perpetually be working on and never
17:59 kind of went anywhere.
18:00 Yeah.
18:00 It took like 15 years of people talking about this and it finally landed.
18:05 Like last year, yes, of course, last year it was beta because like in Python 3.13, it
18:12 was like, this is an experiment still.
18:14 That was the asterisk on top.
18:18 But since Python 3.14, that's considered stable.
18:22 It's exactly as the normal GIL-included interpreter.
18:30 And at the end of the day, the biggest part of all of this was using reference counting instead of singleton.
18:39 So I guess I'm excited about free-threaded Python because finally I have the language I love the most.
18:49 not because of the structure, not because of types or how it's designed,
18:53 but because of this awesome community that only Python language has, at least in my experience.
19:00 And so finally, I can use this language with actual threads.
19:04 So I'm not obliged anymore to think, oh, wait, we are in Python, so I need to use a C library or something to take advantage of my CPU,
19:14 Especially given, I don't know, we are in the era of, hey, even if you buy a $200 CPU that probably has 12 cores or something.
19:26 So yeah, I'm very excited.
19:29 I'm confused about the lack of adoption.
19:33 I would love to see more adoption in this.
19:35 And of course, like there are reasons, like every native C compiled extension has to be fixed to be thread safe.
19:47 And probably like on a bunch of ancient extensions, that's not simple or easy.
19:55 Yeah, I don't know, man.
19:56 Like I'm just too excited.
19:58 Like I want people to use free thread.
20:00 I know.
20:02 And getting it is so much easier as well.
20:04 it's just uv Python install, you know, that 3.14 T and you got it, right?
20:10 Yeah.
20:11 Yeah.
20:12 And like, I don't know, I'm so pissed at the maintainer of the Docker images
20:16 because we don't have like an official 3.14 free threaded image.
20:20 Like why?
20:21 Why?
20:21 Oh yeah, that is actually a good point.
20:23 Like it has been a year and a half and it just requires putting the T on the uv installed bit.
20:29 Yeah.
20:29 Yeah.
20:30 Yeah.
20:30 Yeah, but again, to my perspective, free-threaded Python opens the window to do a bunch of...
20:36 First, to solve a bunch of the issues you mentioned, which is, for example, in Centri,
20:42 the number one issue is...
20:44 I mean, Centri at the end of the day is this 18-years-old Django monolith.
20:51 And so, yes, we deploy in several multiple different deployments, activating just some parts.
20:59 But even so, like, yeah, we have the same problem, which is, hey, like, in order to use like one C view out of like a VM that GCP gives us, like, yeah, we need to spawn like four different processes and require like, I don't know, four, five, six gigabytes of memory for like a single pod.
21:20 And not only do you have to do that, you also have to, anytime there's some sort of in-memory cache, like an LRU cache or something, every one of those has to be spun up for all four, not just for one, right? There's a lot of powerful sharing.
21:31 Do we want to start talking about observability?
21:34 Because you cannot just use like the Prometheus SDK or anything like that.
21:40 Because again, like then those became like four different instances.
21:43 So you cannot even track like the same metrics unless you do like some weird tricks.
21:48 Like, yeah, relying on the multiprocessing library.
21:54 It's messy, man.
21:54 So like, yeah, I think like FreeThreader like solves a bunch of problems,
22:00 Especially in web development in Python.
22:05 Because everybody says like, oh yeah, they did this just because of AI, you know,
22:09 because they can like make things parallel, whatever.
22:12 I don't get that.
22:14 Like, like we have like this amazing feature and it solves a bunch of problem
22:18 on web development and also like it opens up the a bunch of new opportunities.
22:23 Because again, like one of the most annoying parts in Granian, which is also
22:27 Like one of the things that people opens like the most, the vast majority of issues about
22:33 is because nobody understands like how socket sharing works across different processes.
22:39 And again, like with FreeThreader, like that's just go away if we have something different
22:45 from a Synco.
22:46 And I guess that's the, that's when we get on the point here.
22:52 Because for example, like in Grainium, so Grainium supports FreeThreader Python scenes
22:57 2.0, if I'm not mistaken.
22:59 And so when you use Granian on a free-threaded interpreter, workers become
23:07 threads.
23:08 They're no longer processes.
23:10 And I can skip all the dance of, yeah, open the socket, but just bind on that, but not on listen, because then
23:17 it gets to be shared across processes and so each worker can have his own
23:21 backlog. It's a mess, man.
23:23 So on the free-threaded variant, Like, Grenian already, like, used threads.
23:29 The subtlety is that each thread has its own event loop.
23:35 So what Grenian cannot do today is sharing things between those threads.
23:41 At the end of the day, yes, the application is shared, but, like, every request, like, if a repus comes to a worker, that's it.
23:49 Like, there's no...
23:50 You cannot cross boundaries, right?
23:52 when you await for something, when you suspend for something, that's the loop,
23:57 the event loop you're running into.
23:59 Which is a big limitation for servers.
24:02 Yeah, and one is calling await and another is calling await or maybe doing some work,
24:06 but if they were the same event loop, one thing could run because the other is awaiting,
24:10 but they're actually separate altogether, right?
24:12 Yeah, and also you can still end up in these weird conditions in which maybe, I don't know,
24:19 50% of your connection we're bound to a specific event loop.
24:25 And so you also have like unbalancement between like the different threads.
24:29 So you have like one thread that receives the vast majority of things, the other thread doing nothing.
24:35 Yeah.
24:35 Yeah.
24:36 Let's say like we have new opportunities.
24:39 We kind of lack some libraries, like some libraries behavior that can actually give us like
24:45 all the actual meat here.
24:48 Agreed.
24:49 I want to talk about one area before we dive in, the Tone.io, and that's just asyncio event loops.
24:56 And I know during your presentation, so you gave a talk on this at EuroPython this year,
25:00 and I'll link to the topic right here in the moment.
25:05 But there are people like, oh, well, we have uvloops, so doesn't that kind of solve it?
25:08 And I think that's a really interesting thought because without calling out anybody, I didn't intend to call out anybody.
25:14 I think it just highlights in the Python space, There's just not a lot of thinking about what concurrency means,
25:22 why it matters, how you program for it.
25:25 And that's going to be a challenge.
25:27 I think that's part of the challenge of what you're talking about with the adoption of free-threaded Python.
25:30 But I think also it is a challenge in the sense that thinking about making the event loop faster by, I don't know,
25:37 20% or whatever uvloop actually does, which is great, it doesn't really address the fact that you're still only on one thread.
25:43 Your event loop can access, like, I just, against probably better judgment,
25:47 I just bought a Mac Studio Max, which has 18 cores.
25:51 If I write one thread, that's about like 5% of the capacity of that machine.
25:56 So what does it matter if it's 4.9% or 5.1% depending on which loop?
26:01 I don't care.
26:02 That is broken either way.
26:04 I want 95% capacity.
26:06 Yes.
26:07 Right?
26:07 And that's, I don't know.
26:08 I thought it would be worthwhile to kind of like talk about uvloop a little bit.
26:12 And you built our loop.
26:13 And those things are great in their building blocks.
26:14 Yeah.
26:15 They're not the panacea.
26:16 They're not the fix.
26:17 Yeah, and we also now have Marcelo Trzesitsky, the maintainer of UVCore and Starlet.
26:27 It has recently published Zoovloop, which is another event loop for Python relying on libUV, but it's written in Zig.
26:40 But yeah, as you said, all of these event loops, yes, they can optimize some of the hot part of the cycle.
26:53 But basically, that's it.
26:55 Nothing really changed the shape of how things work.
27:02 And so, yes, for sure, you should actually install uvloop or r loop or zoo v loop or whatever to to speed up like because that's performance you're
27:13 leaving on the table regardless right like so uh it's good to have this project because they also
27:19 like can light uh some of the inefficiencies of a sink io in my opinion um so i think it's still
27:26 good to have these projects and you should use them um but yeah they want like the best you can
27:31 GAT is probably like in the order of like 20% in, and we're talking about like row throughput
27:39 if you're just doing TCP and nothing else, which is nobody does like, like nothing does
27:46 nothing out there.
27:47 Right?
27:47 Like we do a bunch of stuff.
27:49 Yeah.
27:49 If you construct an example where basically all you're doing is waiting very efficiently,
27:53 then you start to see those, those deviate.
27:55 And also if you've got, like, I've got 10 tasks that run over two seconds.
27:59 It does zero.
28:00 It doesn't matter.
28:01 This only matters when you're doing very, very fine-grained work and tons of it,
28:05 and there's lots of switching and that sort of thing, right?
28:08 Yes, yes.
28:11 And so, yes, I guess this was one of my realizations.
28:16 Because, again, I worked on ArtLoop mainly last year.
28:21 And working on ArtLoop made me realize two things.
28:25 The first thing is that asimkayo, it's overcomplicated sometimes.
28:31 It's so fun fact at the presentation I did about that loop in Python Italy 25.
28:40 I had a slide asking to the audience, what's the difference between a protocol and a transport in Async.io.
28:52 Because if you check the official documentation of Python and you get to that chapter, you will find out that they have like to try to explain you that difference.
29:01 they try to do that like in three different ways, which to my experience,
29:07 like if you need three different ways to explain the same thing, something is deeply wrong.
29:14 Definitely violates an Einstein core philosophy.
29:18 Yes.
29:19 Also because like, again, if we think about the Zen of Python, like simple is better than complex, right?
29:27 So that was my first realization, right?
29:30 Because, again, like you have a bunch of primitives, like you have events, futures.
29:37 Oh, by the way, every time they get spawned into the event loop, they get wrapped into a task.
29:44 What is a task? Nobody knows.
29:46 Oh, and by the way, when they actually run inside the event loop, they became handles.
29:52 So we have a bunch of this weird thing that they're not really exposed to you if you just write async code.
30:00 But if you need to debug something when something is wrong, then you need all of that to understand what's going on, right?
30:06 And the other realization, again, was, okay, I reached a point in which there's no other possible optimization in our book.
30:14 Because the vast majority of CPU time spent here is around AsyncIO primitives.
30:21 And I have one single thread.
30:22 And so, like, the only, when I came to realize this, I just said, okay, wait a second.
30:29 Is that, is really that hard to start like scratch whiteboard?
30:35 Like from the beginning, like let's just assume for a second, like asyncio never existed.
30:40 And I want to do like async stuff in Python.
30:43 What that look like?
30:43 Like what, what does it mean to make like an asynchronous runtime in Python?
30:47 Is that hard?
30:48 And I don't know, like, I guess three or four weeks after this, I just got like an asynchronous
30:55 runtime, a complete alternative asynchronous runtime running on Python with
30:59 multiple threads.
31:01 To be clear on free-threaded Python, right?
31:03 Yes, free-threaded Python only because again, to have like real threads I mean, the design is to have
31:09 real threads, so the moment you have the gill, I say, no, no, no, runtime error, change your
31:15 interpreter because this is not good, right?
31:19 This portion of Talk Python I May is brought to you by our AI tools. You know that
31:23 thing where you ask an AI something about Python and it confidently tells you about a library version from 18 months ago? Well, we fixed that,
31:31 at least for our shows. Talk Python and Python Bytes both have MCP servers now. Connect Talk Python
31:37 and your AI can search over 550 episodes, full transcripts, every guest in the entire course
31:44 catalog. Connect Python Bytes and you get almost 500 episodes of Python news going back to 2026,
31:50 including every link we've ever put in the show notes. That means you can say things like,
31:55 ask Talk Python what astral joining OpenAI means for uv or what has Python bytes said about uv
32:02 and get real answers with real links, not hallucination. Name the show in your prompt
32:06 and your AI knows exactly where to look. And if you live in the terminal, Talk Python also has a CLI
32:12 too. One line, uv tool install talk-python-cli and then search episodes, transcripts, guests,
32:21 courses without ever opening a browser. It's open source and it outputs text, JSON, and Markdown,
32:26 so it feeds the AI tools that don't speak MCP yet. And here's the real reason I built it.
32:32 Both shows cover around 10 years of Python history. The people, the decisions, the packages that took
32:38 over, and the ones that quietly didn't. This enhanced access is free. No account, no API key,
32:45 nothing to buy. This history should belong to all of us. Visit talkpython.fm and Python Bytes and
32:52 click the MCP link in the nav bar. Connect them right now to your agents so that they'll be
32:57 accessible anytime you need them in the future. And so yeah, that was like kind of the landscape
33:02 in which I started building Tonio or Tonio. You can pronounce it like however you want. Like I'm
33:09 Italian, so I say Tonio, of course. I think obviously, well, you have to do Tonio.
33:14 So it's really good.
33:16 But as it pairs to asyncio, tone IO also kind of like as a hat to that, right?
33:22 Yeah.
33:22 Yeah.
33:23 But we have Trio.
33:24 So that's not Trio.
33:28 Trio is living there a little bit as well.
33:30 Yeah.
33:30 So this is a really interesting project that you have.
33:33 And it basically says, what if we actually had threads?
33:37 One of the things that's endlessly frustrated me about Python and asyncio and consequently
33:44 I think the language implementation of async await is it's really good.
33:48 I think, you know, the way it sort of turns async code into what looks like structurally
33:54 serializable code or serial code is really, really nice.
33:57 But the, the fact that the developer has to juggle loops and which loop and that loop.
34:02 No, that's not the right loop.
34:04 Like so many times I've been, Oh, I want to do this request.
34:07 Oh, are you using court or an async web framework? You, there is an async loop, but not that one.
34:12 you need the one created by the web framework.
34:14 Like, oh, did you initialize the async database connection before it ran the web thing?
34:19 And like, nope, that's the wrong one.
34:21 And there's all this weird juggling that's just so janky.
34:25 And I'm pretty sure it's janky because we didn't have true threads.
34:29 And the possibility, oh, what if you crisscross this?
34:32 Everything sort of falls apart.
34:33 And if you could just say, look, it's just multi-threaded.
34:35 And there is a thing that called the loop and that's where stuff runs.
34:39 And I don't care who started it.
34:41 It's our loop.
34:41 When I need a loop, the runtime Python itself has a loop for me and I'll use it.
34:47 Right.
34:47 And I feel like you sort of took that philosophy a little bit.
34:49 Right.
34:50 Yeah.
34:50 I'd say like the number one inspiration for Sonio is Tokyo, which is the number one Rust
34:58 asynchronous runtime.
35:00 That's probably because like I spend like in Rust a bunch of time.
35:04 So yeah, like, I don't know, for example, like the weird thing to me is like, we still like,
35:09 if you just want to do like an asynchronous program in Python, like a program, like let's say a script or, I don't know,
35:18 like a simple CLI, we still need to do like a synch.io run.
35:22 Like why?
35:23 Like the, yeah, the amount of things you need to know and decide and stuff there is like completely different
35:33 from other languages.
35:34 Yeah.
35:34 Because again, like think about JavaScript.
35:37 You don't decide anything.
35:38 Like that's async in JavaScript.
35:40 That's true.
35:41 Well, I'll tell you a little bit why you can't just, why you still got to call async run
35:44 because there's like a foundational layer that's not present.
35:48 And it's your job to write the foundation on which asyncio executes, right?
35:53 Like it's still your job to figure, okay, how do I actually create a loop?
35:56 How do I run a loop?
35:57 How do I, like, it's your job.
35:59 You can't just create an async def method and call it because there's nowhere for it to go
36:04 until you go create, you know what I mean?
36:05 There's like, there's just, But the foundation isn't quite there.
36:08 But again, it's an entry point, right?
36:11 So in Tonio, for example, yes, you have Tonio run.
36:14 You want to instruct everything and configure the runtime and do whatever you want.
36:18 Yes, you can do that.
36:19 You don't want to do that.
36:21 You have a single decorator, which is Tonio main.
36:24 You put it on your function, your entry point in your program, and it's done.
36:32 Yeah. All right. Before we talk about that, before we get too much in the weeds of it,
36:37 why don't you just give us like, talk us through writing a program in this. There's also two ways,
36:43 like maybe you could, after you talk about this, you could like sort of get into the,
36:48 what this concept of colored functions versus not, and sometimes call async code viral code. And like,
36:55 let's just like talk through the differences here. Yeah. So, okay. Yes. Tonyo has two
37:02 supports two different syntax modes.
37:08 And that's because, again, when I designed this, given that I just throw everything away
37:15 and started from scratch, I ended up having a system which didn't really require async await syntax
37:23 or a notation because at the end of the day, behind async await notation,
37:28 those are generated, right?
37:30 And so given the effort to support like two different syntax was like minimal, writing everything from scratch, I ended up like leaving up to the final user to decide, hey, you dislike async await temptation?
37:46 There's still plenty of people that like, I don't know why, but they argue like all the time about, oh, you know, like I have to put async everywhere and a wait everywhere, whatever.
37:56 OK, you don't like that.
37:58 Guess what?
37:58 Tonya also have just a yield syntax.
38:01 So instead of await, you just yield from the next coroutine you want to suspend for or wait for.
38:11 And that's it.
38:11 You don't need to write async def everywhere.
38:17 To be here is still colored, right?
38:20 Because the moment you have a generator function, then whatever it calls it before has to be a generator.
38:26 as well. But I guess my point was mainly like, okay, to support the syntax doesn't really take
38:33 that much of a work. And once it was settled in, I mean, it's there and people can decide by,
38:40 hey, you dislike one notation? Sure, just use the other one. But yes, let's say for people
38:48 familiar with AsyncIO, the syntax, like the AsyncAwait syntax is very similar to AsyncIO.
38:55 So you basically have your coroutine, so your sync dev, whatever you want, and you await primitives or stuff.
39:08 So the main difference is that instead of importing stuff from a sync.io, so like, I don't know, sleep or timer or whatever, you import similar primitives from Tonio.
39:21 We, of course, have way less primitives because, again, like, as I said before, AsyncIO has too many primitives.
39:29 And the only big difference from AsyncIO, let's say, native people is that you have, like, spawn methods.
39:40 So instead of saying...
39:43 So in AsyncIO, when you want to make things, like spawn several tasks and then wait for all of them to complete,
39:52 you usually do...
39:53 You have different ways of doing this, right?
39:56 You can have a task set.
39:59 You can have scopes from Trio.
40:04 But I mean, you can use gather so you can create tasks and then gather.
40:10 with SyncIO.
40:10 So Tonio is like, to do this is, you just have like two methods.
40:16 You either spawn asynchronous stuff or you spawn something that is blocking
40:22 and then the primitive is spawn blocking.
40:26 The other major difference from a SyncIO is that when you spawn something
40:31 that gets run immediately.
40:34 Whereas like in a SyncIO this is true only if you create a task.
40:38 Otherwise, everything else is eager.
40:41 So in order to run whatever you want to wait, you need to await.
40:47 So in AsyncIO, the vast majority of operations bound together the launching the operation
40:54 and waiting for the result.
40:55 Unless you create a task that starts immediately and then you await later, right?
41:00 In Tonya, everything is wrapped around the spawn because you can call spawn and forget about it
41:05 or you can await Tonya spawn to wait for the results, right?
41:08 Or you can park the result of the spawn, the join, and join later.
41:12 So that's, I don't know if, like, maybe it's more confusing than, it's hard to explain it simply.
41:20 Maybe, but one of the challenges I've seen with standard asyncIO is you want to create a bunch of work and let it run,
41:28 and then you want to get the answers back.
41:30 So a naive way would be, like, call a bunch of stuff, and then when you call await,
41:36 It's like you've got to create them not started and then start all of them
41:40 and then go through each one and await them.
41:42 Whereas this way, you get a list back and you just await them in order or await one after another somehow.
41:48 They're already started, right?
41:49 And so there's like the skip of this like, okay, I've got all the things
41:52 that are going to become tasks, but I got to turn them into tasks so then I can await them
41:56 because if I regularly await them, it'll still fall back to serial just running on the event loop, you know?
42:01 Yeah.
42:01 And I mean, like in Tanya is the same.
42:02 Like if you await a coroutine, it means like I want to wait that to happen, right? If you don't want to
42:08 just spawn, park the result of spawn into a variable, await later. I think
42:14 there's an example down below in the page about spawning something and awaiting later.
42:24 Somewhere.
42:26 There's a bunch of documentation.
42:28 Spawning there, yeah.
42:30 But yeah, the idea is, and again, I didn't invent anything because the syntax. Yes, this is the example. So in this example, we compute like numbers,
42:41 like stupid example, but it is to give the point. So we start like two functions,
42:46 which computes numbers. Then we await for third function. So we immediately wait for the third
42:53 result. And then we wait for the first two results. So in the meantime, so when you do the first poem,
43:00 those two functions start already, like immediately at that point in your code,
43:06 which kind of makes sense if you think about it, because it's like syntax-based, like you're invoking those coroutines, right?
43:12 In that moment, you just don't await for them.
43:15 And so at the end, like you just await for the parallel, actual parallel result.
43:21 Like everything here is concurrent, but the only point in which you have parallel code,
43:25 the actual parallel code is when you await for parallel, right?
43:30 So that's the major difference from a Synco and Tonio.
43:35 When you spawn stuff, that thing happened in parallel.
43:39 So by default, Tonio starts with the number of threads equal to the number of your CPUs.
43:47 The blocking, Tonio also has a blocking pool for blocking stuff.
43:54 But that's separate.
43:54 Let's say the main work loop that runs your code, by default, you have X threads per CPU cores.
44:05 So if you run this on your M3 Max, M3 Studio Max, what was it?
44:11 You will end up having 18 threads running stuff.
44:14 Yeah.
44:15 Which is kind of what we want in general, right?
44:19 Like if I write a program-
44:20 I'm sure you can configure it, right?
44:22 You could configure the thread pool to say, like, look, this thing has to be a good citizen.
44:26 Let's just take the number of CPUs, divide by two or something like that
44:29 so I can still do other work.
44:30 Yeah, you can like, again, that Tonyo main decorator or the Tonyo run method
44:40 accepts some parameters so you can configure like the size of the standard thread pool,
44:45 the maximum amount of blocking threads you want to spawn because that does get spawned on demand.
44:52 you can configure if you want to have support for context bars because it's not that's another thing
44:58 about python right like we kind of went from thread locals to context bars uh but in thonio
45:07 that's a bit more complicated right because you have async code in multiple threads yeah because
45:12 they used to leverage the thread right yeah so if you want to actually use context bars
45:18 you have to tell to the runtime, right?
45:20 Because otherwise, like, some side effects might be weird in that condition.
45:26 Hold on.
45:28 Nomenclature definition, please, here.
45:30 What are context vars for people who don't know?
45:32 Give us examples.
45:32 Oh, yeah.
45:33 So context vars, so the interpreter has these, every thread into the Python interpreter
45:43 has what it's called a context.
45:47 And so with a sync.io, so what was the problem?
45:51 So we used to have thread locals, which meant like if I have two threads and I have, for example, a request,
46:01 and I have to keep the request state global, like global between quotes in my code,
46:11 but I want to use the correct request in the code and don't make them mix in the two different threads, states,
46:22 we use thread vocals because that's basically like a dictionary, more or less, where you can store that data.
46:30 And that snapshot of data is for a single thread.
46:35 Context var are a similar concept, but for a same code, which means you can store global,
46:43 again, between quotes, global state from an asynchronous code, suspend, and when you get back,
46:53 you have the correct object back instead of mixing global state between different coroutines.
47:00 Was my explanation good?
47:01 Yeah, that's good.
47:02 People are probably pretty common with flask.request.
47:05 Yeah.
47:06 Because you've got a view method or maybe deep down inside some lower part of your program,
47:10 you're like, well, I need to know what the URL was.
47:12 You just say Flask.request, you don't pass it around.
47:14 Like very handy, probably architecturally a bad choice.
47:17 You know, it's hard to like test it out, right?
47:20 And you mock it.
47:20 I don't know.
47:21 Emmet does the same.
47:23 It's the one thing I copied from Flask because I really like it.
47:27 But I think we can do the same argument about database, right?
47:31 Like with a synchronous database, like you have to start a context, Like, async with database, whatever, and then you need to pass that, like, all through.
47:41 Like, a context bar is more handy.
47:43 I don't know.
47:44 Yeah, yeah.
47:45 It certainly unlocks some really interesting things.
47:48 Some extensions do cool things with it.
47:50 So that's what you're talking about.
47:51 But because it's not all just tied to the thread and using asyncio context variables,
47:55 now it's kind of shared in potential ways.
47:57 It's a little bit, you got to opt into it.
47:59 That's right?
48:00 Yeah, CSR.
48:01 Again, like, it's a Boolean when you want to start the runtime.
48:05 And I mean, it's an implementation detail.
48:09 So it is just to be like, I think the big warning at the beginning should have been like,
48:15 Tonyo is very alpha right now.
48:18 So maybe that decision will change in the future because I think right now the default is false.
48:23 That might become true as soon as I stabilize the API.
48:28 But yeah, the idea was if you're testing Tonyo right now, if you're using Tonyo to do some tests right now,
48:34 I want the developer to explicitly state, okay, I'm going to use context bars.
48:41 Because again, the fact you have multiple threads and stuff happening in parallel,
48:49 I just want for the developer to be sure if it's thinking model is correct
48:55 before trying to do some stuff.
48:59 Because again, Tony is multi-threaded.
49:01 multi-threaders is usually like not it's it's i don't know i think for humans multi-threading is
49:08 hard to get sometimes um so that's the trade-off right like uh yes we now have all of these
49:16 capabilities on the other end we need to think about the fact that the sync io always had this
49:22 uh hidden uh let's say a feature which is hey there's only one coroutine running at the time
49:30 Whereas Antonio can have like whatever number.
49:32 Yep.
49:34 Now you still had to manage stuff across await calls.
49:37 So that was, I feel like people felt like the GIL saved them from all thread considerations,
49:43 like locks and semaphores and so on.
49:46 And I don't think it did.
49:47 There's not a guarantee that like a thread couldn't interrupt you.
49:50 It's just less likely to interrupt you, right?
49:53 So like the simple thing is that it did around other primitives.
49:59 So for example, in a SyncIO, it's really hard to deadlock yourself because you're using like a threading lock.
50:09 Because again, like the hidden feature of a SyncIO is you have only one thread working on that shit, right?
50:16 Yeah, exactly.
50:17 Like you can do all the locks you want.
50:19 It's the same thread in the re-entrance.
50:21 Exactly.
50:21 It's effectively a no-op other than it costs CPU.
50:24 Yes, exactly.
50:25 Whereas in Tonio, like that's exactly where people usually make mistakes because now you have the Tonio locking because you have the asynchronous locks, but you also have thread locks, which are two different things.
50:39 And if you, and so like you can deadlock Tonio.
50:43 How?
50:43 Yeah.
50:44 Welcome to multi-threading.
50:45 Yes.
50:45 Like you, you, you, you create, like you enter a threading lock and then you await a coroutine inside the threading lock.
50:52 Now you're deadlocked.
50:53 Yeah.
50:53 Because the two things doesn't speak each other.
50:56 But I guess my advice, if people want to start working in multi-threaded Python,
51:03 my advice is just think about those two rules and then multi-threading is not that hard.
51:11 Again, rule number one, never await inside a threading lock.
51:17 That's a simple rule.
51:18 Also because in Python, you use width lock.
51:22 So that's super easy even to identify, right?
51:26 That's rule number one.
51:28 Rule number two, you have multiple threads.
51:30 So if you want to write into anything, I mean, all the objects are thread safe in free thread of Python
51:37 because a dictionary is thread safe, a list is thread safe, whatever.
51:41 But that doesn't mean like you can screw up in using those.
51:46 Because if you write state anywhere, You might have like race conditions and side effects because you expect to write something and then expect to read that value.
51:58 But in the meantime, you have another thread right into the same location.
52:01 So again, like my advice is rule number one, never use locks, threading locks, and then await.
52:08 If you want to have an async lock, then use an async lock.
52:12 So a Tonio lock for that.
52:14 Rule number two, watch out.
52:16 So you probably need locks when you want to be sure that, like, when you have a sequence of operation, right, on shared memory.
52:25 Those are the two rules.
52:27 I think with those two rules, multithreading is not that hard.
52:31 Yeah, agreed.
52:32 So does Tonio come with its own dedicated lock and synchronization primitives?
52:38 Yes, there's an entire sync module.
52:41 So Tonio has different modules.
52:46 Tonya time, which contains time primitives, like timeouts, timers, etc.
52:52 It has async module, which contains all the synchronization primitives, so logs, semaphore, barriers, etc.
53:01 It has the network module, which is one providing like sockets.
53:06 So that's quite a big difference from a sync.io.
53:10 So in a sync.io, we don't have like an async socket library.
53:16 So if you think about the standard lib socket module, Tonio, AsyncIO needs you to use that
53:25 and then pass the sockets to the event loop and then handle the socket through the event.
53:31 Whereas in Tonio, there's a network module and inside that module, there's a socket module,
53:36 which has the same exact interface of the standard lib plus socket.
53:40 The only difference is, of course, every async method is async in Tonio.
53:47 there's the streams module inside the network module which is the high level
53:53 trio like interface to make it easy to manage network. It has a file system module which
54:02 contains all the standard open kind of methods that they async with open
54:10 tonio.files.open something like that yeah.fs yes and it also contains like
54:17 mirror of the pathlib, standard lib pathlib, so you can use like frontoniofs import path and that path
54:24 has all the methods that are required to be asynchronous so that's more or less like the
54:33 design I don't think like so right now I'm at tonio 0.9.14 I guess and I don't think
54:44 There's much left in terms of modules and primitives and features.
54:50 The big feature missing is Windows support.
54:52 More on that later.
54:55 But yeah, I'd say I'm close to stabilize the API.
55:02 So right now, again, this is all alpha.
55:05 That's mostly because there's only me working on this.
55:08 And I have Fable working on a bunch of other things, which I guess like if I can digress for a second,
55:17 I think like I have a very peculiar and unique way of using AI because right now I'm like,
55:22 and I guess like it's a good use case to advertise.
55:26 So the entirety of Tonio, so Tonio has maybe 1% AI written code.
55:32 The only code that was written by Claude was the pytest plugin.
55:38 Oh, by the way, we also have a pytest plugin.
55:40 so you can hide the smart tone.
55:45 But that's the only part that I let AI touch.
55:49 Everything else, like it's old school.
55:52 I written all of it like the old way, text editor, not even any suggestion and everything.
56:02 What I used AI for, and it helped me a lot into reaching a state in which I think Tonio is really stable now
56:13 was to use AI to build projects that were using Tonio and stress test everything about Tonio.
56:21 So for example, I made a port of Pi, the harness from Maria Zeckner.
56:28 So I made Claude rewrite the whole thing in Python because Pi is written in TypeScript, regardless of the name.
56:37 So I made that entire harness with Claude saying, okay, take five, meter every behavior and just use Tonio to do everything.
56:47 And that like helped me like catch like probably 95% of the bugs in Tonio.
56:53 And I kind of started doing the same for the ecosystem.
56:57 Maybe we can talk about it a bit later, but yeah, that's also like a super,
57:02 it's super useful for me, right?
57:04 Like as a solo open source developer, which, yeah, I want to focus on Tonio.
57:09 I don't have time to do all the other packages.
57:12 And so, yeah, that was nice.
57:13 That's a very interesting way.
57:14 I hadn't really considered that.
57:16 It's just like, I need users before I have users.
57:19 I need these three use cases covered.
57:21 So give me a web app that uses an async database.
57:24 Give me this terminal app and so on.
57:27 And that's, yeah, that's really cool.
57:28 Yeah.
57:29 Okay.
57:30 I agree.
57:30 Ecosystem is interesting.
57:31 I want to talk about it.
57:32 But before that, I want to come back to blocking threads.
57:35 Yes.
57:35 So blogging threads are CPU bound generally type of things.
57:39 Is that right?
57:40 So I think Tonio is a bit peculiar in this regard because it really depends what your program does.
57:48 Because if your program, whatever that is, has mixed load, which means like you have both IO bound stuff,
57:58 like network, disk, terminal, whatever, and CPU bound stuff, then the only way to be sure that everything keeps running smoothly,
58:10 like on the network part, the IO part, is to spawn the CPU bound stuff on the blocking thread pool.
58:19 But if your program is just CPU bound, then who cares?
58:24 Just use the normal thread pool because then you don't have anything to block.
58:32 like your CPU bound, your CPU limited.
58:35 So the standard configuration of Tonio gives you exactly the perfect number of CPU cores to use.
58:41 On the other hand, if you just have like IO bound.
58:46 So again, like if you have just CPU bound work or just IO bound work, you can use like the standard pool,
58:53 nothing particular like this.
58:55 The blocking thread pool is useful only when you have mixed workloads and you need to balance them, right?
59:01 Okay, how do I create such a thing?
59:04 How do I start a task in one or the other?
59:07 So every await or yell destruction or a tonio spawn destruction runs on the standard thread pool.
59:16 To spawn something on the blocking thread pool is just tonio spawn blocking.
59:21 Done.
59:21 You don't need to manage the size of the pool.
59:23 You don't need to create the pool.
59:27 If you want to control concurrency in the blocking threat pool, you either use, like, barriers when you spawn stuff
59:38 or semaphores when you spawn stuff.
59:41 But, yeah, like, in general, it's not different from spawning anything else.
59:46 Sure.
59:46 So you can spawn blocking.
59:48 Here's what I was thinking.
59:49 Here's what I was getting at.
59:51 That I think would be a sweet feature.
59:53 You've got at tonio.main for the entry point, right?
59:57 Yes.
59:58 So that runs on tonio.run instead of just Python run or whatever.
01:00:02 For functions that you know are computational, you would like to ensure that they run on a blocking thread
01:00:08 or along those lines.
01:00:10 You could put a decorator that just says tonio.cpu or whatever.
01:00:15 I don't know.
01:00:16 You come up with a name, but that way I don't even have to think.
01:00:19 If I import another library also built on tonio, I don't know, oh, that function wants me to run it this way.
01:00:25 No, I just call it.
01:00:26 Antonio goes, ah, this is an async CPU one.
01:00:29 So we'll scale it up, scaffold it up on that, basically.
01:00:33 That's a nice suggestion, okay?
01:00:36 All right.
01:00:37 I think it will land in Antonio 0.10, yes.
01:00:40 Awesome, okay, cool.
01:00:41 I like that.
01:00:43 When you're in the function, the locality of the information that, oh, this is computational and so on is right there.
01:00:49 But when you're calling, and especially in a big app, you don't know, right?
01:00:51 So I think that would be really cool.
01:00:53 So I'm glad you like it.
01:00:54 No, we'll work on that.
01:00:55 Awesome.
01:00:56 All right, we got time for two really quick things.
01:00:59 So one thing that is nice about standard asyncio is everyone uses asyncio
01:01:03 if they're writing async code at the moment, unless they do something like Trio
01:01:06 or one of these other things.
01:01:08 So for example, if I use HTTPX2 or HTTPX and I, with async, you know, async with create a client of it
01:01:16 and I await its get, that runs on the same sort of deal.
01:01:20 but it's probably not compatible with Tonio, is it?
01:01:23 And if it's not, what do I do?
01:01:24 Yeah, it's not.
01:01:28 So I have one thing that is the current state, and I also have the idea for the future.
01:01:35 So right now we have a package which is called TonioMonkey, and you probably can guess what it means, what it does.
01:01:43 It basically like monkey patches popular, Or I would either say, like, asyncio native libraries I thought about in the last few months.
01:01:56 That might be useful.
01:01:59 And so I guess today we have, like, TonyaMonkey can monkey patch async.
01:02:05 No, sorry.
01:02:06 PsychoPG.
01:02:08 It can patch HTTPX and HTTPX2.
01:02:12 It can patch Redis.
01:02:15 And I think that's most of it for the moment.
01:02:17 But I'm open for issues.
01:02:21 If anyone from the listeners want to try Tonio to an existing code base and you have a package you would like to see monkey patched,
01:02:33 just open an issue in the TonioMonkey repo or even in Tonio discussions, and I will look at it.
01:02:41 Because again, right now I need the vast majority of possible use cases to see that I covered everything.
01:02:50 Ideally, as soon as I stabilize the API and I say, okay, this is the thing,
01:02:56 my idea was to try to contact the NEIO maintainers to see if we can have NEIO backend in NEIO,
01:03:13 which should be like a way easier way to deal with this.
01:03:20 I'm not sure how much it's feasible because again, like the main problem for Tonio right now is that it doesn't support any of the
01:03:27 synchio primitives.
01:03:28 So if any code base like relies a ton on task or futures or whatever, that's not in the runtime.
01:03:36 Yeah.
01:03:37 Yeah.
01:03:37 Okay.
01:03:38 Interesting.
01:03:38 Interesting.
01:03:39 But there are some choices.
01:03:40 And are you also creating your own?
01:03:42 I saw some custom HTTP or some other library or two.
01:03:48 I'm also trying to be a very contained ecosystem of libraries, mainly on the web part.
01:03:55 So right now, I published a low-level HTTP library for Python that is compatible both with Asyncio and Tonio.
01:04:07 It's called HTTPunk.
01:04:09 I published an eye-level client, HTTP client, on top of that, which is called Punk Rec.
01:04:19 So again, you can use that regardless of Tonio Asincaio, because the adaptation layer is inside the package.
01:04:27 I kind of published a very beta experiment of a Uvicorn port to work in Tonio,
01:04:38 Which I guess brings up a question like, hey, when will Granian support this?
01:04:45 Am I right?
01:04:46 Yeah, right.
01:04:47 So I have plans.
01:04:50 It's actually way bigger plans for Granian.
01:04:53 So as I said before, 2026 was a bit boring in Granian.
01:04:58 Like, no, a bunch of new features, not a bunch of that stuff.
01:05:02 But that's just because several months ago, I started designing two things for Granian.
01:05:08 So the first is this revision two of the ArchGee protocol, which will be async independent.
01:05:17 So it will become like a callback protocol because the other big feature I want to add in Granian
01:05:26 is to support a bunch of runtimes.
01:05:29 So my idea is not the Granian tree, when it will land, no promises on the time here,
01:05:36 but like when it will land, Ideally, it will support Asyncio, Trio, Tonio, gEvent, eventlet, and a bunch of stuff.
01:05:45 So that's the idea, is to isolate, let's say, the protocol implementation and the Python runtime implementation.
01:05:55 So it will happen one day.
01:05:59 In the meantime, I wanted to have a test server to use with Tonio and see what happens.
01:06:04 So that's why.
01:06:07 Yeah, using something like Cord or FastAPI or something like that.
01:06:11 If you were able to run it in Granian with the Tonio, Tonio backend, the async
01:06:17 endpoints you write would be running on Tonio, yeah?
01:06:20 Yeah, that's the plan.
01:06:22 In TonioMonkey, there are also patches for FastAPI, by the way. And I'm working
01:06:27 on a one on Django.
01:06:28 I need to ping Carleton on that because we met this year at this year, PyCon Italy,
01:06:36 and we talk about it.
01:06:39 So maybe in a month or so, we will also have like a patch for Django.
01:06:44 Sweet.
01:06:45 Yeah, I just had Carlton on to talk about basically all the async work in Django 6 and 6.0 and so on.
01:06:55 So that was fun.
01:06:56 I watched the episode and I was like, huh, sounds familiar, huh, huh.
01:07:01 Yeah, we've talked about that.
01:07:03 I see.
01:07:03 Interesting.
01:07:04 All right.
01:07:04 Well, I got one final main topic for us.
01:07:07 Yep.
01:07:08 Oh, yeah.
01:07:08 What about windows?
01:07:12 So.
01:07:12 And I'm not putting pressure on you to do it.
01:07:15 No, no, no.
01:07:16 I'm just rounding out the conversation.
01:07:18 Okay.
01:07:18 So if you ask me like a few months ago, my answer would probably be like that short video
01:07:26 extract from a series like where you had the actor.
01:07:32 Whatever.
01:07:33 um you know i'm gonna not gonna not gonna swear on the podcast but uh uh you get what i mean
01:07:41 uh so i i'm not so first of all like the usual problem so supporting windows in granian
01:07:49 it's a pain man like a real pain like i'm not joking right because it has like all these
01:07:55 quirk behaviors not just in python not just in terms of threads like do you know that win that
01:08:02 that windows have as like a bug from i think it was like nt4 uh that like if you if you share a
01:08:11 socket it becomes blocking and so you cannot use it like anywhere um so windows for start like
01:08:18 windows is a pain and is a very weird operating system second i don't have like a windows environment
01:08:27 because finally in 2026, I was able to finally have just macOS and Linux
01:08:32 on all my machines, also for gaming.
01:08:35 So I'm so happy.
01:08:37 So those are, like I say, the two main preambles on Windows support.
01:08:44 But also because implementing the library I use Antonio to manage the Polar,
01:08:52 the actual event loop, It supports Windows, but you know, weird way.
01:08:58 So, months ago I was like almost a hundred percent convinced now, never windows, never now, thanks to fable by chatting with fable.
01:09:09 I think we found a way to hack into, into meal, this library to, to fake
01:09:18 some stuff about windows.
01:09:20 So it will interpret some stuff as TCP sockets.
01:09:24 even if they are like file descriptors or anything.
01:09:28 So I won't make a promise here because then I have to do it.
01:09:34 But ideally, end of the year, we might see partial Windows support in Tom.
01:09:41 Sweet.
01:09:41 So that's the idea right now.
01:09:44 I still hate Microsoft, to be clear here.
01:09:47 Like, man, I just like Windows.
01:09:51 So if any of the listeners, like if you're programming in Windows and not using Weasel,
01:09:59 please put down in the comments why.
01:10:01 Like, why are you self-inflicting this to you?
01:10:05 Like, explain me the rationale into dealing with all the BS that Windows 11 is nowadays.
01:10:14 So yeah, but anyways, I will try end of the year to have some form of Windows support in time.
01:10:21 I can tell, I can sense your excitement.
01:10:23 My Windows computer over there is Windows 10, by the way.
01:10:26 But I do think for people listening, Windows Subsystem for Linux is an escape hatch
01:10:30 that you can do, right?
01:10:32 If you had a project, you really want to use this here, like set up Windows Subsystem for Linux
01:10:35 and run it there, right?
01:10:36 To be here, like the only reason I want to, like I want to try to add Windows support in Tonio
01:10:42 is not for developers, is actually to provide developers a way to say, okay, I want to build a program in Python
01:10:50 with Tonio that has to work like everywhere.
01:10:53 Because right now, again, like if you code anything in Tonio, it works only on POSIX systems,
01:10:58 so only Linux and macOS.
01:11:00 So that's the reason why I want to have at least some form of Windows support
01:11:04 because, I mean, it's not about developers.
01:11:07 It's about like where the software runs at the end of the day.
01:11:10 Right, right.
01:11:10 It's the deployment targets, yeah.
01:11:12 Yeah.
01:11:13 All right, Joe, final call to action.
01:11:14 People have been listening and they're like, this sounds pretty excellent.
01:11:17 I want to try it.
01:11:18 What do you tell them?
01:11:19 I mean, like, again, I think we're living in the perfect moment to try stuff.
01:11:28 We've given with this big opportunity, big new opportunity of free-threaded Python.
01:11:35 We have AI agents.
01:11:38 And by the way, I was surprised, but like pointing any agent, any model,
01:11:44 frontier model to a project and say, okay, implement this in Tonio.
01:11:48 I was surprised.
01:11:49 They got all the API correctly most of the times, which is amazing.
01:11:54 There's no training data about Tonio.
01:11:56 And regardless, it was able to drive through stuff.
01:12:02 So I guess it's the perfect moment to try stuff.
01:12:06 I'd say if for any reason you're not happy with Asyncio, if for any reason you experience any of the pain points we talked about,
01:12:18 I think it's like the perfect moment to try new stuff.
01:12:24 And if you try Tonio and you have like a use case where it's not working or the API can be improved,
01:12:33 like open an issue, open a discussion, ping me, DM me on Twitter.
01:12:39 Oh, sorry, X the everything up.
01:12:41 I will call it.
01:12:44 Ping me and we'll figure out.
01:12:48 Yeah, I think that's my message.
01:12:51 Yeah, awesome.
01:12:52 Well, it looks like a very ambitious project and I think you've done a lot of interesting things.
01:12:57 So thanks for coming on and sharing it.
01:12:58 Tell people where they can stay in touch with you as well.
01:13:01 Yes, so my GitHub handle is G-I-0-B-A-R-O.
01:13:09 You can find me with the same handle Twitter. I have a blog, even if I don't write a ton, but you can find my blog at blog.baro.dev.
01:13:24 And if you want to join me and Marcelo Trilesinski, again, author of Uvicorn, Starlet, and a bunch of
01:13:36 stuff he's making with TokenMaxing, we recently started a podcast available on YouTube.
01:13:44 at the www pod.
01:13:47 Nice.
01:13:47 So people, check them out.
01:13:49 Check out the pod.
01:13:49 That sounds fun.
01:13:50 I'll give it a look as well.
01:13:51 Joe, thanks for coming on the show.
01:13:53 Nice to catch up with you.
01:13:54 Thank you for having me.
01:13:55 It was super good.
01:13:57 This has been another episode of Talk Python To Me.
01:14:00 Thank you to our sponsors.
01:14:01 Be sure to check out what they're offering.
01:14:02 It really helps support the show.
01:14:04 This episode is sponsored by Sentry's Seer.
01:14:07 If you're tired of debugging in the dark, give Seer a try.
01:14:10 There are plenty of AI tools that help you write code, but Sentry's seer is built to help you fix it when it breaks.
01:14:16 Visit talkpython.fm/sentry and use the code talkpython26, all one word, no spaces, for $100 in Sentry credits.
01:14:24 And it's brought to you by us.
01:14:26 Talk Python and Python Bytes both now have MCP servers.
01:14:30 Point your AI at 10 plus years of Python episodes, transcripts, and show notes free.
01:14:36 Click MCP in the nav at talkpython.fm and at Python Bytes.
01:14:40 If you or your team needs to learn Python, We have over 270 hours of beginner and advanced courses on topics ranging from complete beginners
01:14:48 to async code, Flask, Django, HTMX, and even LLMs.
01:14:52 Best of all, there's no subscription in sight.
01:14:55 Browse the catalog at talkpython.fm.
01:14:57 And if you're not already subscribed to the show on your favorite podcast player, what
01:15:01 are you waiting for?
01:15:03 Just search for Python in your podcast player.
01:15:05 We should be right at the top.
01:15:06 If you enjoy that geeky rap song, you can download the full track.
01:15:09 The link is actually in your podcast blur show notes.
01:15:12 This is your host, Michael Kennedy.
01:15:13 Thank you so much for listening.
01:15:15 I really appreciate it.
01:15:16 I'll see you next time.
01:15:27 I'm me.
01:15:29 Get me ready to roll.
01:15:32 Upgrade the code.
01:15:34 No fear of getting old.
01:15:37 We tapped into that modern vibe over Kenny's storm.
01:16:11 Продолжение следует...


