
About this episode
Talk Python To Me is made possible by:
Episode sponsors
Sentry Error Monitoring, Code talkpython26
Python in Production
Talk Python Courses
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: @[email protected]
X.com: @talkpython
Michael on Bluesky: @mkennedy.codes
Michael on Mastodon: @[email protected]
Michael on X.com: @mkennedy
Get every episode summarized
Each time Talk Python To Me publishes, we email you a written briefing from the transcript — the topics, who appeared, and any specific claims, with the ad reads skipped.
Email me new episodesFree for 3 shows. No card needed.
Hosts & guests
Transcript ready
1,249 searchable segments. Every word is indexed and playable.
Full transcript
Talk Python To Me — #561: TonIO, a Multi-threaded Async Runtime for Python. Machine-transcribed; use the interactive transcript above to jump the player to any line.
How many cores does your machine have? 10, 18? Well, your Async Python code uses just one of them. That isn't a bug in Async IO. That's the design. And things like optimizing the event loops to be faster by maybe 20% with UV doesn't fundamentally change that. That's why Giovanni Barreliari started over. Joe is the creator of Granian, the rest-based server that powers Talk Python and all the other things we run here. His new project is Tonio. An Async runtime written from scratch, specifically for free-threaded Python. Real threads, a handful of primitives instead of Async IO's pile of them. And it flat out refuses to even start if the GIL is present. This is Talk Python May, episode 561 recorded September 1, 2026. Welcome to Talk Python, the number one Python podcast
for developers and data scientists. This is your host, Michael Kennedy. I'm a PSF fellow who's been coding for over 25 years. Let's connect on social media. You'll find me and Talk Python on Macedon, BlueSky, and X, the social links are all in your show notes. You can find over 10 years of past episodes at Talk Python.fm. And if you want to be part of the show, you can join our recording live streams. That's right, we live stream the raw, uncut version of each episode on YouTube. Just visit TalkPython.fm, slash YouTube to see the schedule of upcoming events. Be sure to subscribe there and press the bell. So you'll get notified anytime we're recording. This episode is sponsored by Centres Seer. If you're tired of debugging in the dark, give Seer a try. There are plenty of AI tools that help you write code, but Centres Seer is built to help you fix it when it breaks. Visit TalkPython.fm slash Centres and use the code TalkPython26, all one word in those spaces, for $100 and Centres credits. And it's brought to you by us. Talk Python and Python bytes both now have MCP servers.
Point your AI at 10 plus years of Python episodes, transcripts and show notes. Free, like MCP in the nav at talkpython.fm and at Python bytes. Hello, welcome back to Talk Python to me. Great to have you here, Joe. Hey, thanks for having me. It's nice to get back to the show. It's been a while. It has been a while. And we talked about gradient last time, the Rust-based web application server for Python. And I got to just take a moment and say, thank you, we'll come back to this. But thanks for powering Talk Python and all the different things. I think across their web apps, I don't know, like 10 million requests a month. And it's been flawless. So thanks. I appreciate you serving up all the stuff for everyone. Oh, yeah, thanks. It's been a pleasure. It's nice to see with something just works. Like when you do something and at the end of the day, just works and people start using it. Of course, they ask for a bunch of features. But it's nice to see.
Yeah, it's really nice to see. And I think it's good. Now, it's been a while since you've been on the show. A couple of years, I didn't check the exact date when I looked back. Maybe people are new to the show or haven't heard your prior episode. Or just don't remember. Like give us a quick introduction. Here you are. Yeah, sure. So I'm Giovanni, but everybody can call me Joe. I'm Italian, but I leave him Vienna. I am working as a programmer in century, but I'm a physicist. So I think that's pretty peculiar. I've been in the open source ecosystem, mainly in Python, probably for the last, oh god. I think it's almost 20 years now. And fun fact, I actually started doing open source or contributing to open source projects even before actually working as a programmer. Yeah, that's me. I maintain like too many packages nowadays
for like the energy I have. But yeah, that's pretty much excellent. Yeah, I know how that goes with the packages. It's like, oh, here's a cool idea. I want to put it out there. If it gains no traction, you can shut it down. If it gains a ton of traction, you're excited. If it gains a little traction, but enough that you've got to keep working it, that's kind of a not not ideal right? Yeah, and especially like nowadays, we do. I like the amount of things you, oh, I have an idea. Like the chance is like to just throw a bunch of agents into that and see what happens. Like it's very dangerous. It's very do. Yeah, I have an idea. Probably take me an hour. Let me see if I can validate it. Thursday afternoon, you're still like, I'm still almost there. I was like, yeah, where do my week go? What happened? Yeah, yeah. It's fun though. It's a wild time. Yeah, it's it's it's weird, right? Because in a way, we have this very powerful, not super constant tool, but it's very powerful and sometimes just amazing.
Like sometimes like what do you, what do you get back is just amazing? And you say like, oh, okay, that's nice. That's unexpected. Sometimes is the dumbest thing possible. You can you, you can get. But I guess like the point, the main point is that I think it showed up how as humans, we we have like a complicated relationship with tools sometimes. It kind of exploits a bunch of weird human behavior sometimes. So yeah, yeah, if you like building things, you know, create, you've got ideas and you want to see them come to life. It's an unprecedented time. If you really your main joy was to be in the code working on the little bits and nuance, I could see that it's frustrating and there's a whole, there's a whole thing that we go into there, but that's probably not what a crazy time, right? Yeah, absolutely. Yeah, so let's jump into the topics here. I think the first thing I want to do is let's just, let's just do a little dive into
Granian like it's, that's why I reached out to you to have you on the show the first time. Like I said, it's powering. I have on the talk Python server, there's 33 different containers running a bunch of different things and probably about 15 of them are running Granian. Nice. Yeah. So like I said, it's been really, really cool. Just tell people what is Granian because I think not only is it interesting as a thing that you've done previously in the show before, it's also an interesting lead in to where we're going with Tonio. So what's great? Yeah, sure. So Granian is an application server for Python. So in Python for historical reasons, we separated like the application layer from the HTTP layer. So when we want to serve like a web application in Python, like compared to other languages in which like the server part is actually a part of the application in Python, we tend to have like a separated package, which is a server and that servers like import your actual
application. And then we have application protocols to make the server talk with your application and back. But the two main protocols in Python are Vusky or WSGI. I never really understood like how to say that, which is like the original PEP 333A if I'm not mistaken. So it's quite ancient in the Python history. And once we had like a syncIO, a new protocol emerge that's called ASGI or ASGI because of course is the asynchronous server gateway. So Granian is again, an application server. So you can consider it as an alternative to microvusky, UV corn, hyper corn and a bunch of other servers that happened during the years. And yeah, the peculiar fact is it's made with Rust.
So compared to all the other servers you might use in Python, all the HTTP part or the network layer is running completely out of the interpreter, which kind of makes kind of alleviates like the load on the interpreter like on the Python side. And depending on the context might be more performant or it might stabilize your latency more. And you know, I think we're thinking back now. I'm pretty sure that that was the reason I chose Granian. Not that it was a ton faster, but the P95 bad bad side of things like how much might it slow down under certain weird circumstances. It was way more stable than a lot of the other servers, right? And that's kind of what you're referring to. Yeah, because again, like a normal server which runs everything into Python, 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. And so like Granian 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. It doesn't disturb let's say the interpreter with that. Yeah, I do want to just give a little bit of a hat tip to WSGI or WSGI because yeah, you pointed out it is old, but it's really, really nice that if somebody out there running on G unicorn or they're running on some other server, microwisky, which you just are operating in microwisky because it's not that important anymore. You don't have to change your code at all. You just take your flask or your jinger or whatever and you just say, now run here and they just, Granian talks WSGI or ASGI to that thing and it's transparent. It's beautiful. Yeah, I mean, there are still some, you know, sometimes weird things like,
like, where it's like has some subtleties, sometimes some nuances. So I had to like add some environment and environment key just for for the dog because under some circumstances, if that key is not there, things don't work exactly good. So yes, I agree that like having like the two protocols separated and coded like in a specific way gives you disability to switch the server. On the other hand, sometimes, you know, you can be a pain in some time, in some sort. Yeah, for sure. Yeah, for sure. But yeah, I'd say like we like recently it's getting more popular. So I'd say if I'm not mistaken, like few weeks ago, I think I surpassed like hypercouroning in terms of downloads. Awesome. And yeah, it's used like in a bunch of different big companies, Microsoft, Google for sure.
We use it at Century, of course. Beautiful. And I think like centuries, the only company in which we use like all the three supported protocols of gradient because gradient also supports like its own protocol, which is named like Arzgi, I don't know like RSGI, which is a redesign. Risky. Yeah. So yeah, I'd say it's going pretty good. I think like the last let's say this year was mostly into not not not mostly about features at the features, but mostly about, you know, making the thing more stable and covering like some edge cases, especially again, we're using it the centuries. So now like if something is wrong with gradient, like I have people knocking on my door, I bet you do. Well, I mean, with century, century gets an insane amount of like an unimaginable amount of traffic. I'm sure with the error reporting side of things, they're probably a little more so these
days on the MCP and API side as well with all the agents. I know that I use the MCP and absolutely love it. It's been incredible. It's really, really good. I could just say on my project, hey, there's a century error. I got what's up. Like literally that's all I got to say in clause like hold on, we're on it and it's like, yeah, you know, five minutes later, it's like here's what's going on. Let's work on it, you know, yeah, really good stuff. I do want to give a quick shout out as well on this regard to gradient like people like, oh, it's, it's not as popular as server X or whatever, right? Although I think that's starting to fade. It's got 5,000 stars and a ton of people using it and so on. Yeah, but it's also based on hyper from Rust, which itself has, where's my numbers? 410,000 projects using it. Yeah, it's really tested, right? And obviously the stuff at century as well. Yeah. Yeah, like the only thing I'm sure about gradient is the like DHDP stack. That's super safe. Like that's like I could bet like everything I have on that. So if something is wrong in gradient like that's on me, not on hyper likes to be clear.
This portion of talk path into me is brought to you by century and see your AI. There are plenty of AI tools that help you write code, but century see is built to help you fix it when it breaks. The difference is context. See isn't just guessing based on syntax. It's analyzing your actual century data, your stack traces logs and failure patterns, because it has the full context. It can a spot buggy code in review and help prevent issues before they happen and b, identify the root cause of production errors. It can even draft a fix and hand the work off to an agent like cursor to open a PR for you. Cier turns century into a complete loop. You have your traces errors logs and replace to see the problem and now AI to help solve it. Join millions of devs at companies like Claude, Disney Plus and even talk by thon who use century to move faster. Check them out at talkbython.fm slash century and use code talk by thon 26, all one word, for $100 in century credits. Thank you to century for supporting talk by thon.
I recently did a post over on my blog, personal blog calling cutting Python web at memory by 31 percent and I did a ton of analysis and stuff on how I was running gradient and different things. I think this is going to be a good lead in to where we're going as well. I got it to where I was using 120 1.3 gigs, let's say, for talk by thon training the courses specifically, which is people like always just like a little courses thing. Well, it has 178,000 lines of Python. So it's not a totally small project. But down by the end, I did a bunch of stuff on it. Easy imports. I switched to async, which is why I brought this up, but somewhere down here, I don't know what the final number is. Here we go. In the end, I got it down to 450 megs from 0.28 gigs. One of the main reasons I did that as a way I want to kind of want to lead into like this where we're going is I said, well, the way gradient works is for each, either you can make
multi processes that are single thread, it could do multiple threads and then it'll create a separate interpreter for each one. Either way, it kind of like replicates out the running, it'll create copies in one way or another of your code. And that if you could run one of them, that would be great. But why do we have so many? We have so many because of the gill. We have poor concurrency. It's really hard to handle a lot of load and like actually access, you know, my servers eight cores, like I could only access one core like in normally, right? And so we do all these things to like explode them out. But what is the most expensive? Like five years ago, this was true on servers, but really is true now. What is most expensive memory, not compute, right? Yeah. Yeah. Thanks, Hay. Thanks, and all the time. We really appreciate that. But we've free threaded Python now. That's the thing. 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, you know, I know your process takes 500 megs of RAM, but we need four of them.
So now you need two gigs just to run your code like that. We don't necessarily need that anymore. And gradient would be a really good foundation for that. But let's just maybe, let's just think broadly like about free threading and async as it is and stuff. And just where do you, I know you've thought a lot about this because this project that you built. So well, what do you think? First thing, like, can I say like free threading for me like it's super exciting and like, I don't know why people is not as much excited as me on this because like, I don't like, this is the, I'd say like the biggest thing ever happened into Python like since the beginning of Python, like in the last 30 years, like do you remember like Python two to Python three and all like, oh, now strings are not by its anymore and everything is unique. Yeah. That's nothing compared to, hey, the Gile is not there anymore. Like we talk about like every Python room, everywhere in the world, like at some point
talked about the Gile and why we have to do to have the Gile, right? Right. The Gile, like to me, that very hasty and simple and seem to perpetually be working on and never kind of. Yeah. Yeah. It took like 15 years of people talking about this and it finally landed like like last year, yes, of course, last year it was beta because like in Python three 13, it was like, this is an experiment still that was the the the asterisk on top. Like since Python three 14, that's considered stable is exactly as the normal Gile included interpreter. So and at the end of the day, like the biggest part of all of this was using reference counting instead instead of singletons. So I guess like I'm excited about free threaded Python because finally I have the language I love the most, not because of this structure, not because of types or how it's designed,
but because like of this awesome community that only Python language has like at least in my experience. And so finally I can use like this language with actual threads. So I'm not all life anymore to think, oh wait, we're in Python. So I need to use like a C library or something to take advantage of my CPU, especially given like, I don't know, we are in the era of K, like even if you buy a $200 CPU, that probably has like 12 cores or something. So yeah, I'm very excited. I'm confused about like the lack of adoption. Like I would love to see more adoption on these and of course, like there are reasons like every native C compiled extension has to be fixed to be thread safe. And probably like on a bunch of ancient extensions that's not simple or easy.
Yeah, I don't know man, like I'm just too excited. I want people to use free thread like I know and getting it is so much easier as well. It's just UV Python install, you know, that 3.14 T and you got it, right? Yeah, yeah. And like I don't know, I'm so pissed that the the maintainer of the Docker images, because we don't have like an official 314 free thread of image like why? Why? No, yeah, that is a good, that is actually a good point. Like it has been a year and a half. And it just requires a putting a T on the UV and solve it. Yeah, yeah. Yeah, but again, like to my perspective, like free thread is Python like opens the window to do a bunch of first to solve a bunch of the issues you mentioned, like which is for example, in center, like the number one issue is, I mean, center at the end of the day is 18 years old, Django Monolith. And so like yes, we have, we deploy like in several multiple different deployments, activating
like just some parts, but even so like, yeah, we have the same problem, which is hey, like in order to use like one CPU 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. Like that. 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 that you want to start talking about the surfability because you cannot just use like the bromine to use SDK or anything like that's because again, like then then doesn't behave like for different instances. So you cannot even track like the same metrics unless you do like some weird tricks like, yeah, relying on the multi processing library. It's my cement. So like, yeah, I think like free thread that like sold a bunch of problems, especially
in web development in Python. Yeah. Because everybody says like, oh, yeah, they did this just because of the I you know, because they can like make things parallel, whatever. I don't get that like like we have like this amazing feature and it solves a bunch of problems on web development and also like it opens up the a bunch of new opportunities because again, like one of the most annoying parts in in gradient, which is also like one of the things that people opens like the most the vast majority of issues about is because nobody understands like how sockets sharing works across different processes. And again, like we free thread it like that's just go away if we have something different from a sinker you and I guess that's the that's when we get on the point here. Because for example, like in gradient, so gradient supports free threaded Python scenes 2.0. They found out mistaken. And so when you use gradient on a free threaded interpreter, workers becomes become threads.
They're not longer processes. So and I can skip like all the dance of yeah, open this socket, but just just buying on that but not only some because then it gets to be shared across processes and so each worker can have his own like backlog. It's a mess man. So like on the free threaded variant like gradient already like use threads, then subtlety is that each thread has its own event loop. So what gradient cannot do today is sharing things between those threads. Like at the end of the yes, the application is shared, but like every request like if a repress comes to a worker, that's it. Like there's no you cannot cross boundaries. Why when you when you wait for something when you're suspended for something like that's that's the loop. The event loop you're running into which is a big limitation. Yeah, and one is calling a wait and another is calling a wait or maybe doing some work. But like if they were the same event loop, one thing could run because the other is awaiting
but they're actually separate. Yeah, together. Yeah. Yeah. And also like you can end up because still end up in these weird conditions which like maybe like I don't know 50% of your connection were bound to a specific event loop. And so you also have like on balles met between like the different threads. So you have like one thread that receives the vast majority of things, the other thread doing nothing. Yeah. Yeah. Let's say like we have the opportunities. We kind of lock some libraries like some libraries behavior that can actually give us like old the actual mates here. Agreed. I want to talk about one area before we dive in the tone I owe and that's just async I owe event loops and I know during your presentation. So you gave a talk on this and a euro python this year and I'll link like I'll link to the topic right here in the moment. But there are people like oh well we have UV loop so it doesn't that kind of solve it. And it I think that's a really interesting thought because it without calling that anybody
or anything called anybody like I think it it just highlights. There's in the python space. There's just not a lot of thinking about like what concurrency means. Why it matters how you program for it. And that's going to be a challenge. I think that's part of the challenge of what you're talking about the adoption of free thread of Python. But I think also it is a challenge in the sense that thinking about making the event loop faster by I don't know 20% or whatever you view loop actually does which is great. It doesn't really address the fact that you're still only on one thread. Your event loop can access like I just against probably better judgment. I just bought a max 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% depending on which loop I like I don't care. That is a broken either way like I want 95% capacity. Yes. Right. I just I thought it would be worthwhile to kind of like talk about UV loop a little bit and you built our loop and those things are great in their building blocks.
Yeah. They're not the panacea. They're not the fix. Yeah. And we also now have Marcelo Tlesiski, the maintainer of UV loop. Sorry. UV core and starboard that is it has recently published the ZooV loop which is another event loop for Python relying on the UV but it's written in Z. But yeah, like as you said all of these event loops, yes, they can optimize some of the hot part of the cycle. But basically like that's it. Like nothing really changed the shape of how things works. And so like yes, for sure you should you should actually install UV loop or our loop or ZooV loop or whatever to speed up like because that's performance you're living on the table regardless, right?
So it's good to have this project because they also like and light some of the inefficiencies of the synchio in my opinion. So I think it's still good to have this project and you should use them. But yeah, they want like the best you can get is probably like in the order of like 20% and we're talking about like row through the pool if you're just doing TCP and nothing else which is nobody does like nothing does nothing out there, right? Like we do a bunch of stuff. Yeah. If you constructed an example where basically all you're doing is waiting very efficiently, then you start to see those DBA and also if you've got like I've got 10 tasks that run over two seconds, it does zero, it doesn't matter. This only matters when you're doing very, very fine grain work in tons of it and there's lots of switching and that sort of thing, right? Yes. Yes. And so yes, like I guess these was one of my realization like because again like I work
on that loop mainly last year and working on our loop like made me realize two things. The first thing is that as SYNCIO it's overcomplicated sometimes. It's so fun fact at the presentation I did about error loop in Picon Italy 25. I had a slide asking to the list to the audience what's the difference between a protocol and a transport in a SYNCIO 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. They try to do that like in three different ways which to my experience like if you if you need three different ways to explain the same thing, something is deeply wrong. Definitely violates an Einstein core philosophy. Yes.
Also because like again, if we think about the zen of Python, like simple is better than complex. So that was my first realization, right? Because again, like you have a bunch of primitives like you have events, futures. Oh, by the way, every time they get a spawning to the into the event loop, they get wrapped into a task. What is a task? Nobody knows. Oh, and by the way, when they actually run inside the event loop, they became handles. So we have a bunch of these weird thing that they're not really exposed to you. If you just write a sync code, but if you need to debug something, when something is wrong, then you need all of that to understand what's going on, right? And the other realization again was, okay, I reached a point in which there's no other possible optimization in our loop because the vast majority of CPU timespaint here is around
Async.io primitives. And I have one single thread. And so like the only at that, when I came to realize this, I just said, okay, wait a second. Is that is really that hard to start like scratch whiteboard like from the beginning? Like let's just assume for a second, like Async.io never existed. And I want to do like a sync stuff in Python. What does it mean to make like an asynchronous front-time in Python? Is that hard? And I don't know, like I guess three or four weeks after this, I just got like an asynchronous runtime, a completely alternative asynchronous runtime, running on Python with multiple threads. That's cool. So to be clear on free thread Python, right? Yes, free thread Python only because again, to have like real threads, I mean, the design is to have real threads. So the moment you have the gill, I say, no, no, no, runtime error, change your interpreter because this is not good, right? This portion I talk Python is brought to you by our AI tools.
You know that thing where you ask an AI something about Python and it confidently tells you about a library version from 18 months ago? Well, we fix that at least for our shows. Talk Python and Python bytes both have MCP servers now. Connect talk Python and your AI can search over 550 episodes, full transcripts, every guest in the entire course catalog. Connect Python bytes and you get almost 500 episodes of Python news going back to 2026, including every link we've ever put in the show notes. That means you can say things like ask talk Python, what astral joining open AI means for UV or what has Python bytes said about UV and get real answers with real links, not hallucination, name the show in your prompt in your AI knows exactly where to look. And if you live in the terminal, talk Python also has a CLI too. One line, UV tool install, talk dash Python dash CLI and then search episodes, transcripts, guests and courses without ever opening a browser.
It's open source and it outputs text, JSON and markdown so it feeds the AI tools that don't speak MCP yet. And here's the real reason I built it. Both shows cover around 10 years of Python history. The people, the decisions, the packages that took over and the ones that quietly didn't, this enhanced access is free. No account, no API key, nothing to buy. This history should belong to all of us. Visit talkbython.fm and Python bytes and click the MCP link in the navbar, connect them right now to your agents so that they'll be accessible anytime you need them in the future. And so yeah, that was like kind of the landscape in which I started building Tonyo. Or Tonyo, you can pronounce it like however you want. I'm Italian so I say Tonyo, of course. I think obviously, well, you have to do Tonyo. Tonyo. So it's really good, but as it pairs to Async IO, Tonyo also kind of like as a hazard to that, right? Yeah. But we have trio.
So that's right. That's right. And it dry. And trio is living there a little bit as well. Yeah. So this is a really interesting project that you have. And it basically says, what if we actually had threads? One of the things that's endlessly frustrated me about Python and Async IO and consequently Async IO, I think the language implementation of Async in a way is it's really good. I think, you know, the way it sort of turns Async code into what looks like structurally a serializable code or serial code is really, really nice. But the fact that the developer has to juggle loops in which loop and that loop, no, that's not the right loop. Like so many times I've been, oh, I want to do this request. Oh, are you using court or an Async web framework? There is an Async loop, but not that one. You need the one created by the web framework. Like, oh, did you initialize the Async database connection before it ran the web thing and like, nope, that's the wrong one. And there's all this weird juggling. It's just so janky.
And I'm pretty sure it's janky because we didn't have true threads and the possibility, well, what if you crisscross this, it's everything sort of falls apart. And if you could just say, look, it's just multi-threaded. And there is a thing that called the loop. And that's where stuff runs. And I don't care who started it. It's our loop, which is when I need to loop the run time Python itself has a loop for me. And I'll use it. And I feel like you sort of took that philosophy a little bit, right? Yeah. I say like the number one inspiration for Sonyo is Tokyo, which is the number one Brass asynchronous runtime. That's probably because I spend in Brass a bunch of time. So yeah, I don't know, for example, the real thing to me is we still, if you just want to do an Asyncronous program in Python, like a program, let's say a script or a simple CLI. We still need to do Async your run. Why? Like the amount of things you need to know and decide and stuff there is like completely
different from other language. Yeah. Think about JavaScript. You don't decide anything. That's Async in JavaScript. That's true. Well, I'll tell you a little bit why you can't just why you still got to call Async Run because there's like a foundational layer that's not present. And it's your job to write the foundation on which Async IO executes. Right? Like it's still your job to figure out how do I actually create a loop? How do I run a loop? How do I, like it's your job? You can't just create an Async Dev method and call it because there's nowhere for it to go until you go create. You know what I mean? There's like, there's just yeah, but the foundation isn't quite there. But again, it's, it's an entry point, right? Like, so in Tanya, for example, yes, you have Tanya run. You want to like, you can struct everything and configure the runtime and do whatever you want. Like, yes, you can do that. You don't want to do that. Like you have a single decorator, which is Tanya main. You put it in on your function, like your entry point in your program and it's done. Like it's, yeah. I mean, talk about that.
Yeah. Before we get too much in the, the, the, the, the words of it, why don't you just give us like, talk us through writing a program in this, there's also two ways. Like, maybe you could after you talk about this, you could like, sort of get into the, this concept of colored functions versus not and, and sometimes call async code viral code and like, yeah, let's just like talk through the differences here. Yeah. So, um, okay. Yes. Tanya has two different supports to different syntax modes. Um, and that's because again, when I designed this, given that I just throw everything away and started from scratch, I ended up having a scene. System, which didn't really required a sync away, uh, syntax or notation because at the end of the day behind a sync away notation, there are like, those are generated, right? Um, and so given the effort to support like a two different syntax was like minimal, uh,
writing everything from scratch, I ended up like living up to the final user to decide, hey, you dislike a sync away notation. 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 a sync everywhere and away and everywhere whatever. Um, okay, you don't like that. Guess what? Tanya also have like just a yield syntax. So instead of a wait, you just yield from, from the next, uh, a coroutine you want to, to suspend for or wait for, um, and that's it. You don't need to set like the, the right sync deaf everywhere. Um, it, to be here is still color it, right? Because the moment you have like a generator function, then whatever it calls it before has to be a generator as well. Um, but I guess like my point was mainly like, okay, to support the syntax like, doesn't really take that much of a work. Uh, and once it was settled in, um, I mean, it's there and people come decide by, hey,
you don't, you dislike like one notation. Sure. Just use the other one. Um, but yes, like, let's say for people familiar with the sync. Oh, uh, the syntax like the sync away syntax is very similar to a sync. Oh, um, so you basically have your coroutine. So you, your sync deaf, uh, whatever you want, uh, and you await, um, and you await, uh, primitives or, or stuff. Um, the, so the main difference is that instead of important, um, uh, uh, uh, uh, uh, uh, uh, setting stuff from a sync a yo. So like, I don't know, sleep or timer or whatever, you import like similar primitives to from from Tonyo. Uh, we of course have way less primitives because again, like, as I said before, um, I think a yo has too many primitives. Um, and the only big difference from a sync a yo, let's say native people is that you have like, spawn methods. So instead of saying, um, yeah, sync a yo, so in a sync a yo, when you want to, um,
may things like like, spawn several tasks and then wait for all of them to complete. You usually do like, you have different ways of doing this, right? Like, you can have like, uh, task set, uh, you can have, uh, now scopes on from trio. Um, but I mean, like, you can use gather so you can create tasks and then gather, uh, with sync a yo. So Tonyo is like to do this is, you just have like two methods, you either spawn, um, a synchronous stuff or you spawn something that is blocking and then the primitives is, the primitive is spawn walking. Um, the other major difference from a sync a yo is that when you spawn something that gets run immediately, um, whereas like in a sync a yo, this is true only if you create a task. Otherwise everything else is, uh, eager. So it, it, it, in order to run, whatever you want to wait, you need to await.
So in a sync a yo, the vast majority of operations, uh, bound together, the launching the operation and waiting for the result unless you create a task that starts immediately and then you await later, right? And in Tonyo, everything is wrapped around the spawn because you can call spawn and forget about it or you can await Tonyo's poem to wait for the results, right? Or you can park like the result of the spawn, the join and join later. Um, so that's, I, I don't know if like, maybe it's more confusing than it's hard to like explain it like simply, but maybe, but one of the, uh, the challenges I've seen with, uh, standard async a yo is you, you want to create a bunch of work and let it run and then you want to get the answers back. So a naive way would be like, call a bunch of stuff. Mm-hmm. And then when you call a wait, it's like, you, you've, you've got to create them not started and then start all of them and then go through each one and await them. Whereas this way, you get a list back and you just await them in order or, or wait one after another somehow, they're already started, right?
And so there's like the skip of this like, okay, I've got all the things that are going to become tasks, but I got to turn them into tasks. So then I can await them because if I regular wait them, it'll still fall back to serial, just running on the vent loop, you know? Yeah. And I mean, like, in Tony's the same, like, if you await a coroutine, it means like, I want to wait for that to happen, right? If you don't want to, like, just spawn park the, the, the result of spawning into a variable, await later. I think like there's an example, uh, down below in the page, uh, about like, spawning something and awaiting later, um, um, uh, so yeah, that it's, that's, there's a bunch of the questions. So yeah, um, but yeah, like the idea is, and again, like, I didn't invent anything, but because the, the, the, the syntax, yes, this is the example. So in this example, we compute like numbers, like stupid example, but it is, to give the point. So we start like two functions, which compute numbers, then we await for third function.
So we immediately wait for the third result, and then we wait for the first two results. So in the meantime, so when you, when you do the first spawn, those two functions start already, like, immediately at that point in, in your code, um, which kind of makes sense if you think about it, because like syntax base, like you're invoking those code routines, right? Uh, in that moment, you just don't wait for them. Um, and so at the end, like, you just await for the parallel, actual parallel result, like everything here is concurrent, but the only point in which you have parallel code, the actual parallel code is, uh, when you await for, for, for parallel, right? Yeah. Um, so that's the major difference from a synchial and Antonio, right? You, when you spawn stuff, that thing happened like in parallel. So, uh, by default, Antonio starts with the number of threads equal to the number of your CPUs. Um, the, the, the blocking, with, Tony also have like a blocking pool for blocking stuff.
Um, but that's separate. Say the main work loop, um, that runs your code, um, by default, you have X, X threads per CPU course. Um, so if you run this on your, uh, M3 max, uh, M3 studio max, uh, what was it? Like you will end up having like 18 threads, yeah, running stuff. Yeah. Which is kind of what we want in general, right? Like, like, if you, if I write a program, you could configure it, right? You could configure the, yes, the report, let's say, like, look, uh, this thing has to be a good citizen. Let's just take the number CPUs divided by two or something like that so I can still do other work. Yeah, yeah. You can like, uh, again, the, that Tonyo main, uh, the curator or the Tonyo run methods, uh, accepts, um, some parameter. So you can configure like the size of the standard thread pool, the maximum amount of blocking threads you want to spawn, uh, because that, that does get spawn on demand. Um, you can configure if you want to have support for context bars, because it's not,
that's another thing about Python, right? Like we, we kind of went from thread locals to context bars, uh, but in Tonyo, that's a bit more complicated, right? Because you have a sync code in multiple threads. Yeah, because they usually leverage the thread, right? Yeah. So if you want to actually use context bars, you, you have to tell the, the, the runtime, right? Because otherwise like some side effects might be weird, uh, in that condition, um, hold on, um, um, no manclature definition, please hear what a context bars for people don't know, give us exact. Oh, yeah. So, context bars, uh, supply, so the interpreter has these, um, every thread, um, into the Python interpreter has what's its called a context. Um, and so we've seen Kyo, um, so what was the, um, the, um, the, um, the, um, the, um, what was the problem? So, um, we, we used to have thread locals, uh, which meant like, um, if I have two threads
and I have, um, for example, a request and I have to, uh, keep like the request state, um, global, like global between quotes, uh, in my code, but I want to use like the correct request in, in, um, in the code, um, and don't make them mixing, uh, the two different threads states, um, we use thread locals because that's it. That's basically like, um, addictionary, more or less, um, where you can store that data and that snapshot of data is for, for a single thread. Uh, context bar are similar concepts, but for a scene code, which means, uh, you can store global again between quotes, global state, uh, from, uh, asynchronous, from a synchronous code, uh, suspend. And when you get back, you have the correct object back, uh, instead of mixing global state between different code team, was my explanation good?
Yeah, that's good. Like, if people are probably pretty common with flask.request, because you don't, you've got a view method or maybe deep down inside some lower part of your programming, like, well, I need to know what the URL was. You just say flask.request, you know, passed around, like very handy, probably architecturally a bad choice. You know, it's hard to like test it out, right? And you mock it. I don't know. Emma, Emma does the same, um, is, is the one thing I call it from flask because I really like it. Um, but I think we can do the same argument about database, right? Like, yeah, with the synchronous database, like, you have to start the context, like, a sync with database, whatever. And then you need to pass that, like, all through, like a context bar is more handy. I don't know. Yeah, yeah. It's certainly unlocks some really interesting things. Some extensions do cool things with it. So that's what you're talking about, but because it's not all just tied to the thread and using async IO context variables now, it's kind of shared in potential ways. It's, it's a little bit, you got to opt into it. That's right. Yeah.
Yeah. Yeah. So it is just to be, like, I think like the big warning at the beginning should have been like, Tonya is very alpha right now. So maybe that decision will change in the future because I think right now the default is false. That might become true as soon as I stabilize like the API. But yeah, the idea was if you're testing Tony right now, if you're using Tony to do some tests right now, I want the developer to explicitly state, okay, I'm gonna use Context Vars, right? Because again, the fact you have multiple threads and stuff happening in parallel, I just want the developer to be sure what is, if it's thinking model is correct before trying to do some stuff. Because again, Tony is multi-threaded, multi-threaded is usually like, it's, I don't know.
I think for humans, multi-threading is hard to get sometimes. So that's the trade-off, right? Like, yes, we now have all of these capabilities on the other end, we need to think about the fact that the Sinkayo always had this hidden, let's say a feature which is, hey, there's only one coroutine running at the time. Whereas in Tony, you can have like whatever number. Yep. Now you still had to manage stuff across a wait calls. So that was, I feel like people felt like the guild saved them from all the red considerations, like locks and semaphores and so on. And I don't think it did. There's not a guarantee that it, like I've read, it could interrupt you, it's just less likely to interrupt you, right? So it did, like, the subal thing is that it did around other primitives. So for example, in a Sinkayo is really hard to deadlock yourself
because you're using like a threading lock. Because again, like the hidden feature of a Sinkayo is, you have only one thread working on that shit, right? So yeah, exactly. Like, you can do all the locks you want. It's the same thread in the re-entrant. So you're exactly. It's not, it's effectively a no-up other than a cost CPU. Yes, exactly. Whereas in Tonio, like that's exactly where people usually make mistakes because now you have detonial locking, because you have the Sink-ronos locks, but you also have thread locks, which are two different things. And if you, and so like, you can deadlock Tonio, how? Yeah. Welcome to multi-threading. Yes, like you, you, you, you create like you enter a threading lock and then you await a coroutine inside a threading lock. Now you're deadlock. Yeah. Because the two things doesn't speak each other. So, but I guess like if, like my advice, like if people wants to like start working in multi-threaded Python, like my advice is just think about like those two rules
and then multi-threading is not that hard. Again, like rule number one, never await inside a threading lock. That's a simple rule. Like, like also because like in Python, use with lock. So that's super easy even to identify, right? That's the rule number one. Rule number two, you have multiple threads. So if you want to write into anything, I mean, all the objects are thread safe in in free threaded Python, because addiction is thread safe, at least it's thread safe, whatever. But that doesn't mean like you can screw up in using those. Because if you write state anywhere, you might have like race conditions and side effects because you expect to write something and then as a spec to read that value, but in the meantime you have another thread writing to the same location. So again, like my advice is rule number one, never use locks, threading locks and then await. If you want to have an async lock,
then use an async lock. So a tonal lock for that. Rule number two, watch out. So you probably need locks when you want to be sure that like when you have a sequence of operations, right? On shared memory. Those are the two rules. I think like with those two rules, multi-threaded and is not that hard. Yeah, agreed. So does tonal come with its own dedicated lock and synchronization primitives? Yes, there's an entire sync module. So tonal has different modules, tonal time, which contains time primitives, like timeouts, timers, et cetera. It has a sync module, which contains all the synchronization primitives, so locks, semaphore, barriers, et cetera. It has the next work module, which is one providing like sockets. So that's quite a big difference from a sync.io. So in a sync.io, we don't have like a sync socket library.
So if you think about like the standard lib socket module, Tonyo, a sync.io needs you to like use that and then pass the sockets to the event loop and then handle the socket through the event. Whereas in Tonyo, there's like a socket, like there's an etro module and inside that module, there's a socket module, which has the same exact interface of the standard lib socket. The only difference is of course, like every sync method is a sync in Tonyo. There's the stream, there's the streams module inside the network module, which is the high level, trio like interface to make it easy to manage network. It has a file system module, which contains like all the standard, like open kind of methods that they sink with open. Tonyo.files.open, something like that. Yeah, that's the FES. And it also contains like mirror of the pathlib, standard lib pathlib.
So you can use like, front-only FES import path and that path has all the methods that are required to be a sync or no to be a sync. So that's more or less like the design. I don't think like, so right now I am at Tonyo 0.9.14, I guess. And I don't think there's much left in terms of modules and primitives and features. The big feature missing is Windows support, more on that later. But yeah, I'd say I'm close to stabilize the API. So right now again, this is all alpha. That's mostly because like there's only me working on this. And I have fable working on a bunch of other things, which I guess like if I can digress for a second, I think like I have a very peculiar and unique way of using the eye because right now I'm like, and I guess like it's a good
us use case to advertise. So the entirety of Tonyo, so Tonyo has maybe one of the most Tonyo. So Tonyo has maybe 1% AI written code, the only code that was written by Claude was the pi test plugin. Oh, by the way, we also have a pi test plugin. So you can pi test mark Tonyo. But that's the only part that I let AI touch. Everything else like it's old school. I written all of it like the old way, text editor, not even like I'm not even the any suggestion and everything. Yeah. What I use the I for and it helped me a lot into reaching like a setting which I think Tonyo is really stable now was to use the I to build projects that were using Tonyo and stress test everything about Tonyo. So for example, I made a port of pi the the harness from
Maria's seconder. So I made Claude rewrite the whole thing in Python because pi is retaining in TypeScript, regardless of the name. So I made an entire harness with Claude saying, okay, take pi meter every behavior and just use Tonyo to do to do everything. And that like helped me like catch like probably 95% of the bugs in Tonyo. And I kind of started doing the same for the ecosystem. Maybe we can talk about it a bit later, but yeah, that's also like a super you super useful for me, right? Like as a solo open source developer, which yeah, I want to focus on Tonyo. I don't have like time to do like all the other packages. And so yeah, that was nice. That's a very interesting way. I hadn't really considered that. That's just like I need users before I have users. Yeah, I need these three use cases covered. So give me a web app that uses the eight sync database. Give me this terminal app and so on.
And that's yeah, that's exactly. Yeah. Okay. I agree ecosystem is interesting. I want to talk about it. But before that, I want to come back to blocking threads. Yes. So blocking threads are CPU bound generally type of things. Is that right? So I think Tonyo is a bit peculiar in this regard because it really depends what your program does because if your program, whatever that is, has mixed load, which means like you have both IO bound stuff like network, disk, terminal, whatever, and CPU bound stuff. Then the only way to be sure that everything like keeps running smoothly, like on the network part, IO part is to spawn like the CPU bound stuff on the on the blocking thread. But if your if your program is just CPU bound, then who cares? Just use the normal just just just use the normal thread pool.
Because then you don't have anything to block like your CPU bound, your CPU limited. So the standard configuration of Tonyo gives you exactly the perfect number of CPU course to use. On the other hand, if you just use if you just have like a IO bound. So again, like if you have just CPU bound work or just IO bound work, you can use like the standard pool, nothing particular. Like the blocking thread pool is useful only when you have mix at workloads and you need to balance them, right? Okay. How do I create such a thing? I know. I just how do I start a task in one or the other? So every away to a yellow destruction or a Tonyo spawn instruction runs on the standard thread pool to spawn like something on the blocking thread pool is just Tonyo spawn blocking. Done. You don't need to manage like the size of the pool. You don't need to manage like you don't need to create the pool. If you want to control concurrency in the in the blocking thread pool,
you either use like body or swing your spawn stuff or or or or or semi for freedom of spawn stuff. But yeah, like in general is is not different from spawning anything else. Sure. So you guys. One thing that we're working here. Here's here's what I was thinking. I was always getting that that I think would be a sweet, a sweet feature. You've got at Tonyo.main for the entry point, right? Yes. So that runs on you know Tonyo.run instead of just Python, run or whatever. For functions that you know are computational, you would like to ensure that they run on the blocking thread or along those lines, you could put a decorator that just says Tonyo.com dot CPU or whatever. I don't know. Like you come up with a name, but that way I don't need to have to think like if I import another library also built on Tonyo, I don't know. Oh, like all that function wants me to run it this way. Like no, I just call it and Tonyo goes, ah, this is an async CPU one. So I'll scale it up. You know, scaffolded up on that basically.
That's a nice suggestion. Okay. All right. I think I think it will land in Tonyo 0.10. Yes. Awesome. Okay. Cool. I like that. When you're in the function, you know, the locality of the information that oh, this is computational and so on is right there. But when you're calling and especially in a big app, like you don't know, right? So I think that would be really cool. So I'm glad you like it. No, we'll work on that. Awesome. All right. We got a timer. Two really quick things. So one thing that is nice about standard async, I always everyone uses async. I know if they're writing async code at the moment, unless I just like trio or one of these other things. So for example, if I use HTTP X to or HTTP X and I with a saying, you know, async with create a client of it and I await it's get that runs on the same sort of deal. But it's not probably not compatible with Tonyo. Is it? And if it's not, it would not be. Yeah. Yeah, it's not. So we have. So I have one thing that is like the current state and I also have the idea for the like the
future. So right now we have a package which is called Tonyo monkey. And you probably can guess what it means, what it does. It basically like monkey patches. Popular or like I would either say like async, a your native libraries I thought about in the last few months. That might be useful. And so I guess today we have like Tonyo monkey can monkey patch. I think no, sorry, psychopg. It can patch HTTP X and HTTP X to it can patch red. This when I think that's most of it for the moment. But I'm I'm open for issues like if you if you if anyone from the listeners want to try Tonyo to an existing like code base and you have like a package you would like to see monkey patched just open an issue in the Tonyo monkey repo or even like in Tony
discussions. And I will look at it. Because again, right now I need like the vast majority of possible use cases to see that I covered everything. Ideally, as soon as I stabilize the API and I say, okay, this is the thing. My idea was to try to contact the NIO maintainers to see if we can have NIO back end in in any of you, which should be like a way easier way to deal with this. I'm not sure how much it's feasible because again, like the main problem for Tonyo right now is that it doesn't support any of the sync IO primitive. So if any code base like realize a ton on task or futures or whatever, that's not that's not in the runtime. Yeah.
Yeah. Okay. Interesting. Interesting. But there are some there's some choices and you are you also creating your own. I saw like a some like custom HTTP or like some other. Yes. I'm also I'm also trying to be like a very like contain ecosystem of libraries mainly like on the web part. So right now I published a low level HTTP library for Python that is compatible both with the sync IO and Tonyo. It's called HDD Punk. I published a nine level client, HTTP client on top of that, which is called punk rack. So again, like you can use that regardless of Tonyo sync IO because the the adaptation layer is inside the package. I kind of published like a very big experiment of a UV corn port of yeah a UV corn port to work
in Tonyo, which I guess brings up a question like hey, when when do we'll grain and support this? Am I right? Yeah. So I have plans. It's actually way bigger plans for GANION. So as I said before, 2026 was a bit boring in GANION like no, the bunch of new features, not a bunch of that stuff. But that's just because several months ago I started like designing two things for GANION. So the first is this revision two of the art key protocol, which will be a sync independent. So it will it will become like a callback protocol because the other big feature I want to add in GANION is to support a bunch of runtimes. So my idea is not the GANION tree when it will land. No promises on the on the time here. But like when it will lands, I really it will support a sync IO, Trio, Tonyo, GEVENT,
EVENT, LET and a bunch of stuff. So that's that's the idea is to isolate let's say the protocol's implementation and the Python runtime implementation. That's what it will happen. Okay. One day. In the meantime, I had like I wanted to have like a test server to use like with TORN and see what happens. Like so that's why I guess. I guess using yeah, using something like court or fast API or something like that. If you were able to run it in GANION with the Tonyo, Tonyo, Tonyo, Tonyo, IO back end, the async endpoint you write would be running on Tonyo. Yeah. Yeah, that's the plan. Oh, okay. We also have in Tonyo Monke, there are also patches for fast API by the way. And I'm working on a one on Django. I need to be I need to be in carton on that. Because we met this year at this year, by GANI, and we talk about it. So maybe in a month or so, we will also have like a patch for Django.
Sweet. Yeah, I just had carton on Carlton on sorry. He mentioned Carson, but Carlton on to talk about basically all the async work in Django, six and six months or so on. So that was fun. I watched the episode that I was like, huh, sounds familiar. Huh. Yeah, we've talked about that. I see. Interesting. All right. Well, I got one final main topic for us. Yeah. Oh, yeah. What about Windows? So, and I'm not I'm not putting pressure on you to do it. I'm just I'm just rounding out the conversation. Okay. So, um, if you ask me like a few months ago, my answer would probably be like that, short video extract from a series like where you had the actor, whatever. You know, I'm not gonna not gonna swear on the podcast, but uh, you get what I mean. Uh, so I'm not so first of all like the the the usual problem.
So supporting Windows in Grainian, it's a pain man, like a real pain. Like I'm not joking. I because it has like all these queer behaviors, not just in Python, not just in terms of threads. Like do you know that we that that Windows have as like a bug from, I think it was like anti-four. Uh, that like if you if you share a socket, it becomes blocking. And so you cannot use it like anywhere. So Windows first start like Windows is a pain and is a very weird operating system. Second, I don't have like a Windows environment. Because finally in 2026, I was able to finally have just macOS and Linux on all my machines also for gaming. So I'm so happy. So that those are like I say the two main parameters on on Windows support. But because also because like implementing like like the library using Tonyo to manage
the polar, the actual event loop, um, it supports Windows, but you know, weird way. So um, months ago I was like almost 100% convinced I'm now never Windows never. Now thanks to Fable by chatting with Fable, I think we found a way to hack into into Neo this library to to fake some stuff about Windows. So it will interpret some stuff as DCP sockets, even if they are like file descriptors or do anything. So I can I won't make a promise here because then I have to do it. But um, ideally, end of the year, we might see partial Windows support in time. So that's the idea right now. I still hate Microsoft to be to be to be clear here. Like a man, I just like Windows like.
So if you're if if any of the listener like if you're programming in Windows and not using Whizzle, please put down in the comments why like why are you in flick self-inflicting this to you? Like explain me the rationale into dealing with all the BS that Windows 11 is nowadays. So yeah, but anyways, I will try end of the year to have some form of Windows support. I can tell I can sense your excitement. My Windows computer over there is Windows 10 by the way, but I do think for people listening, like Windows subsistence for Linux is an escape hatch that you can do, right? If you got a project, you really want to use this here like set up one of the subsistence for the next and run it there, right? To to to to be here, like the only reason I want to like I want to try to add Windows support in Tonyo is not for developer. He's actually to provide developers a way to say, okay, I want to build a program in Python with Tonyo that has to work like everywhere
because right now again, like if you if you code anything in Tonyo, it works only on POSICS system, so only Linux and macOS. So that's the reason why I want to have at least some form of Windows support because I mean, it's it's not about developers, it's it's about like where the software runs at the other day, right? Right. The deployment targets, yeah. Yeah. All right, Joe, final call to action. People have been listening to like this sounds pretty excellent. I want to try it. What do you tell him? I mean, like again, I think we're living in the perfect moment to try stuff. We've given with this big opportunity, big new opportunity of free trial Python. We have AI agents. And by the way, I was surprised, but like pointing any agent, any model frontier model to a project and say, okay, implement this in Tonyo, I was surprised, they got all the API correctly most of the times, which is amazing. There's no training data about Tonyo
and regardless, they they like it was able to drive through stuff. So I guess it's the perfect moment to try stuff. If you I'd say like, if for any reason, you're not happy with a sync for any reason, you experience like any of the pain points we talked about. I think it's like the perfect moment to try new stuff. And if you try Tonyo and you have like a use case where it's not working or or the API can be improved, like open an issue, open a discussion, be me DM me on on Twitter, oh sorry, X, the everything up. I will call it. Bing me and we'll figure out. Yeah, I think that's that's my message. Yeah, awesome. It looks like a very ambitious project. And I think you've done a lot of interesting things. So
thanks for coming on and sharing it. Tell people where they can stay in touch with you as well. Yes, so my GitHub handle is GI zero B a R O. You can find me with the same handle on Twitter. I have a blog, even if I don't write a ton, but you can find my blog at blog.baro.dev. And if you want to join me and Marcelo Trilizinski, again, author of Uwikorn Starlet at a bunch of stuff he's making with token maxing, we recently started a podcast available on YouTube, the at the www.pod. Nice. So people check them out, check out the pod. That's on fun. I'll give that a look as well. And Joe, thanks for coming on the show. Nice to catch up with you. Thank you for having me. It was super good. This has been another episode of Talk Python to me.
Thank you to our sponsors. Be sure to check out what they're offering. It really helps support the show. This episode is sponsored by centuries sear. If you're tired of debugging in the dark, give sear or try. There are plenty of AI tools that help you write code, but centuries sear is built to help you fix it when it breaks. Visit talkbithon.fm slash century and use the code talk Python 26, all one word no spaces for $100 and century credits. And is brought to you by us. Talk Python and Python bytes both now have mcp servers. Point your AI at 10 plus years of Python episodes, transcripts and show notes. Free like mcp in the nav at talkbithon.fm and at Python bytes. 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 to async code, flask, jingo, htmx, and even LLMs. Best of all, there's no subscription in sight. Browse the catalog at talkbithon.fm. And if you're not already subscribed to the show on your favorite podcast player, what are you waiting
for? Just search for Python and your podcast player. We should be right at the top. If you enjoy that geeky rap song, you can download the full track. The link is actually in your podcast blur share notes. This is your host, Michael Kennedy. Thank you so much for listening. I really appreciate it. I'll see you next time. We tapped into that modern viable k-d song. Talk Python and me, i-sync is the norm.
More episodes
More from Talk Python To Me

#562: DuckLake: The Lakehouse That's Just SQL and Parquet
Talk Python To Me

#560: Building a Research OS: From Django to 30,000 Samples
Talk Python To Me

#559: 12 Things You Should (and Shouldn't) Do in AWS
Talk Python To Me

#558: Hyper-Personal Software with Python
Talk Python To Me