WEBVTT

00:00:00.020 --> 00:00:01.500
<v Michael Kennedy>How many cores does your machine have?

00:00:02.080 --> 00:00:02.220
<v Michael Kennedy>10?

00:00:02.720 --> 00:00:02.960
<v Michael Kennedy>18?

00:00:03.740 --> 00:00:06.840
<v Michael Kennedy>Well, your async Python code uses just one of them.

00:00:07.580 --> 00:00:09.060
<v Michael Kennedy>That isn't a bug in asyncio.

00:00:09.420 --> 00:00:10.180
<v Michael Kennedy>That's the design.

00:00:10.840 --> 00:00:16.940
<v Michael Kennedy>And things like optimizing the event loops to be faster by maybe 20% with uv doesn't fundamentally

00:00:17.090 --> 00:00:17.480
<v Michael Kennedy>change that.

00:00:17.760 --> 00:00:20.520
<v Michael Kennedy>That's why Giovanni Bariliari started over.

00:00:21.180 --> 00:00:26.400
<v Michael Kennedy>Joe is the creator of Granian, the Rust-based server that powers Talk Python and all the

00:00:26.450 --> 00:00:27.740
<v Michael Kennedy>other things we run here.

00:00:28.020 --> 00:00:33.580
<v Michael Kennedy>His new project is Tone.io, an async runtime written from scratch specifically for free-threaded Python.

00:00:34.280 --> 00:00:38.320
<v Michael Kennedy>Real threads, a handful of primitives instead of async.io's pile of them,

00:00:38.820 --> 00:00:43.260
<v Michael Kennedy>and it flat out refuses to even start if the GIL is present.

00:00:43.880 --> 00:00:49.600
<v Michael Kennedy>This is Talk Python To Me, episode 561, recorded September 1st, 2026.

00:01:10.620 --> 00:01:14.940
<v Michael Kennedy>Welcome to Talk Python To Me, the number one Python podcast for developers and data scientists.

00:01:15.460 --> 00:01:16.840
<v Michael Kennedy>This is your host, Michael Kennedy.

00:01:17.220 --> 00:01:20.760
<v Michael Kennedy>I'm a PSF fellow who's been coding for over 25 years.

00:01:21.400 --> 00:01:22.520
<v Michael Kennedy>Let's connect on social media.

00:01:22.840 --> 00:01:26.000
<v Michael Kennedy>You'll find me and Talk Python on Mastodon, BlueSky, and X.

00:01:26.190 --> 00:01:28.120
<v Michael Kennedy>The social links are all in your show notes.

00:01:28.880 --> 00:01:32.400
<v Michael Kennedy>You can find over 10 years of past episodes at talkpython.fm.

00:01:32.580 --> 00:01:35.780
<v Michael Kennedy>And if you want to be part of the show, you can join our recording live streams.

00:01:36.120 --> 00:01:36.600
<v Michael Kennedy>That's right.

00:01:36.790 --> 00:01:40.060
<v Michael Kennedy>We live stream the raw uncut version of each episode on YouTube.

00:01:40.460 --> 00:01:45.060
<v Michael Kennedy>Just visit talkpython.fm/youtube to see the schedule of upcoming events.

00:01:45.290 --> 00:01:48.940
<v Michael Kennedy>Be sure to subscribe there and press the bell so you'll get notified anytime we're recording.

00:01:49.840 --> 00:01:52.380
<v Michael Kennedy>This episode is sponsored by Sentry's Seer.

00:01:52.660 --> 00:01:55.020
<v Michael Kennedy>If you're tired of debugging in the dark, give Seer a try.

00:01:55.680 --> 00:01:59.320
<v Michael Kennedy>There are plenty of AI tools that help you write code, but Sentry's Seer is built to

00:01:59.320 --> 00:02:00.900
<v Michael Kennedy>help you fix it when it breaks.

00:02:01.480 --> 00:02:07.200
<v Michael Kennedy>Visit talkpython.fm/sentry and use the code Talk Python26, all one word, no spaces,

00:02:07.620 --> 00:02:09.340
<v Michael Kennedy>for $100 in Sentry credits.

00:02:09.979 --> 00:02:11.720
<v Michael Kennedy>And it's brought to you by us.

00:02:12.360 --> 00:02:15.440
<v Michael Kennedy>Talk Python and Python Bytes both now have MCP servers.

00:02:16.040 --> 00:02:20.440
<v Michael Kennedy>Point your AI at 10 plus years of Python episodes, transcripts, and show notes.

00:02:20.840 --> 00:02:25.160
<v Michael Kennedy>Free. Click MCP in the nav at talkpython.fm and at Python Bytes.

00:02:25.760 --> 00:02:29.020
<v Michael Kennedy>Hello, hello. Welcome back to Talk Python and me.

00:02:29.350 --> 00:02:30.020
<v Michael Kennedy>Great to have you here, Joe.

00:02:30.280 --> 00:02:33.640
<v Michael Kennedy>Hey, thanks for having me. It's nice to get back to the show.

00:02:33.930 --> 00:02:34.540
<v Michael Kennedy>It's been a while.

00:02:35.040 --> 00:02:35.920
<v Michael Kennedy>It has been a while.

00:02:36.220 --> 00:02:41.800
<v Michael Kennedy>And we talked about Granian last time, the Rust-based web application server for Python.

00:02:42.200 --> 00:02:44.200
<v Michael Kennedy>And I got to just take a moment and say thank you.

00:02:44.320 --> 00:02:45.000
<v Michael Kennedy>We'll come back to this.

00:02:45.040 --> 00:02:49.400
<v Michael Kennedy>But thanks for powering Talk Python and all the different things.

00:02:49.480 --> 00:02:52.680
<v Michael Kennedy>I think across our web apps, I don't know, like 10 million requests a month.

00:02:52.780 --> 00:02:53.740
<v Michael Kennedy>And it's been flawless.

00:02:54.920 --> 00:02:55.720
<v Michael Kennedy>So thanks.

00:02:55.880 --> 00:02:58.420
<v Michael Kennedy>I appreciate you serving up all the stuff for everyone.

00:02:58.680 --> 00:02:59.120
<v Giovanni Barallari>Oh, yeah.

00:02:59.340 --> 00:02:59.440
<v Giovanni Barallari>Thanks.

00:03:00.220 --> 00:03:00.820
<v Giovanni Barallari>It's been a pleasure.

00:03:00.980 --> 00:03:04.540
<v Giovanni Barallari>Like, it's nice to see, you know, when something just works.

00:03:04.980 --> 00:03:09.660
<v Giovanni Barallari>like when you do something and like at the end of the day it just works and people start using it

00:03:09.740 --> 00:03:16.560
<v Giovanni Barallari>like of course like they they ask for a bunch of features you know um but it's nice to see yeah it

00:03:16.580 --> 00:03:21.300
<v Michael Kennedy>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

00:03:21.400 --> 00:03:26.440
<v Michael Kennedy>show a couple years i don't i didn't check the exact date when i look back maybe people are new

00:03:26.480 --> 00:03:30.419
<v Michael Kennedy>to the show or haven't heard your prior episode or just don't remember like give us a quick

00:03:30.440 --> 00:03:37.160
<v Giovanni Barallari>introduction who you are yeah sure so i'm giovanni but everybody can call me joe i'm italian but i

00:03:37.160 --> 00:03:44.400
<v Giovanni Barallari>live in vienna i am working as a programmer in century but i'm a physicist so i think like

00:03:44.760 --> 00:03:53.360
<v Giovanni Barallari>that's pretty peculiar i've been like in the open source ecosystem uh mainly in python um probably

00:03:53.440 --> 00:04:00.560
<v Giovanni Barallari>for the last oh god I think like it's almost 20 years now um and fun fact like I actually started

00:04:00.800 --> 00:04:07.160
<v Giovanni Barallari>like um doing open source or or um contributing to open source projects even before actually

00:04:07.440 --> 00:04:16.900
<v Giovanni Barallari>working as a programmer so um yeah that's me I maintain like too many packages nowadays for like

00:04:16.959 --> 00:04:18.380
<v Giovanni Barallari>the energy I have.

00:04:20.080 --> 00:04:22.019
<v Giovanni Barallari>But yeah, that's pretty much.

00:04:22.360 --> 00:04:22.640
<v Giovanni Barallari>Excellent.

00:04:22.890 --> 00:04:25.320
<v Michael Kennedy>Yeah, I know how that goes with the packages.

00:04:25.660 --> 00:04:26.740
<v Michael Kennedy>It's like, oh, here's a cool idea.

00:04:26.740 --> 00:04:27.500
<v Michael Kennedy>I want to put it out there.

00:04:27.800 --> 00:04:29.520
<v Michael Kennedy>If it gains no traction, you can shut it down.

00:04:30.080 --> 00:04:32.000
<v Michael Kennedy>If it gains a ton of traction, you're excited.

00:04:32.100 --> 00:04:33.280
<v Michael Kennedy>If it gains a little traction,

00:04:33.780 --> 00:04:35.140
<v Michael Kennedy>but enough, then you got to keep working on it.

00:04:35.200 --> 00:04:36.740
<v Michael Kennedy>That's kind of not ideal, right?

00:04:37.080 --> 00:04:37.320
<v Giovanni Barallari>Yeah.

00:04:37.430 --> 00:04:39.060
<v Giovanni Barallari>And especially like nowadays with AI,

00:04:39.560 --> 00:04:41.440
<v Giovanni Barallari>like the amount of things you,

00:04:41.570 --> 00:04:42.520
<v Giovanni Barallari>oh, I have an idea.

00:04:43.740 --> 00:04:48.680
<v Giovanni Barallari>Like the chances like to just throw a bunch of agents into that and see what happens.

00:04:49.200 --> 00:04:49.840
<v Giovanni Barallari>It's very dangerous.

00:04:50.240 --> 00:04:50.820
<v Giovanni Barallari>It's very dangerous.

00:04:51.240 --> 00:04:52.040
<v Michael Kennedy>I have an idea.

00:04:52.040 --> 00:04:52.840
<v Michael Kennedy>It'll probably take me an hour.

00:04:52.880 --> 00:04:54.080
<v Michael Kennedy>Let me see if I can validate it.

00:04:54.520 --> 00:04:57.080
<v Michael Kennedy>Thursday afternoon, you're still like, oh, I'm still almost there.

00:04:57.120 --> 00:04:58.980
<v Michael Kennedy>It's like, where did my week go?

00:04:59.280 --> 00:04:59.500
<v Michael Kennedy>What happened?

00:05:00.360 --> 00:05:01.420
<v Michael Kennedy>It's fun though, isn't it?

00:05:01.500 --> 00:05:02.360
<v Michael Kennedy>It's a wild time.

00:05:02.720 --> 00:05:05.000
<v Giovanni Barallari>Yeah, it's weird, right?

00:05:05.200 --> 00:05:12.800
<v Giovanni Barallari>Because in a way, we have this very powerful, not super constant tool.

00:05:14.080 --> 00:05:19.760
<v Giovanni Barallari>but it's very powerful and and sometimes just amazing like sometimes like what do you what

00:05:19.760 --> 00:05:27.160
<v Giovanni Barallari>you get back is just amazing and you say like oh okay that's nice that's unexpected sometimes is

00:05:27.280 --> 00:05:34.960
<v Giovanni Barallari>the dumbest thing possible you can you you can get but i guess like the point the main point is that

00:05:35.240 --> 00:05:42.860
<v Giovanni Barallari>i think it it shows up how as humans we have like a complicated relationship with tools

00:05:43.860 --> 00:05:51.980
<v Giovanni Barallari>it can exploit a bunch of weird human behavior sometimes so yeah yeah if you like building things

00:05:52.520 --> 00:05:56.560
<v Michael Kennedy>you know create you've got ideas and you want to see them come to life it's an unprecedented time

00:05:56.720 --> 00:06:03.220
<v Michael Kennedy>if if you really your main joy was to be in the code working on the little bits of nuance i can

00:06:03.300 --> 00:06:07.300
<v Michael Kennedy>see that it's frustrating and uh there's there's a whole there's a whole thing that we go into there

00:06:07.260 --> 00:06:08.120
<v Michael Kennedy>but that's probably not.

00:06:08.130 --> 00:06:09.660
<v Michael Kennedy>But wow, what a crazy time, right?

00:06:09.900 --> 00:06:10.900
<v Michael Kennedy>Yeah, absolutely.

00:06:11.400 --> 00:06:14.000
<v Michael Kennedy>Yeah, so let's jump into the topics here.

00:06:14.010 --> 00:06:18.560
<v Michael Kennedy>I think the first thing I want to do is let's just do a little dive into Granian.

00:06:19.320 --> 00:06:21.520
<v Michael Kennedy>That's why I reached out to you to have you on the show the first time.

00:06:21.840 --> 00:06:23.160
<v Michael Kennedy>Like I said, it's powering.

00:06:23.390 --> 00:06:25.460
<v Michael Kennedy>I have on the Talk Python server,

00:06:25.680 --> 00:06:29.680
<v Michael Kennedy>there's 33 different containers running a bunch of different things,

00:06:29.770 --> 00:06:32.440
<v Michael Kennedy>and probably about 15 of them are running Granian.

00:06:33.020 --> 00:06:33.380
<v Michael Kennedy>Nice.

00:06:33.880 --> 00:06:34.160
<v Michael Kennedy>Yeah.

00:06:34.800 --> 00:06:36.820
<v Michael Kennedy>So like I said, it's been really, really cool.

00:06:37.040 --> 00:06:41.400
<v Michael Kennedy>just tell people what is Granian because I think not only is it interesting as a thing that you've

00:06:41.400 --> 00:06:45.580
<v Michael Kennedy>done previously and been on the show before it's also an interesting lead-in to where we're going

00:06:45.580 --> 00:06:52.940
<v Giovanni Barallari>with Tonio so what's great yeah sure uh so Granian is an application server for Python so in Python

00:06:53.540 --> 00:07:02.700
<v Giovanni Barallari>for historical reasons um we separated like the application layer from the HTTP layer so when we

00:07:02.600 --> 00:07:07.640
<v Giovanni Barallari>want to serve a web application in Python compared to other languages

00:07:07.740 --> 00:07:12.260
<v Giovanni Barallari>in which the server part is actually a part of the application.

00:07:12.640 --> 00:07:18.640
<v Giovanni Barallari>In Python, we tend to have a separated package, which is a server,

00:07:19.100 --> 00:07:22.340
<v Giovanni Barallari>and that server's import your actual application.

00:07:22.760 --> 00:07:25.820
<v Giovanni Barallari>And then we have application protocols

00:07:26.720 --> 00:07:30.120
<v Giovanni Barallari>to make the server talk with your application and back.

00:07:31.100 --> 00:07:34.960
<v Giovanni Barallari>The two main protocols in Python are VUSGI or WSGI.

00:07:35.500 --> 00:07:38.120
<v Giovanni Barallari>I never really understood how to say that,

00:07:39.720 --> 00:07:44.760
<v Giovanni Barallari>which is like the original PET333, if I'm not mistaken.

00:07:45.050 --> 00:07:48.100
<v Giovanni Barallari>So it's quite ancient in the Python history.

00:07:49.140 --> 00:07:53.860
<v Giovanni Barallari>And once we had like AsyncIO, a new protocol emerged.

00:07:54.380 --> 00:07:57.660
<v Giovanni Barallari>That's called ASGI or ASGI because, of course,

00:07:57.780 --> 00:08:00.080
<v Giovanni Barallari>it's the asynchronous server gateway.

00:08:00.520 --> 00:08:05.260
<v Giovanni Barallari>So, Granian is, again, an application server,

00:08:06.360 --> 00:08:11.720
<v Giovanni Barallari>so you can consider it as an alternative to MicroVoosgi,

00:08:13.860 --> 00:08:17.600
<v Giovanni Barallari>Uvicorn, Hypercorn, and a bunch of other servers

00:08:17.820 --> 00:08:19.240
<v Giovanni Barallari>that happened during the years.

00:08:20.520 --> 00:08:24.640
<v Giovanni Barallari>And, yeah, the peculiar fact is it's made with Rust.

00:08:26.080 --> 00:08:29.880
<v Giovanni Barallari>So, compared to all the other servers you might use in Python,

00:08:29.960 --> 00:08:34.659
<v Giovanni Barallari>all the HTTP part or the network layer

00:08:35.300 --> 00:08:39.780
<v Giovanni Barallari>is running completely out of the interpreter,

00:08:40.940 --> 00:08:46.020
<v Giovanni Barallari>which kind of alleviates the load on the interpreter,

00:08:46.230 --> 00:08:47.320
<v Giovanni Barallari>like on the Python side.

00:08:48.550 --> 00:08:50.680
<v Giovanni Barallari>And depending on the context,

00:08:50.860 --> 00:08:53.420
<v Giovanni Barallari>it might be more performant

00:08:53.660 --> 00:08:57.500
<v Giovanni Barallari>or it might stabilize your latency more.

00:08:58.660 --> 00:09:03.360
<v Michael Kennedy>You know, I think thinking back now, I'm pretty sure that that was the reason I chose Granian.

00:09:03.770 --> 00:09:09.420
<v Michael Kennedy>Not that it was a ton faster, but the P95 bad side of things.

00:09:09.550 --> 00:09:13.260
<v Michael Kennedy>Like how much might it slow down under certain weird circumstances?

00:09:13.760 --> 00:09:17.800
<v Michael Kennedy>It was way more stable than a lot of the other servers, right?

00:09:17.870 --> 00:09:19.220
<v Michael Kennedy>And that's kind of what you're referring to.

00:09:19.820 --> 00:09:23.640
<v Giovanni Barallari>Yeah, because again, like a normal server which runs everything into Python,

00:09:23.720 --> 00:09:39.480
<v Giovanni Barallari>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.

00:09:39.860 --> 00:09:49.940
<v Giovanni Barallari>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.

00:09:50.100 --> 00:09:52.880
<v Giovanni Barallari>It doesn't disturb, let's say the interpreter with that.

00:09:53.200 --> 00:09:53.280
<v Michael Kennedy>Yeah.

00:09:53.540 --> 00:09:58.080
<v Michael Kennedy>I do want to just give a little bit of a hat tip to WSGI or WSGI.

00:09:58.450 --> 00:10:14.240
<v Michael Kennedy>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.

00:10:14.600 --> 00:10:16.580
<v Michael Kennedy>You don't have to change your code at all.

00:10:16.760 --> 00:10:20.480
<v Michael Kennedy>You just take your flask or your Jenga or whatever, and you just say, now run here.

00:10:20.510 --> 00:10:24.740
<v Michael Kennedy>And they just, Grandin talks WizGey or Asggy to that thing, and it's transparent.

00:10:25.330 --> 00:10:25.720
<v Michael Kennedy>It's beautiful.

00:10:26.140 --> 00:10:26.240
<v Michael Kennedy>Yeah.

00:10:26.510 --> 00:10:26.620
<v Michael Kennedy>Yeah.

00:10:26.940 --> 00:10:33.659
<v Giovanni Barallari>I mean, there are like still some, you know, sometimes weird things like, like, Vertzoic

00:10:34.500 --> 00:10:37.680
<v Giovanni Barallari>has some subtleties, sometimes some nuances.

00:10:37.840 --> 00:10:50.700
<v Giovanni Barallari>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.

00:10:51.120 --> 00:11:02.120
<v Giovanni Barallari>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.

00:11:02.520 --> 00:11:08.180
<v Giovanni Barallari>On the other hand, sometimes, you know, you can be a pain in some time.

00:11:08.680 --> 00:11:09.400
<v Giovanni Barallari>Yeah, for sure.

00:11:09.840 --> 00:11:10.460
<v Michael Kennedy>Yeah, for sure.

00:11:11.780 --> 00:11:18.380
<v Giovanni Barallari>But yeah, I'd say like we like recently it's getting more popular.

00:11:18.690 --> 00:11:27.540
<v Giovanni Barallari>So I'd say if I'm not mistaken, like a few weeks ago, I think I surpassed like HyperCorn in terms of downloads.

00:11:28.760 --> 00:11:28.980
<v Giovanni Barallari>Awesome.

00:11:29.060 --> 00:11:37.480
<v Giovanni Barallari>And yeah, it's used like in a bunch of different big companies, Microsoft, Google, for sure.

00:11:38.350 --> 00:11:40.000
<v Giovanni Barallari>We use it at Sentry, of course.

00:11:41.140 --> 00:11:46.040
<v Giovanni Barallari>And I think like Sentry is the only company in which we use like all the three supported

00:11:46.380 --> 00:11:51.300
<v Giovanni Barallari>protocols of Granian because Granian also supports like its own protocol, which is named

00:11:51.410 --> 00:11:56.320
<v Giovanni Barallari>like RSGI, I don't know, like RSGI, which is a redesign.

00:11:56.740 --> 00:11:56.940
<v Giovanni Barallari>Risky.

00:11:57.840 --> 00:11:57.940
<v Giovanni Barallari>Yeah.

00:12:00.220 --> 00:12:07.460
<v Giovanni Barallari>um so yeah i'd say it's going pretty good um i think like the last let's say this year was mostly

00:12:07.800 --> 00:12:16.340
<v Giovanni Barallari>into um not not not not mostly about features i think features but mostly about you know making

00:12:16.480 --> 00:12:22.400
<v Giovanni Barallari>the thing more stable and covering like some edge cases um especially again we're using it the

00:12:22.460 --> 00:12:28.260
<v Giovanni Barallari>Sentry. So now like if something is wrong with Grinia, like I have people knocking on my door.

00:12:28.900 --> 00:12:34.900
<v Michael Kennedy>I bet you do. Well, I mean, with Sentry, Sentry gets an insane amount of like an unimaginable

00:12:35.040 --> 00:12:39.480
<v Michael Kennedy>amount of traffic. I'm sure with the error reporting side of things and probably a little

00:12:39.580 --> 00:12:46.040
<v Michael Kennedy>more so these days on the MCP and API side as well with all the agents. I know that I use the MCP

00:12:46.120 --> 00:12:50.580
<v Michael Kennedy>and absolutely love it. It's been incredible. It's really, really good. I could just say on my

00:12:50.600 --> 00:12:54.720
<v Michael Kennedy>project hey there's a century error i got what's up like literally that's all i gotta say in clause

00:12:54.840 --> 00:12:57.920
<v Michael Kennedy>like hold on we're on it and it's like yeah you know five minutes later it's like here's what's

00:12:57.930 --> 00:13:02.580
<v Michael Kennedy>going on let's work on it you know yeah really good stuff uh i do want to give a quick shout out as

00:13:02.700 --> 00:13:08.220
<v Michael Kennedy>well uh on this regard to gradient like people are like oh it's it's not as popular as server x or

00:13:08.310 --> 00:13:12.120
<v Michael Kennedy>whatever right although i think that's starting to fade it's it's got 5 000 stars and a ton of

00:13:12.210 --> 00:13:19.079
<v Michael Kennedy>people using it and so on yeah but it's also based on hyper from rust which itself has uh where where's

00:13:19.100 --> 00:13:25.420
<v Michael Kennedy>my numbers, 410,000 projects using it. So it's really tested, right? And obviously the stuff at

00:13:25.500 --> 00:13:31.640
<v Giovanni Barallari>Sentry as well. Yeah. Yeah. Like the only thing I'm sure about Granian is that like the HTTP stack,

00:13:32.100 --> 00:13:38.280
<v Giovanni Barallari>that's super safe. Like that's like, I could bet like everything I have on that. So if something

00:13:38.300 --> 00:13:44.660
<v Giovanni Barallari>is wrong in Granian, like that's on me, not on Hyper, like to be clear. This portion of Talk

00:13:44.660 --> 00:13:49.660
<v Michael Kennedy>Python to me is brought to you by Sentry and Sear AI. There are plenty of AI tools that help you

00:13:50.080 --> 00:13:55.280
<v Michael Kennedy>write code, but Sentry Sear is built to help you fix it when it breaks. The difference is context.

00:13:55.920 --> 00:14:00.500
<v Michael Kennedy>Sear isn't just guessing based on syntax. It's analyzing your actual Sentry data,

00:14:00.760 --> 00:14:05.360
<v Michael Kennedy>your stack traces, logs, and failure patterns. Because it has the full context, it can

00:14:05.740 --> 00:14:09.620
<v Michael Kennedy>A, spot buggy code in review and help prevent issues before they happen,

00:14:10.260 --> 00:14:13.040
<v Michael Kennedy>and B, identify the root cause of production errors.

00:14:13.980 --> 00:14:18.540
<v Michael Kennedy>It can even draft a fix and hand the work off to an agent-like cursor to open a PR for you.

00:14:19.140 --> 00:14:21.140
<v Michael Kennedy>Seer turns Sentry into a complete loop.

00:14:21.660 --> 00:14:24.500
<v Michael Kennedy>You have your traces, errors, logs, and replays to see the problem,

00:14:24.800 --> 00:14:26.260
<v Michael Kennedy>and now AI to help solve it.

00:14:26.620 --> 00:14:30.920
<v Michael Kennedy>Join millions of devs at companies like Claude, Disney+, and even Talk Python,

00:14:31.380 --> 00:14:32.560
<v Michael Kennedy>who use Sentry to move faster.

00:14:33.080 --> 00:14:37.860
<v Michael Kennedy>Check them out at talkpython.fm/sentry and use code talkpython26,

00:14:38.440 --> 00:14:41.800
<v Michael Kennedy>all one word, for $100 in Sentry credits.

00:14:42.440 --> 00:14:44.180
<v Michael Kennedy>Thank you to Sentry for supporting Talk Python.

00:14:45.660 --> 00:14:48.980
<v Michael Kennedy>I recently did a post over on my personal blog

00:14:49.080 --> 00:14:52.860
<v Michael Kennedy>calling cutting Python web app memory by 31%.

00:14:52.860 --> 00:14:55.760
<v Michael Kennedy>And I did a ton of analysis and stuff

00:14:56.660 --> 00:14:59.260
<v Michael Kennedy>on how I was running Granian and different things,

00:14:59.300 --> 00:15:00.240
<v Michael Kennedy>just how I was running Python.

00:15:00.820 --> 00:15:02.400
<v Michael Kennedy>And I think this is going to be a good lead-in

00:15:02.400 --> 00:15:03.540
<v Michael Kennedy>to where we're going as well.

00:15:03.880 --> 00:15:13.040
<v Michael Kennedy>I got it to where I was using 1.3 gigs, let's say, for Talk Python training the courses specifically.

00:15:13.360 --> 00:15:15.160
<v Michael Kennedy>And people are like, oh, it's just a little courses thing.

00:15:15.200 --> 00:15:19.960
<v Michael Kennedy>Like, well, it has 178,000 lines of Python, so it's not a totally small project.

00:15:20.440 --> 00:15:23.240
<v Michael Kennedy>But down by the end, I did a bunch of stuff on it.

00:15:24.480 --> 00:15:25.280
<v Michael Kennedy>Lazy imports.

00:15:25.779 --> 00:15:28.660
<v Michael Kennedy>I switched to async, which is why I brought this up.

00:15:28.940 --> 00:15:31.160
<v Michael Kennedy>But somewhere down here, I don't know what the final number is.

00:15:31.400 --> 00:15:31.820
<v Michael Kennedy>Here we go.

00:15:31.880 --> 00:15:36.560
<v Michael Kennedy>In the end, I got it down to 450 megs from 0.28 gigs.

00:15:37.190 --> 00:15:40.900
<v Michael Kennedy>One of the main reasons I did that as a way I want to kind of want to lead into like this,

00:15:41.380 --> 00:15:45.740
<v Michael Kennedy>where we're going is I said, well, the way grading works is for each,

00:15:46.250 --> 00:15:49.540
<v Michael Kennedy>either you can make multi-processes that are single threaded or it could do multiple threads

00:15:49.660 --> 00:15:51.380
<v Michael Kennedy>and then it'll create a separate interpreter for each one.

00:15:51.490 --> 00:15:55.240
<v Michael Kennedy>Either way, it kind of like replicates out the running.

00:15:55.420 --> 00:15:58.660
<v Michael Kennedy>It'll create copies in one way or another of your code.

00:15:59.010 --> 00:16:01.760
<v Michael Kennedy>And that, if you could run one of them, that would be great.

00:16:01.840 --> 00:16:03.000
<v Michael Kennedy>But why do we have so many?

00:16:03.330 --> 00:16:04.900
<v Michael Kennedy>We have so many because of the GIL.

00:16:05.010 --> 00:16:06.140
<v Michael Kennedy>We have poor concurrency.

00:16:06.640 --> 00:16:10.360
<v Michael Kennedy>It's really hard to handle a lot of load and like actually access, you know, my servers,

00:16:10.600 --> 00:16:10.920
<v Michael Kennedy>eight cores.

00:16:11.010 --> 00:16:14.060
<v Michael Kennedy>Like I could only access one core like normally, right?

00:16:14.380 --> 00:16:17.560
<v Michael Kennedy>And so we do all these things to like explode them out.

00:16:17.680 --> 00:16:18.880
<v Michael Kennedy>But what is the most expensive?

00:16:19.140 --> 00:16:22.160
<v Michael Kennedy>Like five years ago, this was true on servers, but really is true now.

00:16:22.250 --> 00:16:23.000
<v Michael Kennedy>What is most expensive?

00:16:23.360 --> 00:16:24.380
<v Michael Kennedy>Memory, not compute, right?

00:16:25.180 --> 00:16:25.300
<v Michael Kennedy>Yeah.

00:16:25.700 --> 00:16:25.760
<v Michael Kennedy>Yeah.

00:16:26.160 --> 00:16:26.600
<v Michael Kennedy>Thanks AI.

00:16:27.000 --> 00:16:27.540
<v Michael Kennedy>Thanks AI.

00:16:27.710 --> 00:16:27.940
<v Michael Kennedy>Thanks Altman.

00:16:28.860 --> 00:16:30.120
<v Michael Kennedy>We really appreciate that.

00:16:32.260 --> 00:16:34.580
<v Michael Kennedy>But we've free-threaded Python now.

00:16:34.780 --> 00:16:35.300
<v Michael Kennedy>That's a thing.

00:16:35.740 --> 00:16:43.280
<v Michael Kennedy>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.

00:16:44.220 --> 00:16:47.060
<v Michael Kennedy>I know your process takes 500 megs of RAM, but we need four of them.

00:16:47.090 --> 00:16:49.580
<v Michael Kennedy>So now you need two gigs just to run your code.

00:16:50.360 --> 00:16:51.940
<v Michael Kennedy>We don't necessarily need that anymore.

00:16:52.160 --> 00:16:53.780
<v Michael Kennedy>And grading would be a really good foundation for that.

00:16:54.000 --> 00:16:59.440
<v Michael Kennedy>But let's just think broadly about free-threading and async as it is and stuff.

00:16:59.660 --> 00:17:02.620
<v Michael Kennedy>I know you've thought a lot about this because of this project that you built.

00:17:03.680 --> 00:17:04.819
<v Michael Kennedy>Well, what do you think?

00:17:05.350 --> 00:17:08.720
<v Giovanni Barallari>First thing, can I say free threading to me?

00:17:09.140 --> 00:17:11.199
<v Giovanni Barallari>It's super exciting.

00:17:11.640 --> 00:17:16.060
<v Giovanni Barallari>And I don't know why people are not as much excited as me on this.

00:17:16.819 --> 00:17:25.480
<v Giovanni Barallari>Because this is, I'd say, the biggest thing that ever happened into Python since the beginning of Python.

00:17:25.680 --> 00:17:27.140
<v Giovanni Barallari>In the last 30 years.

00:17:27.380 --> 00:17:31.720
<v Giovanni Barallari>do you remember like Python 2 to Python 3 and all like,

00:17:32.080 --> 00:17:36.360
<v Giovanni Barallari>oh, now strings are not bytes anymore and everything is Unicode?

00:17:36.700 --> 00:17:36.800
<v Michael Kennedy>Yeah.

00:17:37.040 --> 00:17:40.960
<v Giovanni Barallari>That's nothing compared to, hey, the gil is not there anymore.

00:17:41.080 --> 00:17:46.800
<v Giovanni Barallari>Like we talk about like every Python room everywhere in the world,

00:17:46.860 --> 00:17:53.220
<v Giovanni Barallari>like at some point talked about the gil and why we have to do to have the gil, right?

00:17:53.960 --> 00:17:54.160
<v Giovanni Barallari>Right.

00:17:54.720 --> 00:17:59.220
<v Michael Kennedy>The Gilectomy that Larry Hastings had, it seemed to perpetually be working on and never

00:17:59.440 --> 00:18:00.020
<v Michael Kennedy>kind of went anywhere.

00:18:00.340 --> 00:18:00.480
<v Michael Kennedy>Yeah.

00:18:00.480 --> 00:18:05.660
<v Michael Kennedy>It took like 15 years of people talking about this and it finally landed.

00:18:05.860 --> 00:18:12.000
<v Giovanni Barallari>Like last year, yes, of course, last year it was beta because like in Python 3.13, it

00:18:12.000 --> 00:18:14.380
<v Giovanni Barallari>was like, this is an experiment still.

00:18:14.900 --> 00:18:17.840
<v Giovanni Barallari>That was the asterisk on top.

00:18:18.960 --> 00:18:22.120
<v Giovanni Barallari>But since Python 3.14, that's considered stable.

00:18:22.540 --> 00:18:28.560
<v Giovanni Barallari>It's exactly as the normal GIL-included interpreter.

00:18:30.480 --> 00:18:33.540
<v Giovanni Barallari>And at the end of the day, the biggest part of all of this

00:18:33.800 --> 00:18:38.800
<v Giovanni Barallari>was using reference counting instead of singleton.

00:18:39.160 --> 00:18:44.000
<v Giovanni Barallari>So I guess I'm excited about free-threaded Python

00:18:44.020 --> 00:18:47.840
<v Giovanni Barallari>because finally I have the language I love the most.

00:18:49.020 --> 00:18:53.180
<v Giovanni Barallari>not because of the structure, not because of types or how it's designed,

00:18:53.180 --> 00:18:57.840
<v Giovanni Barallari>but because of this awesome community that only Python language has,

00:18:58.220 --> 00:18:59.080
<v Giovanni Barallari>at least in my experience.

00:19:00.220 --> 00:19:04.080
<v Giovanni Barallari>And so finally, I can use this language with actual threads.

00:19:04.540 --> 00:19:08.460
<v Giovanni Barallari>So I'm not obliged anymore to think, oh, wait, we are in Python,

00:19:08.920 --> 00:19:13.760
<v Giovanni Barallari>so I need to use a C library or something to take advantage of my CPU,

00:19:14.480 --> 00:19:25.040
<v Giovanni Barallari>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.

00:19:26.380 --> 00:19:28.420
<v Giovanni Barallari>So yeah, I'm very excited.

00:19:29.700 --> 00:19:32.740
<v Giovanni Barallari>I'm confused about the lack of adoption.

00:19:33.160 --> 00:19:35.260
<v Giovanni Barallari>I would love to see more adoption in this.

00:19:35.500 --> 00:19:46.120
<v Giovanni Barallari>And of course, like there are reasons, like every native C compiled extension has to be fixed to be thread safe.

00:19:47.640 --> 00:19:53.360
<v Giovanni Barallari>And probably like on a bunch of ancient extensions, that's not simple or easy.

00:19:55.180 --> 00:19:56.240
<v Giovanni Barallari>Yeah, I don't know, man.

00:19:56.320 --> 00:19:58.100
<v Giovanni Barallari>Like I'm just too excited.

00:19:58.380 --> 00:20:00.620
<v Giovanni Barallari>Like I want people to use free thread.

00:20:00.800 --> 00:20:02.020
<v Michael Kennedy>I know.

00:20:02.180 --> 00:20:04.260
<v Giovanni Barallari>And getting it is so much easier as well.

00:20:04.340 --> 00:20:10.840
<v Michael Kennedy>it's just uv Python install, you know, that 3.14 T and you got it, right?

00:20:10.980 --> 00:20:11.200
<v Michael Kennedy>Yeah.

00:20:11.600 --> 00:20:11.720
<v Giovanni Barallari>Yeah.

00:20:12.080 --> 00:20:16.740
<v Giovanni Barallari>And like, I don't know, I'm so pissed at the maintainer of the Docker images

00:20:16.880 --> 00:20:20.240
<v Giovanni Barallari>because we don't have like an official 3.14 free threaded image.

00:20:20.360 --> 00:20:20.660
<v Giovanni Barallari>Like why?

00:20:21.160 --> 00:20:21.220
<v Michael Kennedy>Why?

00:20:21.400 --> 00:20:23.460
<v Michael Kennedy>Oh yeah, that is actually a good point.

00:20:23.580 --> 00:20:28.900
<v Michael Kennedy>Like it has been a year and a half and it just requires putting the T on the uv installed bit.

00:20:29.160 --> 00:20:29.440
<v Giovanni Barallari>Yeah.

00:20:29.740 --> 00:20:29.860
<v Giovanni Barallari>Yeah.

00:20:30.320 --> 00:20:30.460
<v Giovanni Barallari>Yeah.

00:20:30.480 --> 00:20:36.660
<v Giovanni Barallari>Yeah, but again, to my perspective, free-threaded Python opens the window to do a bunch of...

00:20:36.840 --> 00:20:42.040
<v Giovanni Barallari>First, to solve a bunch of the issues you mentioned, which is, for example, in Centri,

00:20:42.160 --> 00:20:43.980
<v Giovanni Barallari>the number one issue is...

00:20:44.120 --> 00:20:50.460
<v Giovanni Barallari>I mean, Centri at the end of the day is this 18-years-old Django monolith.

00:20:51.420 --> 00:20:59.520
<v Giovanni Barallari>And so, yes, we deploy in several multiple different deployments, activating just some parts.

00:20:59.720 --> 00:21:20.080
<v Giovanni Barallari>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.

00:21:20.320 --> 00:21:31.320
<v Michael Kennedy>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.

00:21:31.440 --> 00:21:34.120
<v Michael Kennedy>Do we want to start talking about observability?

00:21:34.420 --> 00:21:36.580
<v Giovanni Barallari>Because you cannot just use like

00:21:36.640 --> 00:21:39.380
<v Giovanni Barallari>the Prometheus SDK or anything like that.

00:21:40.000 --> 00:21:42.000
<v Giovanni Barallari>Because again, like then those became like

00:21:42.480 --> 00:21:43.340
<v Giovanni Barallari>four different instances.

00:21:43.600 --> 00:21:45.800
<v Giovanni Barallari>So you cannot even track like the same metrics

00:21:46.620 --> 00:21:48.480
<v Giovanni Barallari>unless you do like some weird tricks.

00:21:48.960 --> 00:21:53.200
<v Giovanni Barallari>Like, yeah, relying on the multiprocessing library.

00:21:54.180 --> 00:21:54.800
<v Giovanni Barallari>It's messy, man.

00:21:54.980 --> 00:21:57.080
<v Giovanni Barallari>So like, yeah, I think like

00:21:57.620 --> 00:22:00.220
<v Giovanni Barallari>FreeThreader like solves a bunch of problems,

00:22:00.820 --> 00:22:04.180
<v Giovanni Barallari>Especially in web development in Python.

00:22:05.420 --> 00:22:09.780
<v Giovanni Barallari>Because everybody says like, oh yeah, they did this just because of AI, you know,

00:22:09.880 --> 00:22:12.240
<v Giovanni Barallari>because they can like make things parallel, whatever.

00:22:12.720 --> 00:22:14.200
<v Giovanni Barallari>I don't get that.

00:22:14.300 --> 00:22:18.280
<v Giovanni Barallari>Like, like we have like this amazing feature and it solves a bunch of problem

00:22:18.460 --> 00:22:23.180
<v Giovanni Barallari>on web development and also like it opens up the a bunch of new opportunities.

00:22:23.400 --> 00:22:27.680
<v Giovanni Barallari>Because again, like one of the most annoying parts in Granian, which is also

00:22:27.700 --> 00:22:33.400
<v Giovanni Barallari>Like one of the things that people opens like the most, the vast majority of issues about

00:22:33.580 --> 00:22:38.960
<v Giovanni Barallari>is because nobody understands like how socket sharing works across different processes.

00:22:39.370 --> 00:22:45.100
<v Giovanni Barallari>And again, like with FreeThreader, like that's just go away if we have something different

00:22:45.230 --> 00:22:45.940
<v Giovanni Barallari>from a Synco.

00:22:46.310 --> 00:22:51.280
<v Giovanni Barallari>And I guess that's the, that's when we get on the point here.

00:22:52.520 --> 00:22:57.660
<v Giovanni Barallari>Because for example, like in Grainium, so Grainium supports FreeThreader Python scenes

00:22:57.680 --> 00:22:59.440
<v Giovanni Barallari>2.0, if I'm not mistaken.

00:22:59.920 --> 00:23:01.520
<v Giovanni Barallari>And so when you use

00:23:01.700 --> 00:23:03.420
<v Giovanni Barallari>Granian on a free-threaded interpreter,

00:23:03.840 --> 00:23:05.520
<v Giovanni Barallari>workers become

00:23:07.600 --> 00:23:07.660
<v Giovanni Barallari>threads.

00:23:08.200 --> 00:23:09.340
<v Giovanni Barallari>They're no longer processes.

00:23:10.200 --> 00:23:11.520
<v Giovanni Barallari>And I can skip all the dance

00:23:11.620 --> 00:23:13.160
<v Giovanni Barallari>of, yeah, open the socket,

00:23:14.140 --> 00:23:15.500
<v Giovanni Barallari>but just bind

00:23:15.500 --> 00:23:17.440
<v Giovanni Barallari>on that, but not on listen, because then

00:23:17.640 --> 00:23:19.440
<v Giovanni Barallari>it gets to be shared across processes

00:23:19.960 --> 00:23:21.500
<v Giovanni Barallari>and so each worker can have his own

00:23:21.660 --> 00:23:23.540
<v Giovanni Barallari>backlog. It's a mess, man.

00:23:23.740 --> 00:23:25.700
<v Giovanni Barallari>So on the free-threaded variant,

00:23:26.200 --> 00:23:28.100
<v Giovanni Barallari>Like, Grenian already, like, used threads.

00:23:29.799 --> 00:23:35.280
<v Giovanni Barallari>The subtlety is that each thread has its own event loop.

00:23:35.510 --> 00:23:41.280
<v Giovanni Barallari>So what Grenian cannot do today is sharing things between those threads.

00:23:41.560 --> 00:23:49.180
<v Giovanni Barallari>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.

00:23:49.250 --> 00:23:50.120
<v Giovanni Barallari>Like, there's no...

00:23:50.310 --> 00:23:51.780
<v Giovanni Barallari>You cannot cross boundaries, right?

00:23:52.060 --> 00:23:54.040
<v Giovanni Barallari>when you await for something,

00:23:54.130 --> 00:23:55.260
<v Giovanni Barallari>when you suspend for something,

00:23:56.440 --> 00:23:57.500
<v Giovanni Barallari>that's the loop,

00:23:57.750 --> 00:23:59.020
<v Giovanni Barallari>the event loop you're running into.

00:23:59.980 --> 00:24:01.540
<v Michael Kennedy>Which is a big limitation for servers.

00:24:02.300 --> 00:24:03.320
<v Michael Kennedy>Yeah, and one is calling await

00:24:03.540 --> 00:24:04.400
<v Michael Kennedy>and another is calling await

00:24:04.680 --> 00:24:06.060
<v Michael Kennedy>or maybe doing some work,

00:24:06.260 --> 00:24:07.860
<v Michael Kennedy>but if they were the same event loop,

00:24:08.220 --> 00:24:09.180
<v Michael Kennedy>one thing could run

00:24:09.420 --> 00:24:10.240
<v Michael Kennedy>because the other is awaiting,

00:24:10.380 --> 00:24:12.400
<v Michael Kennedy>but they're actually separate altogether, right?

00:24:12.560 --> 00:24:15.460
<v Giovanni Barallari>Yeah, and also you can still end up

00:24:15.460 --> 00:24:16.720
<v Giovanni Barallari>in these weird conditions

00:24:16.930 --> 00:24:19.360
<v Giovanni Barallari>in which maybe, I don't know,

00:24:19.440 --> 00:24:21.139
<v Giovanni Barallari>50% of your connection

00:24:21.740 --> 00:24:24.640
<v Giovanni Barallari>we're bound to a specific event loop.

00:24:25.180 --> 00:24:28.080
<v Giovanni Barallari>And so you also have like unbalancement

00:24:28.190 --> 00:24:29.420
<v Giovanni Barallari>between like the different threads.

00:24:29.510 --> 00:24:30.640
<v Giovanni Barallari>So you have like one thread

00:24:30.800 --> 00:24:32.880
<v Giovanni Barallari>that receives the vast majority of things,

00:24:33.060 --> 00:24:34.100
<v Giovanni Barallari>the other thread doing nothing.

00:24:35.480 --> 00:24:35.640
<v Giovanni Barallari>Yeah.

00:24:35.860 --> 00:24:36.000
<v Giovanni Barallari>Yeah.

00:24:36.390 --> 00:24:38.380
<v Giovanni Barallari>Let's say like we have new opportunities.

00:24:39.200 --> 00:24:41.080
<v Giovanni Barallari>We kind of lack some libraries,

00:24:41.370 --> 00:24:42.700
<v Giovanni Barallari>like some libraries behavior

00:24:43.700 --> 00:24:45.620
<v Giovanni Barallari>that can actually give us like

00:24:45.880 --> 00:24:47.580
<v Giovanni Barallari>all the actual meat here.

00:24:48.600 --> 00:24:48.900
<v Michael Kennedy>Agreed.

00:24:49.020 --> 00:24:53.600
<v Michael Kennedy>I want to talk about one area before we dive in, the Tone.io,

00:24:53.820 --> 00:24:55.740
<v Michael Kennedy>and that's just asyncio event loops.

00:24:56.120 --> 00:24:57.460
<v Michael Kennedy>And I know during your presentation,

00:24:57.700 --> 00:25:00.240
<v Michael Kennedy>so you gave a talk on this at EuroPython this year,

00:25:00.340 --> 00:25:04.120
<v Michael Kennedy>and I'll link to the topic right here in the moment.

00:25:05.140 --> 00:25:06.920
<v Michael Kennedy>But there are people like, oh, well, we have uvloops,

00:25:07.040 --> 00:25:08.300
<v Michael Kennedy>so doesn't that kind of solve it?

00:25:08.500 --> 00:25:10.980
<v Michael Kennedy>And I think that's a really interesting thought

00:25:11.180 --> 00:25:14.080
<v Michael Kennedy>because without calling out anybody, I didn't intend to call out anybody.

00:25:14.240 --> 00:25:18.540
<v Michael Kennedy>I think it just highlights in the Python space,

00:25:18.940 --> 00:25:22.240
<v Michael Kennedy>There's just not a lot of thinking about what concurrency means,

00:25:22.550 --> 00:25:24.660
<v Michael Kennedy>why it matters, how you program for it.

00:25:25.460 --> 00:25:26.760
<v Michael Kennedy>And that's going to be a challenge.

00:25:27.020 --> 00:25:28.620
<v Michael Kennedy>I think that's part of the challenge of what you're talking about

00:25:28.660 --> 00:25:30.180
<v Michael Kennedy>with the adoption of free-threaded Python.

00:25:30.330 --> 00:25:34.380
<v Michael Kennedy>But I think also it is a challenge in the sense that

00:25:34.720 --> 00:25:37.520
<v Michael Kennedy>thinking about making the event loop faster by, I don't know,

00:25:37.720 --> 00:25:39.900
<v Michael Kennedy>20% or whatever uvloop actually does, which is great,

00:25:40.100 --> 00:25:43.300
<v Michael Kennedy>it doesn't really address the fact that you're still only on one thread.

00:25:43.800 --> 00:25:47.400
<v Michael Kennedy>Your event loop can access, like, I just, against probably better judgment,

00:25:47.600 --> 00:25:51.120
<v Michael Kennedy>I just bought a Mac Studio Max, which has 18 cores.

00:25:51.559 --> 00:25:55.700
<v Michael Kennedy>If I write one thread, that's about like 5% of the capacity of that machine.

00:25:56.300 --> 00:26:01.600
<v Michael Kennedy>So what does it matter if it's 4.9% or 5.1% depending on which loop?

00:26:01.840 --> 00:26:02.520
<v Michael Kennedy>I don't care.

00:26:02.640 --> 00:26:04.120
<v Michael Kennedy>That is broken either way.

00:26:04.280 --> 00:26:06.400
<v Michael Kennedy>I want 95% capacity.

00:26:06.780 --> 00:26:07.080
<v Michael Kennedy>Yes.

00:26:07.280 --> 00:26:07.560
<v Michael Kennedy>Right?

00:26:07.940 --> 00:26:08.920
<v Michael Kennedy>And that's, I don't know.

00:26:08.960 --> 00:26:11.940
<v Michael Kennedy>I thought it would be worthwhile to kind of like talk about uvloop a little bit.

00:26:12.020 --> 00:26:12.940
<v Michael Kennedy>And you built our loop.

00:26:13.100 --> 00:26:14.860
<v Michael Kennedy>And those things are great in their building blocks.

00:26:14.960 --> 00:26:15.100
<v Michael Kennedy>Yeah.

00:26:15.180 --> 00:26:16.820
<v Michael Kennedy>They're not the panacea.

00:26:16.920 --> 00:26:17.440
<v Michael Kennedy>They're not the fix.

00:26:17.860 --> 00:26:25.900
<v Giovanni Barallari>Yeah, and we also now have Marcelo Trzesitsky, the maintainer of UVCore and Starlet.

00:26:27.680 --> 00:26:39.500
<v Giovanni Barallari>It has recently published Zoovloop, which is another event loop for Python relying on libUV, but it's written in Zig.

00:26:40.900 --> 00:26:44.280
<v Giovanni Barallari>But yeah, as you said, all of these event loops,

00:26:46.220 --> 00:26:52.580
<v Giovanni Barallari>yes, they can optimize some of the hot part of the cycle.

00:26:53.760 --> 00:26:55.320
<v Giovanni Barallari>But basically, that's it.

00:26:55.820 --> 00:27:00.960
<v Giovanni Barallari>Nothing really changed the shape of how things work.

00:27:02.080 --> 00:27:07.939
<v Giovanni Barallari>And so, yes, for sure, you should actually install

00:27:08.100 --> 00:27:13.780
<v Giovanni Barallari>uvloop or r loop or zoo v loop or whatever to to speed up like because that's performance you're

00:27:13.820 --> 00:27:19.320
<v Giovanni Barallari>leaving on the table regardless right like so uh it's good to have this project because they also

00:27:19.480 --> 00:27:26.500
<v Giovanni Barallari>like can light uh some of the inefficiencies of a sink io in my opinion um so i think it's still

00:27:26.550 --> 00:27:31.960
<v Giovanni Barallari>good to have these projects and you should use them um but yeah they want like the best you can

00:27:31.920 --> 00:27:39.520
<v Giovanni Barallari>GAT is probably like in the order of like 20% in, and we're talking about like row throughput

00:27:39.940 --> 00:27:46.180
<v Giovanni Barallari>if you're just doing TCP and nothing else, which is nobody does like, like nothing does

00:27:46.380 --> 00:27:47.020
<v Giovanni Barallari>nothing out there.

00:27:47.180 --> 00:27:47.240
<v Giovanni Barallari>Right?

00:27:47.320 --> 00:27:48.800
<v Giovanni Barallari>Like we do a bunch of stuff.

00:27:49.160 --> 00:27:49.400
<v Giovanni Barallari>Yeah.

00:27:49.480 --> 00:27:52.980
<v Michael Kennedy>If you construct an example where basically all you're doing is waiting very efficiently,

00:27:53.410 --> 00:27:55.180
<v Michael Kennedy>then you start to see those, those deviate.

00:27:55.180 --> 00:27:59.460
<v Michael Kennedy>And also if you've got, like, I've got 10 tasks that run over two seconds.

00:27:59.940 --> 00:28:00.660
<v Michael Kennedy>It does zero.

00:28:00.690 --> 00:28:01.220
<v Michael Kennedy>It doesn't matter.

00:28:01.600 --> 00:28:05.080
<v Michael Kennedy>This only matters when you're doing very, very fine-grained work and tons of it,

00:28:05.080 --> 00:28:07.900
<v Michael Kennedy>and there's lots of switching and that sort of thing, right?

00:28:08.160 --> 00:28:09.080
<v Giovanni Barallari>Yes, yes.

00:28:11.120 --> 00:28:16.140
<v Giovanni Barallari>And so, yes, I guess this was one of my realizations.

00:28:16.570 --> 00:28:20.300
<v Giovanni Barallari>Because, again, I worked on ArtLoop mainly last year.

00:28:21.470 --> 00:28:24.900
<v Giovanni Barallari>And working on ArtLoop made me realize two things.

00:28:25.860 --> 00:28:31.260
<v Giovanni Barallari>The first thing is that asimkayo, it's overcomplicated sometimes.

00:28:31.540 --> 00:28:39.160
<v Giovanni Barallari>It's so fun fact at the presentation I did about that loop in Python Italy 25.

00:28:40.450 --> 00:28:51.900
<v Giovanni Barallari>I had a slide asking to the audience, what's the difference between a protocol and a transport in Async.io.

00:28:52.220 --> 00:29:01.300
<v Giovanni Barallari>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.

00:29:01.860 --> 00:29:05.280
<v Giovanni Barallari>they try to do that like in three different ways,

00:29:06.340 --> 00:29:07.720
<v Giovanni Barallari>which to my experience,

00:29:07.880 --> 00:29:10.320
<v Giovanni Barallari>like if you need three different ways

00:29:10.380 --> 00:29:11.620
<v Giovanni Barallari>to explain the same thing,

00:29:12.020 --> 00:29:13.800
<v Giovanni Barallari>something is deeply wrong.

00:29:14.620 --> 00:29:17.860
<v Michael Kennedy>Definitely violates an Einstein core philosophy.

00:29:18.180 --> 00:29:18.360
<v Giovanni Barallari>Yes.

00:29:19.260 --> 00:29:20.400
<v Giovanni Barallari>Also because like, again,

00:29:20.660 --> 00:29:22.460
<v Giovanni Barallari>if we think about the Zen of Python,

00:29:23.140 --> 00:29:25.280
<v Giovanni Barallari>like simple is better than complex, right?

00:29:27.000 --> 00:29:29.440
<v Giovanni Barallari>So that was my first realization, right?

00:29:30.440 --> 00:29:35.480
<v Giovanni Barallari>Because, again, like you have a bunch of primitives, like you have events, futures.

00:29:37.400 --> 00:29:43.880
<v Giovanni Barallari>Oh, by the way, every time they get spawned into the event loop, they get wrapped into a task.

00:29:44.200 --> 00:29:45.780
<v Giovanni Barallari>What is a task? Nobody knows.

00:29:46.920 --> 00:29:51.220
<v Giovanni Barallari>Oh, and by the way, when they actually run inside the event loop, they became handles.

00:29:52.380 --> 00:29:59.820
<v Giovanni Barallari>So we have a bunch of this weird thing that they're not really exposed to you if you just write async code.

00:30:00.180 --> 00:30:06.020
<v Giovanni Barallari>But if you need to debug something when something is wrong, then you need all of that to understand what's going on, right?

00:30:06.800 --> 00:30:14.120
<v Giovanni Barallari>And the other realization, again, was, okay, I reached a point in which there's no other possible optimization in our book.

00:30:14.300 --> 00:30:20.620
<v Giovanni Barallari>Because the vast majority of CPU time spent here is around AsyncIO primitives.

00:30:21.000 --> 00:30:22.560
<v Giovanni Barallari>And I have one single thread.

00:30:22.960 --> 00:30:28.920
<v Giovanni Barallari>And so, like, the only, when I came to realize this, I just said, okay, wait a second.

00:30:29.100 --> 00:30:35.020
<v Giovanni Barallari>Is that, is really that hard to start like scratch whiteboard?

00:30:35.420 --> 00:30:40.380
<v Giovanni Barallari>Like from the beginning, like let's just assume for a second, like asyncio never existed.

00:30:40.720 --> 00:30:42.920
<v Giovanni Barallari>And I want to do like async stuff in Python.

00:30:43.160 --> 00:30:43.860
<v Giovanni Barallari>What that look like?

00:30:43.980 --> 00:30:47.300
<v Giovanni Barallari>Like what, what does it mean to make like an asynchronous runtime in Python?

00:30:47.540 --> 00:30:48.100
<v Giovanni Barallari>Is that hard?

00:30:48.420 --> 00:30:55.119
<v Giovanni Barallari>And I don't know, like, I guess three or four weeks after this, I just got like an asynchronous

00:30:55.140 --> 00:30:57.040
<v Giovanni Barallari>runtime, a complete alternative

00:30:57.240 --> 00:30:59.040
<v Giovanni Barallari>asynchronous runtime running on Python with

00:30:59.220 --> 00:30:59.700
<v Giovanni Barallari>multiple threads.

00:31:01.280 --> 00:31:03.080
<v Michael Kennedy>To be clear on free-threaded Python, right?

00:31:03.320 --> 00:31:05.160
<v Giovanni Barallari>Yes, free-threaded Python only because

00:31:05.650 --> 00:31:07.160
<v Giovanni Barallari>again, to have like real threads

00:31:07.530 --> 00:31:09.260
<v Giovanni Barallari>I mean, the design is to have

00:31:09.400 --> 00:31:11.120
<v Giovanni Barallari>real threads, so the moment you have

00:31:11.120 --> 00:31:12.860
<v Giovanni Barallari>the gill, I say, no, no, no,

00:31:13.200 --> 00:31:15.060
<v Giovanni Barallari>runtime error, change your

00:31:15.280 --> 00:31:17.200
<v Michael Kennedy>interpreter because this is not good,

00:31:17.380 --> 00:31:17.520
<v Michael Kennedy>right?

00:31:19.200 --> 00:31:21.080
<v Michael Kennedy>This portion of Talk Python I May is brought to you by

00:31:21.300 --> 00:31:23.040
<v Michael Kennedy>our AI tools. You know that

00:31:23.180 --> 00:31:25.100
<v Michael Kennedy>thing where you ask an AI something about

00:31:25.120 --> 00:31:30.860
<v Michael Kennedy>Python and it confidently tells you about a library version from 18 months ago? Well, we fixed that,

00:31:31.100 --> 00:31:37.480
<v Michael Kennedy>at least for our shows. Talk Python and Python Bytes both have MCP servers now. Connect Talk Python

00:31:37.530 --> 00:31:43.780
<v Michael Kennedy>and your AI can search over 550 episodes, full transcripts, every guest in the entire course

00:31:44.120 --> 00:31:49.820
<v Michael Kennedy>catalog. Connect Python Bytes and you get almost 500 episodes of Python news going back to 2026,

00:31:50.520 --> 00:31:54.940
<v Michael Kennedy>including every link we've ever put in the show notes. That means you can say things like,

00:31:55.060 --> 00:32:02.460
<v Michael Kennedy>ask Talk Python what astral joining OpenAI means for uv or what has Python bytes said about uv

00:32:02.860 --> 00:32:06.740
<v Michael Kennedy>and get real answers with real links, not hallucination. Name the show in your prompt

00:32:06.920 --> 00:32:12.900
<v Michael Kennedy>and your AI knows exactly where to look. And if you live in the terminal, Talk Python also has a CLI

00:32:12.930 --> 00:32:20.900
<v Michael Kennedy>too. One line, uv tool install talk-python-cli and then search episodes, transcripts, guests,

00:32:21.220 --> 00:32:26.880
<v Michael Kennedy>courses without ever opening a browser. It's open source and it outputs text, JSON, and Markdown,

00:32:26.960 --> 00:32:31.980
<v Michael Kennedy>so it feeds the AI tools that don't speak MCP yet. And here's the real reason I built it.

00:32:32.460 --> 00:32:38.320
<v Michael Kennedy>Both shows cover around 10 years of Python history. The people, the decisions, the packages that took

00:32:38.380 --> 00:32:45.460
<v Michael Kennedy>over, and the ones that quietly didn't. This enhanced access is free. No account, no API key,

00:32:45.560 --> 00:32:52.140
<v Michael Kennedy>nothing to buy. This history should belong to all of us. Visit talkpython.fm and Python Bytes and

00:32:52.260 --> 00:32:56.840
<v Michael Kennedy>click the MCP link in the nav bar. Connect them right now to your agents so that they'll be

00:32:57.080 --> 00:33:02.440
<v Michael Kennedy>accessible anytime you need them in the future. And so yeah, that was like kind of the landscape

00:33:02.980 --> 00:33:09.180
<v Giovanni Barallari>in which I started building Tonio or Tonio. You can pronounce it like however you want. Like I'm

00:33:09.300 --> 00:33:13.540
<v Michael Kennedy>Italian, so I say Tonio, of course. I think obviously, well, you have to do Tonio.

00:33:14.780 --> 00:33:15.980
<v Michael Kennedy>So it's really good.

00:33:16.140 --> 00:33:21.900
<v Michael Kennedy>But as it pairs to asyncio, tone IO also kind of like as a hat to that, right?

00:33:22.180 --> 00:33:22.300
<v Michael Kennedy>Yeah.

00:33:22.560 --> 00:33:22.660
<v Michael Kennedy>Yeah.

00:33:23.280 --> 00:33:24.040
<v Giovanni Barallari>But we have Trio.

00:33:24.460 --> 00:33:26.760
<v Giovanni Barallari>So that's not Trio.

00:33:28.020 --> 00:33:30.280
<v Michael Kennedy>Trio is living there a little bit as well.

00:33:30.660 --> 00:33:30.740
<v Michael Kennedy>Yeah.

00:33:30.800 --> 00:33:33.700
<v Michael Kennedy>So this is a really interesting project that you have.

00:33:33.880 --> 00:33:37.140
<v Michael Kennedy>And it basically says, what if we actually had threads?

00:33:37.560 --> 00:33:44.000
<v Michael Kennedy>One of the things that's endlessly frustrated me about Python and asyncio and consequently

00:33:44.020 --> 00:33:48.220
<v Michael Kennedy>I think the language implementation of async await is it's really good.

00:33:48.320 --> 00:33:49.200
<v Michael Kennedy>I think, you know,

00:33:49.840 --> 00:33:52.940
<v Michael Kennedy>the way it sort of turns async code into what looks like structurally

00:33:54.299 --> 00:33:57.060
<v Michael Kennedy>serializable code or serial code is really, really nice.

00:33:57.320 --> 00:33:58.160
<v Michael Kennedy>But the,

00:33:58.620 --> 00:34:02.800
<v Michael Kennedy>the fact that the developer has to juggle loops and which loop and that loop.

00:34:02.920 --> 00:34:03.940
<v Michael Kennedy>No, that's not the right loop.

00:34:04.240 --> 00:34:07.200
<v Michael Kennedy>Like so many times I've been, Oh, I want to do this request.

00:34:07.640 --> 00:34:10.659
<v Michael Kennedy>Oh, are you using court or an async web framework? You,

00:34:10.879 --> 00:34:12.840
<v Michael Kennedy>there is an async loop, but not that one.

00:34:12.899 --> 00:34:14.720
<v Michael Kennedy>you need the one created by the web framework.

00:34:14.860 --> 00:34:17.460
<v Michael Kennedy>Like, oh, did you initialize the async database connection

00:34:18.240 --> 00:34:19.700
<v Michael Kennedy>before it ran the web thing?

00:34:19.720 --> 00:34:21.100
<v Michael Kennedy>And like, nope, that's the wrong one.

00:34:21.500 --> 00:34:24.320
<v Michael Kennedy>And there's all this weird juggling that's just so janky.

00:34:25.000 --> 00:34:26.659
<v Michael Kennedy>And I'm pretty sure it's janky

00:34:27.240 --> 00:34:28.940
<v Michael Kennedy>because we didn't have true threads.

00:34:29.460 --> 00:34:31.500
<v Michael Kennedy>And the possibility, oh, what if you crisscross this?

00:34:32.160 --> 00:34:33.340
<v Michael Kennedy>Everything sort of falls apart.

00:34:33.540 --> 00:34:35.540
<v Michael Kennedy>And if you could just say, look, it's just multi-threaded.

00:34:35.760 --> 00:34:37.740
<v Michael Kennedy>And there is a thing that called the loop

00:34:37.780 --> 00:34:38.840
<v Michael Kennedy>and that's where stuff runs.

00:34:39.300 --> 00:34:41.080
<v Michael Kennedy>And I don't care who started it.

00:34:41.220 --> 00:34:41.760
<v Michael Kennedy>It's our loop.

00:34:41.899 --> 00:34:47.020
<v Michael Kennedy>When I need a loop, the runtime Python itself has a loop for me and I'll use it.

00:34:47.320 --> 00:34:47.419
<v Michael Kennedy>Right.

00:34:47.720 --> 00:34:49.639
<v Michael Kennedy>And I feel like you sort of took that philosophy a little bit.

00:34:49.679 --> 00:34:49.800
<v Michael Kennedy>Right.

00:34:50.320 --> 00:34:50.480
<v Giovanni Barallari>Yeah.

00:34:50.639 --> 00:34:58.180
<v Giovanni Barallari>I'd say like the number one inspiration for Sonio is Tokyo, which is the number one Rust

00:34:58.460 --> 00:34:59.100
<v Giovanni Barallari>asynchronous runtime.

00:35:00.620 --> 00:35:03.600
<v Giovanni Barallari>That's probably because like I spend like in Rust a bunch of time.

00:35:04.900 --> 00:35:09.900
<v Giovanni Barallari>So yeah, like, I don't know, for example, like the weird thing to me is like, we still like,

00:35:09.980 --> 00:35:14.020
<v Giovanni Barallari>if you just want to do like an asynchronous program in Python,

00:35:14.410 --> 00:35:18.160
<v Giovanni Barallari>like a program, like let's say a script or, I don't know,

00:35:18.320 --> 00:35:21.680
<v Giovanni Barallari>like a simple CLI, we still need to do like a synch.io run.

00:35:22.320 --> 00:35:22.920
<v Giovanni Barallari>Like why?

00:35:23.800 --> 00:35:28.080
<v Giovanni Barallari>Like the, yeah, the amount of things you need to know

00:35:28.680 --> 00:35:33.220
<v Giovanni Barallari>and decide and stuff there is like completely different

00:35:33.440 --> 00:35:34.340
<v Giovanni Barallari>from other languages.

00:35:34.520 --> 00:35:34.600
<v Giovanni Barallari>Yeah.

00:35:34.690 --> 00:35:36.860
<v Giovanni Barallari>Because again, like think about JavaScript.

00:35:37.670 --> 00:35:38.520
<v Giovanni Barallari>You don't decide anything.

00:35:38.580 --> 00:35:40.180
<v Giovanni Barallari>Like that's async in JavaScript.

00:35:40.900 --> 00:35:41.260
<v Michael Kennedy>That's true.

00:35:41.640 --> 00:35:43.140
<v Michael Kennedy>Well, I'll tell you a little bit why you can't just,

00:35:43.460 --> 00:35:44.560
<v Michael Kennedy>why you still got to call async run

00:35:44.720 --> 00:35:46.460
<v Michael Kennedy>because there's like a foundational layer

00:35:46.980 --> 00:35:47.740
<v Michael Kennedy>that's not present.

00:35:48.300 --> 00:35:50.120
<v Michael Kennedy>And it's your job to write the foundation

00:35:50.420 --> 00:35:53.580
<v Michael Kennedy>on which asyncio executes, right?

00:35:53.960 --> 00:35:55.080
<v Michael Kennedy>Like it's still your job to figure,

00:35:55.220 --> 00:35:56.820
<v Michael Kennedy>okay, how do I actually create a loop?

00:35:56.880 --> 00:35:57.640
<v Michael Kennedy>How do I run a loop?

00:35:57.740 --> 00:35:59.120
<v Michael Kennedy>How do I, like, it's your job.

00:35:59.520 --> 00:36:01.340
<v Michael Kennedy>You can't just create an async def method

00:36:01.500 --> 00:36:04.100
<v Michael Kennedy>and call it because there's nowhere for it to go

00:36:04.280 --> 00:36:05.440
<v Michael Kennedy>until you go create, you know what I mean?

00:36:05.540 --> 00:36:06.120
<v Michael Kennedy>There's like, there's just,

00:36:06.800 --> 00:36:08.660
<v Giovanni Barallari>But the foundation isn't quite there.

00:36:08.960 --> 00:36:10.800
<v Giovanni Barallari>But again, it's an entry point, right?

00:36:11.700 --> 00:36:14.080
<v Giovanni Barallari>So in Tonio, for example, yes, you have Tonio run.

00:36:14.210 --> 00:36:18.120
<v Giovanni Barallari>You want to instruct everything and configure the runtime and do whatever you want.

00:36:18.180 --> 00:36:19.340
<v Giovanni Barallari>Yes, you can do that.

00:36:19.550 --> 00:36:20.800
<v Giovanni Barallari>You don't want to do that.

00:36:21.060 --> 00:36:23.600
<v Giovanni Barallari>You have a single decorator, which is Tonio main.

00:36:24.020 --> 00:36:30.780
<v Giovanni Barallari>You put it on your function, your entry point in your program, and it's done.

00:36:32.500 --> 00:36:37.300
<v Michael Kennedy>Yeah. All right. Before we talk about that, before we get too much in the weeds of it,

00:36:37.400 --> 00:36:43.480
<v Michael Kennedy>why don't you just give us like, talk us through writing a program in this. There's also two ways,

00:36:43.580 --> 00:36:47.920
<v Michael Kennedy>like maybe you could, after you talk about this, you could like sort of get into the,

00:36:48.380 --> 00:36:55.600
<v Michael Kennedy>what this concept of colored functions versus not, and sometimes call async code viral code. And like,

00:36:55.680 --> 00:37:02.360
<v Michael Kennedy>let's just like talk through the differences here. Yeah. So, okay. Yes. Tonyo has two

00:37:02.360 --> 00:37:06.940
<v Giovanni Barallari>supports two different syntax modes.

00:37:08.030 --> 00:37:11.960
<v Giovanni Barallari>And that's because, again, when I designed this,

00:37:12.580 --> 00:37:15.180
<v Giovanni Barallari>given that I just throw everything away

00:37:15.670 --> 00:37:16.620
<v Giovanni Barallari>and started from scratch,

00:37:17.290 --> 00:37:18.820
<v Giovanni Barallari>I ended up having a system

00:37:19.150 --> 00:37:23.580
<v Giovanni Barallari>which didn't really require async await syntax

00:37:23.730 --> 00:37:26.440
<v Giovanni Barallari>or a notation because at the end of the day,

00:37:26.550 --> 00:37:27.760
<v Giovanni Barallari>behind async await notation,

00:37:28.060 --> 00:37:29.740
<v Giovanni Barallari>those are generated, right?

00:37:30.740 --> 00:37:45.840
<v Giovanni Barallari>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?

00:37:46.460 --> 00:37:55.400
<v Giovanni Barallari>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.

00:37:56.180 --> 00:37:57.560
<v Giovanni Barallari>OK, you don't like that.

00:37:58.000 --> 00:37:58.320
<v Giovanni Barallari>Guess what?

00:37:58.640 --> 00:38:00.980
<v Giovanni Barallari>Tonya also have just a yield syntax.

00:38:01.200 --> 00:38:10.240
<v Giovanni Barallari>So instead of await, you just yield from the next coroutine you want to suspend for or wait for.

00:38:11.320 --> 00:38:11.800
<v Giovanni Barallari>And that's it.

00:38:11.800 --> 00:38:15.460
<v Giovanni Barallari>You don't need to write async def everywhere.

00:38:17.480 --> 00:38:19.960
<v Giovanni Barallari>To be here is still colored, right?

00:38:20.140 --> 00:38:26.020
<v Giovanni Barallari>Because the moment you have a generator function, then whatever it calls it before has to be a generator.

00:38:26.080 --> 00:38:33.060
<v Giovanni Barallari>as well. But I guess my point was mainly like, okay, to support the syntax doesn't really take

00:38:33.210 --> 00:38:40.160
<v Giovanni Barallari>that much of a work. And once it was settled in, I mean, it's there and people can decide by,

00:38:40.580 --> 00:38:48.120
<v Giovanni Barallari>hey, you dislike one notation? Sure, just use the other one. But yes, let's say for people

00:38:48.580 --> 00:38:55.220
<v Giovanni Barallari>familiar with AsyncIO, the syntax, like the AsyncAwait syntax is very similar to AsyncIO.

00:38:55.900 --> 00:39:06.540
<v Giovanni Barallari>So you basically have your coroutine, so your sync dev, whatever you want, and you await primitives or stuff.

00:39:08.380 --> 00:39:20.800
<v Giovanni Barallari>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.

00:39:21.440 --> 00:39:28.080
<v Giovanni Barallari>We, of course, have way less primitives because, again, like, as I said before, AsyncIO has too many primitives.

00:39:29.380 --> 00:39:39.660
<v Giovanni Barallari>And the only big difference from AsyncIO, let's say, native people is that you have, like, spawn methods.

00:39:40.260 --> 00:39:41.879
<v Giovanni Barallari>So instead of saying...

00:39:43.460 --> 00:39:47.720
<v Giovanni Barallari>So in AsyncIO, when you want to make things,

00:39:48.200 --> 00:39:50.200
<v Giovanni Barallari>like spawn several tasks

00:39:50.640 --> 00:39:52.380
<v Giovanni Barallari>and then wait for all of them to complete,

00:39:52.680 --> 00:39:53.500
<v Giovanni Barallari>you usually do...

00:39:53.500 --> 00:39:56.080
<v Giovanni Barallari>You have different ways of doing this, right?

00:39:56.260 --> 00:39:58.620
<v Giovanni Barallari>You can have a task set.

00:39:59.240 --> 00:40:02.780
<v Giovanni Barallari>You can have scopes from Trio.

00:40:04.340 --> 00:40:06.060
<v Giovanni Barallari>But I mean, you can use gather

00:40:06.300 --> 00:40:08.980
<v Giovanni Barallari>so you can create tasks and then gather.

00:40:10.380 --> 00:40:10.840
<v Giovanni Barallari>with SyncIO.

00:40:10.920 --> 00:40:12.800
<v Giovanni Barallari>So Tonio is like,

00:40:12.980 --> 00:40:14.360
<v Giovanni Barallari>to do this is,

00:40:14.820 --> 00:40:16.420
<v Giovanni Barallari>you just have like two methods.

00:40:16.500 --> 00:40:17.160
<v Giovanni Barallari>You either spawn

00:40:19.000 --> 00:40:19.740
<v Giovanni Barallari>asynchronous stuff

00:40:19.980 --> 00:40:21.200
<v Giovanni Barallari>or you spawn something

00:40:21.200 --> 00:40:21.900
<v Giovanni Barallari>that is blocking

00:40:22.460 --> 00:40:23.480
<v Giovanni Barallari>and then the primitive

00:40:24.400 --> 00:40:25.920
<v Giovanni Barallari>is spawn blocking.

00:40:26.980 --> 00:40:28.140
<v Giovanni Barallari>The other major difference

00:40:28.400 --> 00:40:29.000
<v Giovanni Barallari>from a SyncIO

00:40:29.080 --> 00:40:30.680
<v Giovanni Barallari>is that when you spawn something

00:40:31.180 --> 00:40:33.000
<v Giovanni Barallari>that gets run immediately.

00:40:34.560 --> 00:40:35.940
<v Giovanni Barallari>Whereas like in a SyncIO

00:40:35.960 --> 00:40:37.080
<v Giovanni Barallari>this is true only

00:40:37.340 --> 00:40:38.440
<v Giovanni Barallari>if you create a task.

00:40:38.960 --> 00:40:41.380
<v Giovanni Barallari>Otherwise, everything else is eager.

00:40:41.960 --> 00:40:45.900
<v Giovanni Barallari>So in order to run whatever you want to wait,

00:40:45.980 --> 00:40:46.980
<v Giovanni Barallari>you need to await.

00:40:47.300 --> 00:40:49.600
<v Giovanni Barallari>So in AsyncIO, the vast majority of operations

00:40:50.540 --> 00:40:54.100
<v Giovanni Barallari>bound together the launching the operation

00:40:54.380 --> 00:40:55.440
<v Giovanni Barallari>and waiting for the result.

00:40:55.740 --> 00:40:58.320
<v Giovanni Barallari>Unless you create a task that starts immediately

00:40:58.720 --> 00:40:59.880
<v Giovanni Barallari>and then you await later, right?

00:41:00.820 --> 00:41:02.640
<v Giovanni Barallari>In Tonya, everything is wrapped around the spawn

00:41:02.840 --> 00:41:05.440
<v Giovanni Barallari>because you can call spawn and forget about it

00:41:05.660 --> 00:41:08.760
<v Giovanni Barallari>or you can await Tonya spawn to wait for the results, right?

00:41:08.900 --> 00:41:12.000
<v Giovanni Barallari>Or you can park the result of the spawn, the join, and join later.

00:41:12.980 --> 00:41:16.640
<v Giovanni Barallari>So that's, I don't know if, like, maybe it's more confusing than,

00:41:17.380 --> 00:41:19.860
<v Giovanni Barallari>it's hard to explain it simply.

00:41:20.260 --> 00:41:24.680
<v Michael Kennedy>Maybe, but one of the challenges I've seen with standard asyncIO

00:41:24.780 --> 00:41:28.560
<v Michael Kennedy>is you want to create a bunch of work and let it run,

00:41:28.820 --> 00:41:30.340
<v Michael Kennedy>and then you want to get the answers back.

00:41:30.520 --> 00:41:33.140
<v Michael Kennedy>So a naive way would be, like, call a bunch of stuff,

00:41:34.140 --> 00:41:35.980
<v Michael Kennedy>and then when you call await,

00:41:36.380 --> 00:41:38.980
<v Michael Kennedy>It's like you've got to create them not started

00:41:39.280 --> 00:41:40.440
<v Michael Kennedy>and then start all of them

00:41:40.530 --> 00:41:42.500
<v Michael Kennedy>and then go through each one and await them.

00:41:42.730 --> 00:41:44.520
<v Michael Kennedy>Whereas this way, you get a list back

00:41:44.590 --> 00:41:45.680
<v Michael Kennedy>and you just await them in order

00:41:45.990 --> 00:41:47.640
<v Michael Kennedy>or await one after another somehow.

00:41:48.020 --> 00:41:49.380
<v Michael Kennedy>They're already started, right?

00:41:49.470 --> 00:41:51.220
<v Michael Kennedy>And so there's like the skip of this like,

00:41:51.330 --> 00:41:52.400
<v Michael Kennedy>okay, I've got all the things

00:41:52.540 --> 00:41:53.540
<v Michael Kennedy>that are going to become tasks,

00:41:53.660 --> 00:41:54.780
<v Michael Kennedy>but I got to turn them into tasks

00:41:54.900 --> 00:41:56.180
<v Michael Kennedy>so then I can await them

00:41:56.320 --> 00:41:57.400
<v Michael Kennedy>because if I regularly await them,

00:41:57.520 --> 00:41:58.980
<v Michael Kennedy>it'll still fall back to serial

00:41:59.500 --> 00:42:01.080
<v Michael Kennedy>just running on the event loop, you know?

00:42:01.340 --> 00:42:01.440
<v Giovanni Barallari>Yeah.

00:42:01.730 --> 00:42:02.920
<v Giovanni Barallari>And I mean, like in Tanya is the same.

00:42:02.990 --> 00:42:04.260
<v Giovanni Barallari>Like if you await a coroutine,

00:42:04.370 --> 00:42:06.340
<v Giovanni Barallari>it means like I want to wait

00:42:06.360 --> 00:42:08.360
<v Giovanni Barallari>that to happen, right? If you don't want to

00:42:08.460 --> 00:42:10.300
<v Giovanni Barallari>just spawn, park

00:42:10.500 --> 00:42:12.220
<v Giovanni Barallari>the result of spawn into a

00:42:12.460 --> 00:42:14.220
<v Giovanni Barallari>variable, await later. I think

00:42:14.340 --> 00:42:15.120
<v Giovanni Barallari>there's an example

00:42:16.420 --> 00:42:17.480
<v Giovanni Barallari>down below in the page

00:42:19.240 --> 00:42:19.720
<v Giovanni Barallari>about

00:42:20.100 --> 00:42:21.960
<v Giovanni Barallari>spawning something and awaiting later.

00:42:24.520 --> 00:42:24.780
<v Giovanni Barallari>Somewhere.

00:42:26.220 --> 00:42:27.420
<v Giovanni Barallari>There's a bunch of documentation.

00:42:28.220 --> 00:42:28.960
<v Giovanni Barallari>Spawning there, yeah.

00:42:30.480 --> 00:42:31.760
<v Giovanni Barallari>But yeah, the idea

00:42:32.200 --> 00:42:34.000
<v Giovanni Barallari>is, and again, I didn't invent

00:42:34.040 --> 00:42:34.820
<v Giovanni Barallari>anything because

00:42:34.840 --> 00:42:41.240
<v Giovanni Barallari>the syntax. Yes, this is the example. So in this example, we compute like numbers,

00:42:41.550 --> 00:42:46.160
<v Giovanni Barallari>like stupid example, but it is to give the point. So we start like two functions,

00:42:46.850 --> 00:42:53.080
<v Giovanni Barallari>which computes numbers. Then we await for third function. So we immediately wait for the third

00:42:53.380 --> 00:43:00.260
<v Giovanni Barallari>result. And then we wait for the first two results. So in the meantime, so when you do the first poem,

00:43:00.660 --> 00:43:05.560
<v Giovanni Barallari>those two functions start already, like immediately at that point in your code,

00:43:06.660 --> 00:43:08.180
<v Giovanni Barallari>which kind of makes sense if you think about it,

00:43:08.230 --> 00:43:12.400
<v Giovanni Barallari>because it's like syntax-based, like you're invoking those coroutines, right?

00:43:12.980 --> 00:43:14.980
<v Giovanni Barallari>In that moment, you just don't await for them.

00:43:15.960 --> 00:43:20.820
<v Giovanni Barallari>And so at the end, like you just await for the parallel, actual parallel result.

00:43:21.220 --> 00:43:25.280
<v Giovanni Barallari>Like everything here is concurrent, but the only point in which you have parallel code,

00:43:25.580 --> 00:43:30.140
<v Giovanni Barallari>the actual parallel code is when you await for parallel, right?

00:43:30.920 --> 00:43:34.160
<v Giovanni Barallari>So that's the major difference from a Synco and Tonio.

00:43:35.000 --> 00:43:39.340
<v Giovanni Barallari>When you spawn stuff, that thing happened in parallel.

00:43:39.700 --> 00:43:43.320
<v Giovanni Barallari>So by default, Tonio starts with the number of threads

00:43:44.140 --> 00:43:46.340
<v Giovanni Barallari>equal to the number of your CPUs.

00:43:47.700 --> 00:43:52.980
<v Giovanni Barallari>The blocking, Tonio also has a blocking pool for blocking stuff.

00:43:54.360 --> 00:43:54.860
<v Giovanni Barallari>But that's separate.

00:43:54.900 --> 00:43:59.200
<v Giovanni Barallari>Let's say the main work loop that runs your code,

00:44:00.460 --> 00:44:04.280
<v Giovanni Barallari>by default, you have X threads per CPU cores.

00:44:05.380 --> 00:44:09.800
<v Giovanni Barallari>So if you run this on your M3 Max, M3 Studio Max,

00:44:10.740 --> 00:44:11.240
<v Giovanni Barallari>what was it?

00:44:11.600 --> 00:44:14.620
<v Giovanni Barallari>You will end up having 18 threads running stuff.

00:44:14.780 --> 00:44:14.920
<v Giovanni Barallari>Yeah.

00:44:15.360 --> 00:44:18.480
<v Giovanni Barallari>Which is kind of what we want in general, right?

00:44:19.300 --> 00:44:20.840
<v Giovanni Barallari>Like if I write a program-

00:44:20.840 --> 00:44:22.140
<v Michael Kennedy>I'm sure you can configure it, right?

00:44:22.220 --> 00:44:24.140
<v Michael Kennedy>You could configure the thread pool to say,

00:44:24.220 --> 00:44:26.580
<v Michael Kennedy>like, look, this thing has to be a good citizen.

00:44:26.820 --> 00:44:28.220
<v Michael Kennedy>Let's just take the number of CPUs,

00:44:28.320 --> 00:44:29.400
<v Michael Kennedy>divide by two or something like that

00:44:29.400 --> 00:44:30.460
<v Michael Kennedy>so I can still do other work.

00:44:30.680 --> 00:44:33.060
<v Giovanni Barallari>Yeah, you can like, again,

00:44:34.080 --> 00:44:36.700
<v Giovanni Barallari>that Tonyo main decorator

00:44:36.980 --> 00:44:38.360
<v Giovanni Barallari>or the Tonyo run method

00:44:40.840 --> 00:44:42.080
<v Giovanni Barallari>accepts some parameters

00:44:42.220 --> 00:44:43.680
<v Giovanni Barallari>so you can configure like the size

00:44:43.700 --> 00:44:45.220
<v Giovanni Barallari>of the standard thread pool,

00:44:45.640 --> 00:44:47.640
<v Giovanni Barallari>the maximum amount of blocking threads

00:44:47.840 --> 00:44:48.680
<v Giovanni Barallari>you want to spawn

00:44:49.280 --> 00:44:51.020
<v Giovanni Barallari>because that does get spawned on demand.

00:44:52.460 --> 00:44:58.660
<v Giovanni Barallari>you can configure if you want to have support for context bars because it's not that's another thing

00:44:58.940 --> 00:45:07.360
<v Giovanni Barallari>about python right like we kind of went from thread locals to context bars uh but in thonio

00:45:07.520 --> 00:45:12.840
<v Giovanni Barallari>that's a bit more complicated right because you have async code in multiple threads yeah because

00:45:12.930 --> 00:45:18.340
<v Giovanni Barallari>they used to leverage the thread right yeah so if you want to actually use context bars

00:45:18.360 --> 00:45:20.900
<v Giovanni Barallari>you have to tell to the runtime, right?

00:45:20.980 --> 00:45:24.360
<v Giovanni Barallari>Because otherwise, like, some side effects might be weird

00:45:24.940 --> 00:45:25.560
<v Giovanni Barallari>in that condition.

00:45:26.620 --> 00:45:26.960
<v Michael Kennedy>Hold on.

00:45:28.319 --> 00:45:30.240
<v Michael Kennedy>Nomenclature definition, please, here.

00:45:30.500 --> 00:45:32.160
<v Michael Kennedy>What are context vars for people who don't know?

00:45:32.260 --> 00:45:32.720
<v Michael Kennedy>Give us examples.

00:45:32.840 --> 00:45:33.140
<v Michael Kennedy>Oh, yeah.

00:45:33.540 --> 00:45:37.940
<v Giovanni Barallari>So context vars, so the interpreter has these,

00:45:39.060 --> 00:45:43.280
<v Giovanni Barallari>every thread into the Python interpreter

00:45:43.580 --> 00:45:45.780
<v Giovanni Barallari>has what it's called a context.

00:45:47.140 --> 00:45:51.160
<v Giovanni Barallari>And so with a sync.io, so what was the problem?

00:45:51.500 --> 00:46:01.280
<v Giovanni Barallari>So we used to have thread locals, which meant like if I have two threads and I have, for example, a request,

00:46:01.660 --> 00:46:10.900
<v Giovanni Barallari>and I have to keep the request state global, like global between quotes in my code,

00:46:11.100 --> 00:46:16.900
<v Giovanni Barallari>but I want to use the correct request in the code

00:46:17.810 --> 00:46:21.480
<v Giovanni Barallari>and don't make them mix in the two different threads, states,

00:46:22.460 --> 00:46:26.800
<v Giovanni Barallari>we use thread vocals because that's basically like a dictionary,

00:46:27.210 --> 00:46:30.260
<v Giovanni Barallari>more or less, where you can store that data.

00:46:30.640 --> 00:46:34.200
<v Giovanni Barallari>And that snapshot of data is for a single thread.

00:46:35.060 --> 00:46:39.200
<v Giovanni Barallari>Context var are a similar concept, but for a same code,

00:46:39.340 --> 00:46:42.480
<v Giovanni Barallari>which means you can store global,

00:46:43.440 --> 00:46:44.560
<v Giovanni Barallari>again, between quotes,

00:46:44.900 --> 00:46:50.280
<v Giovanni Barallari>global state from an asynchronous code,

00:46:50.880 --> 00:46:52.880
<v Giovanni Barallari>suspend, and when you get back,

00:46:53.360 --> 00:46:55.780
<v Giovanni Barallari>you have the correct object back

00:46:56.620 --> 00:46:58.600
<v Giovanni Barallari>instead of mixing global state

00:46:58.880 --> 00:46:59.840
<v Giovanni Barallari>between different coroutines.

00:47:00.040 --> 00:47:01.420
<v Giovanni Barallari>Was my explanation good?

00:47:01.760 --> 00:47:02.280
<v Michael Kennedy>Yeah, that's good.

00:47:02.900 --> 00:47:04.200
<v Michael Kennedy>People are probably pretty common

00:47:04.380 --> 00:47:05.680
<v Michael Kennedy>with flask.request.

00:47:05.860 --> 00:47:06.080
<v Michael Kennedy>Yeah.

00:47:06.120 --> 00:47:07.940
<v Michael Kennedy>Because you've got a view method

00:47:07.960 --> 00:47:10.500
<v Michael Kennedy>or maybe deep down inside some lower part of your program,

00:47:10.720 --> 00:47:12.300
<v Michael Kennedy>you're like, well, I need to know what the URL was.

00:47:12.300 --> 00:47:14.260
<v Michael Kennedy>You just say Flask.request, you don't pass it around.

00:47:14.460 --> 00:47:17.080
<v Michael Kennedy>Like very handy, probably architecturally a bad choice.

00:47:17.960 --> 00:47:20.080
<v Michael Kennedy>You know, it's hard to like test it out, right?

00:47:20.200 --> 00:47:20.700
<v Michael Kennedy>And you mock it.

00:47:20.720 --> 00:47:21.140
<v Michael Kennedy>I don't know.

00:47:21.640 --> 00:47:22.660
<v Giovanni Barallari>Emmet does the same.

00:47:23.860 --> 00:47:25.900
<v Giovanni Barallari>It's the one thing I copied from Flask

00:47:26.200 --> 00:47:27.100
<v Giovanni Barallari>because I really like it.

00:47:27.960 --> 00:47:30.880
<v Giovanni Barallari>But I think we can do the same argument about database, right?

00:47:31.060 --> 00:47:32.980
<v Giovanni Barallari>Like with a synchronous database,

00:47:33.140 --> 00:47:35.340
<v Giovanni Barallari>like you have to start a context,

00:47:35.740 --> 00:47:40.860
<v Giovanni Barallari>Like, async with database, whatever, and then you need to pass that, like, all through.

00:47:41.340 --> 00:47:43.440
<v Giovanni Barallari>Like, a context bar is more handy.

00:47:43.840 --> 00:47:44.260
<v Giovanni Barallari>I don't know.

00:47:44.960 --> 00:47:45.340
<v Michael Kennedy>Yeah, yeah.

00:47:45.380 --> 00:47:47.880
<v Michael Kennedy>It certainly unlocks some really interesting things.

00:47:48.020 --> 00:47:50.080
<v Michael Kennedy>Some extensions do cool things with it.

00:47:50.340 --> 00:47:51.300
<v Michael Kennedy>So that's what you're talking about.

00:47:51.440 --> 00:47:54.960
<v Michael Kennedy>But because it's not all just tied to the thread and using asyncio context variables,

00:47:55.220 --> 00:47:57.260
<v Michael Kennedy>now it's kind of shared in potential ways.

00:47:57.540 --> 00:47:59.840
<v Michael Kennedy>It's a little bit, you got to opt into it.

00:47:59.940 --> 00:48:00.200
<v Michael Kennedy>That's right?

00:48:00.440 --> 00:48:01.120
<v Giovanni Barallari>Yeah, CSR.

00:48:01.340 --> 00:48:03.560
<v Giovanni Barallari>Again, like, it's a Boolean when you want to start the runtime.

00:48:05.100 --> 00:48:08.740
<v Giovanni Barallari>And I mean, it's an implementation detail.

00:48:09.860 --> 00:48:11.380
<v Giovanni Barallari>So it is just to be like,

00:48:11.700 --> 00:48:13.780
<v Giovanni Barallari>I think the big warning at the beginning

00:48:14.400 --> 00:48:15.040
<v Giovanni Barallari>should have been like,

00:48:15.300 --> 00:48:16.580
<v Giovanni Barallari>Tonyo is very alpha right now.

00:48:18.000 --> 00:48:20.520
<v Giovanni Barallari>So maybe that decision will change in the future

00:48:20.680 --> 00:48:22.820
<v Giovanni Barallari>because I think right now the default is false.

00:48:23.540 --> 00:48:24.940
<v Giovanni Barallari>That might become true

00:48:25.410 --> 00:48:27.580
<v Giovanni Barallari>as soon as I stabilize the API.

00:48:28.420 --> 00:48:29.480
<v Giovanni Barallari>But yeah, the idea was

00:48:29.880 --> 00:48:32.080
<v Giovanni Barallari>if you're testing Tonyo right now,

00:48:32.560 --> 00:48:34.540
<v Giovanni Barallari>if you're using Tonyo to do some tests right now,

00:48:34.880 --> 00:48:41.560
<v Giovanni Barallari>I want the developer to explicitly state, okay, I'm going to use context bars.

00:48:41.960 --> 00:48:48.280
<v Giovanni Barallari>Because again, the fact you have multiple threads and stuff happening in parallel,

00:48:49.200 --> 00:48:55.300
<v Giovanni Barallari>I just want for the developer to be sure if it's thinking model is correct

00:48:55.460 --> 00:48:57.820
<v Giovanni Barallari>before trying to do some stuff.

00:48:59.160 --> 00:49:01.060
<v Giovanni Barallari>Because again, Tony is multi-threaded.

00:49:01.160 --> 00:49:07.900
<v Giovanni Barallari>multi-threaders is usually like not it's it's i don't know i think for humans multi-threading is

00:49:08.200 --> 00:49:16.520
<v Giovanni Barallari>hard to get sometimes um so that's the trade-off right like uh yes we now have all of these

00:49:16.700 --> 00:49:22.480
<v Giovanni Barallari>capabilities on the other end we need to think about the fact that the sync io always had this

00:49:22.940 --> 00:49:30.420
<v Giovanni Barallari>uh hidden uh let's say a feature which is hey there's only one coroutine running at the time

00:49:30.620 --> 00:49:32.640
<v Giovanni Barallari>Whereas Antonio can have like whatever number.

00:49:32.940 --> 00:49:33.100
<v Michael Kennedy>Yep.

00:49:34.160 --> 00:49:37.160
<v Michael Kennedy>Now you still had to manage stuff across await calls.

00:49:37.760 --> 00:49:43.520
<v Michael Kennedy>So that was, I feel like people felt like the GIL saved them from all thread considerations,

00:49:43.960 --> 00:49:46.200
<v Michael Kennedy>like locks and semaphores and so on.

00:49:46.460 --> 00:49:47.360
<v Michael Kennedy>And I don't think it did.

00:49:47.640 --> 00:49:50.780
<v Michael Kennedy>There's not a guarantee that like a thread couldn't interrupt you.

00:49:50.880 --> 00:49:52.780
<v Michael Kennedy>It's just less likely to interrupt you, right?

00:49:53.080 --> 00:49:59.600
<v Giovanni Barallari>So like the simple thing is that it did around other primitives.

00:49:59.940 --> 00:50:08.680
<v Giovanni Barallari>So for example, in a SyncIO, it's really hard to deadlock yourself because you're using like a threading lock.

00:50:09.000 --> 00:50:16.000
<v Giovanni Barallari>Because again, like the hidden feature of a SyncIO is you have only one thread working on that shit, right?

00:50:16.740 --> 00:50:17.320
<v Michael Kennedy>Yeah, exactly.

00:50:17.740 --> 00:50:19.080
<v Michael Kennedy>Like you can do all the locks you want.

00:50:19.160 --> 00:50:20.600
<v Michael Kennedy>It's the same thread in the re-entrance.

00:50:21.140 --> 00:50:21.380
<v Michael Kennedy>Exactly.

00:50:21.920 --> 00:50:24.500
<v Michael Kennedy>It's effectively a no-op other than it costs CPU.

00:50:24.920 --> 00:50:25.640
<v Giovanni Barallari>Yes, exactly.

00:50:25.860 --> 00:50:38.300
<v Giovanni Barallari>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.

00:50:39.000 --> 00:50:42.800
<v Giovanni Barallari>And if you, and so like you can deadlock Tonio.

00:50:43.100 --> 00:50:43.300
<v Giovanni Barallari>How?

00:50:43.580 --> 00:50:43.700
<v Giovanni Barallari>Yeah.

00:50:44.020 --> 00:50:44.940
<v Michael Kennedy>Welcome to multi-threading.

00:50:45.280 --> 00:50:45.480
<v Giovanni Barallari>Yes.

00:50:45.660 --> 00:50:52.160
<v Giovanni Barallari>Like you, you, you, you create, like you enter a threading lock and then you await a coroutine inside the threading lock.

00:50:52.310 --> 00:50:53.320
<v Giovanni Barallari>Now you're deadlocked.

00:50:53.580 --> 00:50:53.680
<v Giovanni Barallari>Yeah.

00:50:53.780 --> 00:50:55.860
<v Giovanni Barallari>Because the two things doesn't speak each other.

00:50:56.960 --> 00:50:59.700
<v Giovanni Barallari>But I guess my advice,

00:51:00.060 --> 00:51:03.360
<v Giovanni Barallari>if people want to start working in multi-threaded Python,

00:51:03.540 --> 00:51:08.620
<v Giovanni Barallari>my advice is just think about those two rules

00:51:09.140 --> 00:51:10.860
<v Giovanni Barallari>and then multi-threading is not that hard.

00:51:11.080 --> 00:51:12.380
<v Giovanni Barallari>Again, rule number one,

00:51:13.060 --> 00:51:16.800
<v Giovanni Barallari>never await inside a threading lock.

00:51:17.180 --> 00:51:17.960
<v Giovanni Barallari>That's a simple rule.

00:51:18.660 --> 00:51:21.740
<v Giovanni Barallari>Also because in Python, you use width lock.

00:51:22.240 --> 00:51:25.400
<v Giovanni Barallari>So that's super easy even to identify, right?

00:51:26.340 --> 00:51:27.620
<v Giovanni Barallari>That's rule number one.

00:51:28.120 --> 00:51:30.160
<v Giovanni Barallari>Rule number two, you have multiple threads.

00:51:30.510 --> 00:51:32.700
<v Giovanni Barallari>So if you want to write into anything,

00:51:33.760 --> 00:51:37.220
<v Giovanni Barallari>I mean, all the objects are thread safe in free thread of Python

00:51:37.420 --> 00:51:40.560
<v Giovanni Barallari>because a dictionary is thread safe, a list is thread safe, whatever.

00:51:41.080 --> 00:51:45.840
<v Giovanni Barallari>But that doesn't mean like you can screw up in using those.

00:51:46.020 --> 00:51:48.780
<v Giovanni Barallari>Because if you write state anywhere,

00:51:49.400 --> 00:51:58.560
<v Giovanni Barallari>You might have like race conditions and side effects because you expect to write something and then expect to read that value.

00:51:58.800 --> 00:52:01.380
<v Giovanni Barallari>But in the meantime, you have another thread right into the same location.

00:52:01.680 --> 00:52:08.180
<v Giovanni Barallari>So again, like my advice is rule number one, never use locks, threading locks, and then await.

00:52:08.580 --> 00:52:12.560
<v Giovanni Barallari>If you want to have an async lock, then use an async lock.

00:52:12.560 --> 00:52:13.880
<v Giovanni Barallari>So a Tonio lock for that.

00:52:14.720 --> 00:52:16.620
<v Giovanni Barallari>Rule number two, watch out.

00:52:16.820 --> 00:52:25.340
<v Giovanni Barallari>So you probably need locks when you want to be sure that, like, when you have a sequence of operation, right, on shared memory.

00:52:25.740 --> 00:52:26.880
<v Giovanni Barallari>Those are the two rules.

00:52:27.720 --> 00:52:31.740
<v Michael Kennedy>I think with those two rules, multithreading is not that hard.

00:52:31.960 --> 00:52:32.440
<v Michael Kennedy>Yeah, agreed.

00:52:32.660 --> 00:52:38.560
<v Michael Kennedy>So does Tonio come with its own dedicated lock and synchronization primitives?

00:52:38.900 --> 00:52:41.460
<v Giovanni Barallari>Yes, there's an entire sync module.

00:52:41.980 --> 00:52:45.600
<v Giovanni Barallari>So Tonio has different modules.

00:52:46.580 --> 00:52:51.920
<v Giovanni Barallari>Tonya time, which contains time primitives, like timeouts, timers, etc.

00:52:52.300 --> 00:52:56.400
<v Giovanni Barallari>It has async module, which contains all the synchronization primitives,

00:52:56.580 --> 00:52:59.520
<v Giovanni Barallari>so logs, semaphore, barriers, etc.

00:53:01.000 --> 00:53:06.380
<v Giovanni Barallari>It has the network module, which is one providing like sockets.

00:53:06.960 --> 00:53:10.580
<v Giovanni Barallari>So that's quite a big difference from a sync.io.

00:53:10.580 --> 00:53:16.080
<v Giovanni Barallari>So in a sync.io, we don't have like an async socket library.

00:53:16.440 --> 00:53:20.620
<v Giovanni Barallari>So if you think about the standard lib socket module,

00:53:21.359 --> 00:53:25.480
<v Giovanni Barallari>Tonio, AsyncIO needs you to use that

00:53:25.760 --> 00:53:28.280
<v Giovanni Barallari>and then pass the sockets to the event loop

00:53:28.560 --> 00:53:30.320
<v Giovanni Barallari>and then handle the socket through the event.

00:53:31.200 --> 00:53:34.360
<v Giovanni Barallari>Whereas in Tonio, there's a network module

00:53:34.540 --> 00:53:36.340
<v Giovanni Barallari>and inside that module, there's a socket module,

00:53:36.670 --> 00:53:40.380
<v Giovanni Barallari>which has the same exact interface of the standard lib plus socket.

00:53:40.960 --> 00:53:42.040
<v Giovanni Barallari>The only difference is, of course,

00:53:42.600 --> 00:53:45.500
<v Giovanni Barallari>every async method is async in Tonio.

00:53:47.280 --> 00:53:47.640
<v Giovanni Barallari>there's the

00:53:48.860 --> 00:53:50.140
<v Giovanni Barallari>streams module

00:53:50.240 --> 00:53:52.140
<v Giovanni Barallari>inside the network module which is the high level

00:53:53.120 --> 00:53:53.900
<v Giovanni Barallari>trio like

00:53:54.580 --> 00:53:55.740
<v Giovanni Barallari>interface to

00:53:56.160 --> 00:53:57.440
<v Giovanni Barallari>make it easy to manage

00:53:57.980 --> 00:54:00.020
<v Giovanni Barallari>network. It has a file

00:54:00.140 --> 00:54:02.020
<v Giovanni Barallari>system module which

00:54:02.280 --> 00:54:02.640
<v Giovanni Barallari>contains

00:54:03.980 --> 00:54:04.640
<v Giovanni Barallari>all the standard

00:54:06.440 --> 00:54:07.880
<v Giovanni Barallari>open kind of

00:54:08.220 --> 00:54:09.060
<v Michael Kennedy>methods that they

00:54:09.240 --> 00:54:10.300
<v Michael Kennedy>async with open

00:54:10.860 --> 00:54:12.940
<v Michael Kennedy>tonio.files.open something like that

00:54:13.100 --> 00:54:14.539
<v Giovanni Barallari>yeah.fs yes

00:54:15.260 --> 00:54:16.500
<v Giovanni Barallari>and it also contains like

00:54:17.400 --> 00:54:18.720
<v Giovanni Barallari>mirror of the

00:54:19.080 --> 00:54:20.260
<v Giovanni Barallari>pathlib, standard lib

00:54:20.680 --> 00:54:22.100
<v Giovanni Barallari>pathlib, so you can use like

00:54:22.400 --> 00:54:24.780
<v Giovanni Barallari>frontoniofs import path and that path

00:54:24.940 --> 00:54:26.060
<v Giovanni Barallari>has all the methods

00:54:26.700 --> 00:54:28.280
<v Giovanni Barallari>that are required to be asynchronous

00:54:29.840 --> 00:54:30.860
<v Giovanni Barallari>so that's

00:54:31.020 --> 00:54:32.120
<v Giovanni Barallari>more or less like the

00:54:33.000 --> 00:54:33.640
<v Giovanni Barallari>design

00:54:34.860 --> 00:54:36.260
<v Giovanni Barallari>I don't think like

00:54:36.420 --> 00:54:38.200
<v Giovanni Barallari>so right now I'm at

00:54:38.360 --> 00:54:40.680
<v Giovanni Barallari>tonio 0.9.14

00:54:40.960 --> 00:54:41.440
<v Giovanni Barallari>I guess

00:54:42.580 --> 00:54:44.520
<v Giovanni Barallari>and I don't think

00:54:44.540 --> 00:54:48.660
<v Giovanni Barallari>There's much left in terms of modules and primitives and features.

00:54:50.440 --> 00:54:52.280
<v Giovanni Barallari>The big feature missing is Windows support.

00:54:52.740 --> 00:54:53.440
<v Giovanni Barallari>More on that later.

00:54:55.839 --> 00:55:02.020
<v Giovanni Barallari>But yeah, I'd say I'm close to stabilize the API.

00:55:02.380 --> 00:55:04.440
<v Giovanni Barallari>So right now, again, this is all alpha.

00:55:05.040 --> 00:55:07.780
<v Giovanni Barallari>That's mostly because there's only me working on this.

00:55:08.920 --> 00:55:12.300
<v Giovanni Barallari>And I have Fable working on a bunch of other things,

00:55:12.920 --> 00:55:16.480
<v Giovanni Barallari>which I guess like if I can digress for a second,

00:55:17.040 --> 00:55:20.420
<v Giovanni Barallari>I think like I have a very peculiar and unique way of using AI

00:55:20.840 --> 00:55:22.020
<v Giovanni Barallari>because right now I'm like,

00:55:22.780 --> 00:55:26.080
<v Giovanni Barallari>and I guess like it's a good use case to advertise.

00:55:26.420 --> 00:55:28.180
<v Giovanni Barallari>So the entirety of Tonio,

00:55:28.340 --> 00:55:32.160
<v Giovanni Barallari>so Tonio has maybe 1% AI written code.

00:55:32.380 --> 00:55:35.900
<v Giovanni Barallari>The only code that was written by Claude

00:55:36.780 --> 00:55:38.140
<v Giovanni Barallari>was the pytest plugin.

00:55:38.540 --> 00:55:40.520
<v Giovanni Barallari>Oh, by the way, we also have a pytest plugin.

00:55:40.780 --> 00:55:42.760
<v Giovanni Barallari>so you can hide the smart tone.

00:55:45.200 --> 00:55:48.220
<v Giovanni Barallari>But that's the only part that I let AI touch.

00:55:49.640 --> 00:55:51.800
<v Giovanni Barallari>Everything else, like it's old school.

00:55:52.210 --> 00:55:56.780
<v Giovanni Barallari>I written all of it like the old way, text editor,

00:55:57.440 --> 00:56:01.280
<v Giovanni Barallari>not even any suggestion and everything.

00:56:02.420 --> 00:56:07.260
<v Giovanni Barallari>What I used AI for, and it helped me a lot

00:56:07.520 --> 00:56:12.660
<v Giovanni Barallari>into reaching a state in which I think Tonio is really stable now

00:56:13.520 --> 00:56:18.480
<v Giovanni Barallari>was to use AI to build projects that were using Tonio

00:56:18.860 --> 00:56:21.440
<v Giovanni Barallari>and stress test everything about Tonio.

00:56:21.510 --> 00:56:24.400
<v Giovanni Barallari>So for example, I made a port of Pi,

00:56:25.570 --> 00:56:27.520
<v Giovanni Barallari>the harness from Maria Zeckner.

00:56:28.510 --> 00:56:31.440
<v Giovanni Barallari>So I made Claude rewrite the whole thing in Python

00:56:32.200 --> 00:56:35.900
<v Giovanni Barallari>because Pi is written in TypeScript, regardless of the name.

00:56:37.320 --> 00:56:40.960
<v Giovanni Barallari>So I made that entire harness with Claude saying,

00:56:41.030 --> 00:56:42.020
<v Giovanni Barallari>okay, take five,

00:56:42.660 --> 00:56:46.380
<v Giovanni Barallari>meter every behavior and just use Tonio to do everything.

00:56:47.430 --> 00:56:49.200
<v Giovanni Barallari>And that like helped me like catch

00:56:49.510 --> 00:56:52.740
<v Giovanni Barallari>like probably 95% of the bugs in Tonio.

00:56:53.720 --> 00:56:55.920
<v Giovanni Barallari>And I kind of started doing the same for the ecosystem.

00:56:57.100 --> 00:56:59.320
<v Giovanni Barallari>Maybe we can talk about it a bit later,

00:56:59.740 --> 00:57:01.760
<v Giovanni Barallari>but yeah, that's also like a super,

00:57:02.480 --> 00:57:03.940
<v Giovanni Barallari>it's super useful for me, right?

00:57:04.080 --> 00:57:06.240
<v Giovanni Barallari>Like as a solo open source developer,

00:57:06.620 --> 00:57:08.660
<v Giovanni Barallari>which, yeah, I want to focus on Tonio.

00:57:09.080 --> 00:57:11.500
<v Giovanni Barallari>I don't have time to do all the other packages.

00:57:12.040 --> 00:57:13.320
<v Giovanni Barallari>And so, yeah, that was nice.

00:57:13.440 --> 00:57:14.760
<v Michael Kennedy>That's a very interesting way.

00:57:14.760 --> 00:57:16.080
<v Michael Kennedy>I hadn't really considered that.

00:57:16.300 --> 00:57:19.360
<v Michael Kennedy>It's just like, I need users before I have users.

00:57:19.840 --> 00:57:21.840
<v Michael Kennedy>I need these three use cases covered.

00:57:21.980 --> 00:57:24.240
<v Michael Kennedy>So give me a web app that uses an async database.

00:57:24.500 --> 00:57:27.180
<v Michael Kennedy>Give me this terminal app and so on.

00:57:27.180 --> 00:57:28.360
<v Michael Kennedy>And that's, yeah, that's really cool.

00:57:28.720 --> 00:57:28.920
<v Michael Kennedy>Yeah.

00:57:29.660 --> 00:57:29.800
<v Michael Kennedy>Okay.

00:57:30.180 --> 00:57:30.620
<v Michael Kennedy>I agree.

00:57:30.820 --> 00:57:31.700
<v Michael Kennedy>Ecosystem is interesting.

00:57:31.780 --> 00:57:32.660
<v Michael Kennedy>I want to talk about it.

00:57:32.840 --> 00:57:34.940
<v Michael Kennedy>But before that, I want to come back to blocking threads.

00:57:35.420 --> 00:57:35.680
<v Michael Kennedy>Yes.

00:57:35.960 --> 00:57:39.600
<v Michael Kennedy>So blogging threads are CPU bound generally type of things.

00:57:39.610 --> 00:57:40.060
<v Michael Kennedy>Is that right?

00:57:40.580 --> 00:57:44.680
<v Michael Kennedy>So I think Tonio is a bit peculiar in this regard

00:57:45.120 --> 00:57:47.680
<v Giovanni Barallari>because it really depends what your program does.

00:57:48.160 --> 00:57:54.020
<v Giovanni Barallari>Because if your program, whatever that is, has mixed load,

00:57:54.210 --> 00:57:58.180
<v Giovanni Barallari>which means like you have both IO bound stuff,

00:57:58.700 --> 00:58:03.960
<v Giovanni Barallari>like network, disk, terminal, whatever, and CPU bound stuff,

00:58:05.180 --> 00:58:10.460
<v Giovanni Barallari>then the only way to be sure that everything keeps running smoothly,

00:58:10.820 --> 00:58:13.300
<v Giovanni Barallari>like on the network part, the IO part,

00:58:13.620 --> 00:58:18.840
<v Giovanni Barallari>is to spawn the CPU bound stuff on the blocking thread pool.

00:58:19.460 --> 00:58:24.740
<v Giovanni Barallari>But if your program is just CPU bound, then who cares?

00:58:24.990 --> 00:58:28.580
<v Giovanni Barallari>Just use the normal thread pool

00:58:29.710 --> 00:58:31.700
<v Giovanni Barallari>because then you don't have anything to block.

00:58:32.000 --> 00:58:34.360
<v Giovanni Barallari>like your CPU bound, your CPU limited.

00:58:35.280 --> 00:58:37.340
<v Giovanni Barallari>So the standard configuration of Tonio

00:58:37.800 --> 00:58:40.760
<v Giovanni Barallari>gives you exactly the perfect number of CPU cores to use.

00:58:41.860 --> 00:58:45.520
<v Giovanni Barallari>On the other hand, if you just have like IO bound.

00:58:46.100 --> 00:58:48.920
<v Giovanni Barallari>So again, like if you have just CPU bound work

00:58:49.100 --> 00:58:50.440
<v Giovanni Barallari>or just IO bound work,

00:58:50.500 --> 00:58:52.540
<v Giovanni Barallari>you can use like the standard pool,

00:58:53.860 --> 00:58:55.200
<v Giovanni Barallari>nothing particular like this.

00:58:55.440 --> 00:58:57.140
<v Giovanni Barallari>The blocking thread pool is useful

00:58:57.440 --> 00:58:59.200
<v Giovanni Barallari>only when you have mixed workloads

00:58:59.820 --> 00:59:01.700
<v Giovanni Barallari>and you need to balance them, right?

00:59:01.960 --> 00:59:03.820
<v Michael Kennedy>Okay, how do I create such a thing?

00:59:04.160 --> 00:59:07.440
<v Michael Kennedy>How do I start a task in one or the other?

00:59:07.980 --> 00:59:11.060
<v Giovanni Barallari>So every await or yell destruction

00:59:11.700 --> 00:59:12.780
<v Giovanni Barallari>or a tonio spawn destruction

00:59:13.480 --> 00:59:15.600
<v Giovanni Barallari>runs on the standard thread pool.

00:59:16.660 --> 00:59:19.280
<v Giovanni Barallari>To spawn something on the blocking thread pool

00:59:19.440 --> 00:59:20.860
<v Giovanni Barallari>is just tonio spawn blocking.

00:59:21.190 --> 00:59:21.300
<v Giovanni Barallari>Done.

00:59:21.670 --> 00:59:23.640
<v Giovanni Barallari>You don't need to manage the size of the pool.

00:59:23.690 --> 00:59:26.140
<v Giovanni Barallari>You don't need to create the pool.

00:59:27.480 --> 00:59:32.700
<v Giovanni Barallari>If you want to control concurrency in the blocking threat pool,

00:59:34.240 --> 00:59:37.200
<v Giovanni Barallari>you either use, like, barriers when you spawn stuff

00:59:38.320 --> 00:59:40.260
<v Giovanni Barallari>or semaphores when you spawn stuff.

00:59:41.700 --> 00:59:42.820
<v Giovanni Barallari>But, yeah, like, in general,

00:59:43.300 --> 00:59:45.740
<v Giovanni Barallari>it's not different from spawning anything else.

00:59:46.160 --> 00:59:46.340
<v Giovanni Barallari>Sure.

00:59:46.620 --> 00:59:47.640
<v Michael Kennedy>So you can spawn blocking.

00:59:48.540 --> 00:59:49.640
<v Michael Kennedy>Here's what I was thinking.

00:59:49.800 --> 00:59:50.620
<v Michael Kennedy>Here's what I was getting at.

00:59:51.440 --> 00:59:53.540
<v Michael Kennedy>That I think would be a sweet feature.

00:59:53.980 --> 00:59:57.100
<v Michael Kennedy>You've got at tonio.main for the entry point, right?

00:59:57.560 --> 00:59:57.700
<v Michael Kennedy>Yes.

00:59:58.260 --> 01:00:01.640
<v Michael Kennedy>So that runs on tonio.run instead of just Python run or whatever.

01:00:02.000 --> 01:00:05.200
<v Michael Kennedy>For functions that you know are computational,

01:00:05.440 --> 01:00:08.140
<v Michael Kennedy>you would like to ensure that they run on a blocking thread

01:00:08.640 --> 01:00:09.820
<v Michael Kennedy>or along those lines.

01:00:10.120 --> 01:00:15.540
<v Michael Kennedy>You could put a decorator that just says tonio.cpu or whatever.

01:00:15.640 --> 01:00:16.000
<v Michael Kennedy>I don't know.

01:00:16.120 --> 01:00:19.000
<v Michael Kennedy>You come up with a name, but that way I don't even have to think.

01:00:19.400 --> 01:00:21.840
<v Michael Kennedy>If I import another library also built on tonio,

01:00:22.040 --> 01:00:25.020
<v Michael Kennedy>I don't know, oh, that function wants me to run it this way.

01:00:25.490 --> 01:00:26.480
<v Michael Kennedy>No, I just call it.

01:00:26.640 --> 01:00:29.600
<v Michael Kennedy>Antonio goes, ah, this is an async CPU one.

01:00:29.910 --> 01:00:33.680
<v Michael Kennedy>So we'll scale it up, scaffold it up on that, basically.

01:00:33.960 --> 01:00:36.200
<v Giovanni Barallari>That's a nice suggestion, okay?

01:00:36.940 --> 01:00:37.200
<v Giovanni Barallari>All right.

01:00:37.320 --> 01:00:40.200
<v Giovanni Barallari>I think it will land in Antonio 0.10, yes.

01:00:40.580 --> 01:00:41.300
<v Giovanni Barallari>Awesome, okay, cool.

01:00:41.680 --> 01:00:42.380
<v Michael Kennedy>I like that.

01:00:43.260 --> 01:00:46.720
<v Michael Kennedy>When you're in the function, the locality of the information

01:00:46.790 --> 01:00:49.080
<v Michael Kennedy>that, oh, this is computational and so on is right there.

01:00:49.170 --> 01:00:50.920
<v Michael Kennedy>But when you're calling, and especially in a big app,

01:00:51.090 --> 01:00:51.700
<v Michael Kennedy>you don't know, right?

01:00:51.780 --> 01:00:52.680
<v Michael Kennedy>So I think that would be really cool.

01:00:53.060 --> 01:00:53.880
<v Michael Kennedy>So I'm glad you like it.

01:00:54.060 --> 01:00:55.080
<v Michael Kennedy>No, we'll work on that.

01:00:55.640 --> 01:00:55.940
<v Michael Kennedy>Awesome.

01:00:56.940 --> 01:00:58.620
<v Michael Kennedy>All right, we got time for two really quick things.

01:00:59.040 --> 01:01:02.100
<v Michael Kennedy>So one thing that is nice about standard asyncio

01:01:02.280 --> 01:01:03.640
<v Michael Kennedy>is everyone uses asyncio

01:01:03.840 --> 01:01:05.460
<v Michael Kennedy>if they're writing async code at the moment,

01:01:05.930 --> 01:01:06.840
<v Michael Kennedy>unless they do something like Trio

01:01:06.970 --> 01:01:07.680
<v Michael Kennedy>or one of these other things.

01:01:08.060 --> 01:01:11.760
<v Michael Kennedy>So for example, if I use HTTPX2 or HTTPX

01:01:12.300 --> 01:01:14.520
<v Michael Kennedy>and I, with async, you know,

01:01:14.840 --> 01:01:16.440
<v Michael Kennedy>async with create a client of it

01:01:16.500 --> 01:01:17.520
<v Michael Kennedy>and I await its get,

01:01:18.260 --> 01:01:20.180
<v Michael Kennedy>that runs on the same sort of deal.

01:01:20.440 --> 01:01:23.040
<v Michael Kennedy>but it's probably not compatible with Tonio, is it?

01:01:23.270 --> 01:01:24.340
<v Michael Kennedy>And if it's not, what do I do?

01:01:24.940 --> 01:01:25.760
<v Giovanni Barallari>Yeah, it's not.

01:01:28.580 --> 01:01:32.080
<v Giovanni Barallari>So I have one thing that is the current state,

01:01:32.380 --> 01:01:35.580
<v Giovanni Barallari>and I also have the idea for the future.

01:01:35.850 --> 01:01:40.060
<v Giovanni Barallari>So right now we have a package which is called TonioMonkey,

01:01:40.460 --> 01:01:43.140
<v Giovanni Barallari>and you probably can guess what it means, what it does.

01:01:43.820 --> 01:01:48.180
<v Giovanni Barallari>It basically like monkey patches popular,

01:01:48.860 --> 01:01:55.520
<v Giovanni Barallari>Or I would either say, like, asyncio native libraries I thought about in the last few months.

01:01:56.580 --> 01:01:57.480
<v Giovanni Barallari>That might be useful.

01:01:59.320 --> 01:02:04.980
<v Giovanni Barallari>And so I guess today we have, like, TonyaMonkey can monkey patch async.

01:02:05.320 --> 01:02:05.860
<v Giovanni Barallari>No, sorry.

01:02:06.640 --> 01:02:07.200
<v Giovanni Barallari>PsychoPG.

01:02:08.380 --> 01:02:11.200
<v Giovanni Barallari>It can patch HTTPX and HTTPX2.

01:02:12.200 --> 01:02:13.760
<v Giovanni Barallari>It can patch Redis.

01:02:15.220 --> 01:02:17.240
<v Giovanni Barallari>And I think that's most of it for the moment.

01:02:17.760 --> 01:02:21.040
<v Giovanni Barallari>But I'm open for issues.

01:02:21.360 --> 01:02:28.440
<v Giovanni Barallari>If anyone from the listeners want to try Tonio to an existing code base

01:02:28.680 --> 01:02:32.640
<v Giovanni Barallari>and you have a package you would like to see monkey patched,

01:02:33.180 --> 01:02:35.340
<v Giovanni Barallari>just open an issue in the TonioMonkey repo

01:02:35.900 --> 01:02:39.900
<v Giovanni Barallari>or even in Tonio discussions, and I will look at it.

01:02:41.560 --> 01:02:46.860
<v Giovanni Barallari>Because again, right now I need the vast majority of possible use cases

01:02:46.880 --> 01:02:48.480
<v Giovanni Barallari>to see that I covered everything.

01:02:50.140 --> 01:02:52.460
<v Giovanni Barallari>Ideally, as soon as I stabilize the API

01:02:52.680 --> 01:02:55.040
<v Giovanni Barallari>and I say, okay, this is the thing,

01:02:56.200 --> 01:03:03.380
<v Giovanni Barallari>my idea was to try to contact the NEIO maintainers

01:03:04.900 --> 01:03:13.260
<v Giovanni Barallari>to see if we can have NEIO backend in NEIO,

01:03:13.680 --> 01:03:19.820
<v Giovanni Barallari>which should be like a way easier way to deal with this.

01:03:20.900 --> 01:03:22.720
<v Giovanni Barallari>I'm not sure how much it's feasible because again,

01:03:22.900 --> 01:03:27.540
<v Giovanni Barallari>like the main problem for Tonio right now is that it doesn't support any of the

01:03:27.740 --> 01:03:28.600
<v Giovanni Barallari>synchio primitives.

01:03:28.880 --> 01:03:33.260
<v Giovanni Barallari>So if any code base like relies a ton on task or futures or whatever,

01:03:34.660 --> 01:03:36.520
<v Giovanni Barallari>that's not in the runtime.

01:03:36.940 --> 01:03:37.060
<v Giovanni Barallari>Yeah.

01:03:37.300 --> 01:03:37.420
<v Michael Kennedy>Yeah.

01:03:37.760 --> 01:03:37.880
<v Michael Kennedy>Okay.

01:03:38.400 --> 01:03:38.640
<v Michael Kennedy>Interesting.

01:03:38.980 --> 01:03:39.140
<v Michael Kennedy>Interesting.

01:03:39.340 --> 01:03:40.560
<v Michael Kennedy>But there are some choices.

01:03:40.820 --> 01:03:42.580
<v Michael Kennedy>And are you also creating your own?

01:03:42.740 --> 01:03:48.320
<v Michael Kennedy>I saw some custom HTTP or some other library or two.

01:03:48.980 --> 01:03:53.640
<v Giovanni Barallari>I'm also trying to be a very contained ecosystem of libraries,

01:03:54.120 --> 01:03:55.460
<v Giovanni Barallari>mainly on the web part.

01:03:55.660 --> 01:04:01.580
<v Giovanni Barallari>So right now, I published a low-level HTTP library for Python

01:04:02.430 --> 01:04:06.440
<v Giovanni Barallari>that is compatible both with Asyncio and Tonio.

01:04:07.040 --> 01:04:08.680
<v Giovanni Barallari>It's called HTTPunk.

01:04:09.100 --> 01:04:17.740
<v Giovanni Barallari>I published an eye-level client, HTTP client, on top of that, which is called Punk Rec.

01:04:19.360 --> 01:04:27.020
<v Giovanni Barallari>So again, you can use that regardless of Tonio Asincaio, because the adaptation layer is inside the package.

01:04:27.580 --> 01:04:38.340
<v Giovanni Barallari>I kind of published a very beta experiment of a Uvicorn port to work in Tonio,

01:04:38.680 --> 01:04:45.120
<v Giovanni Barallari>Which I guess brings up a question like, hey, when will Granian support this?

01:04:45.430 --> 01:04:45.960
<v Giovanni Barallari>Am I right?

01:04:46.220 --> 01:04:46.560
<v Giovanni Barallari>Yeah, right.

01:04:47.640 --> 01:04:48.720
<v Giovanni Barallari>So I have plans.

01:04:50.080 --> 01:04:52.700
<v Giovanni Barallari>It's actually way bigger plans for Granian.

01:04:53.820 --> 01:04:58.700
<v Giovanni Barallari>So as I said before, 2026 was a bit boring in Granian.

01:04:58.900 --> 01:05:02.340
<v Giovanni Barallari>Like, no, a bunch of new features, not a bunch of that stuff.

01:05:02.390 --> 01:05:08.140
<v Giovanni Barallari>But that's just because several months ago, I started designing two things for Granian.

01:05:08.280 --> 01:05:13.140
<v Giovanni Barallari>So the first is this revision two of the ArchGee protocol,

01:05:13.680 --> 01:05:17.020
<v Giovanni Barallari>which will be async independent.

01:05:17.600 --> 01:05:20.580
<v Giovanni Barallari>So it will become like a callback protocol

01:05:21.780 --> 01:05:25.720
<v Giovanni Barallari>because the other big feature I want to add in Granian

01:05:26.180 --> 01:05:29.320
<v Giovanni Barallari>is to support a bunch of runtimes.

01:05:29.660 --> 01:05:31.620
<v Giovanni Barallari>So my idea is not the Granian tree,

01:05:32.240 --> 01:05:36.040
<v Giovanni Barallari>when it will land, no promises on the time here,

01:05:36.380 --> 01:05:37.640
<v Giovanni Barallari>but like when it will land,

01:05:37.980 --> 01:05:45.700
<v Giovanni Barallari>Ideally, it will support Asyncio, Trio, Tonio, gEvent, eventlet, and a bunch of stuff.

01:05:45.890 --> 01:05:55.360
<v Giovanni Barallari>So that's the idea, is to isolate, let's say, the protocol implementation and the Python runtime implementation.

01:05:55.620 --> 01:05:57.540
<v Giovanni Barallari>So it will happen one day.

01:05:59.660 --> 01:06:04.620
<v Giovanni Barallari>In the meantime, I wanted to have a test server to use with Tonio and see what happens.

01:06:04.890 --> 01:06:05.640
<v Giovanni Barallari>So that's why.

01:06:07.220 --> 01:06:08.420
<v Michael Kennedy>Yeah, using something like

01:06:08.780 --> 01:06:10.860
<v Michael Kennedy>Cord or FastAPI or something like that.

01:06:11.120 --> 01:06:12.900
<v Michael Kennedy>If you were able to run it in Granian

01:06:13.400 --> 01:06:15.020
<v Michael Kennedy>with the Tonio, Tonio

01:06:15.520 --> 01:06:17.220
<v Michael Kennedy>backend, the async

01:06:17.560 --> 01:06:18.700
<v Michael Kennedy>endpoints you write would be

01:06:19.120 --> 01:06:19.960
<v Michael Kennedy>running on Tonio, yeah?

01:06:20.240 --> 01:06:21.360
<v Michael Kennedy>Yeah, that's the plan.

01:06:22.800 --> 01:06:25.340
<v Giovanni Barallari>In TonioMonkey, there are also patches for FastAPI,

01:06:25.480 --> 01:06:26.820
<v Giovanni Barallari>by the way. And I'm working

01:06:27.060 --> 01:06:28.480
<v Giovanni Barallari>on a one on Django.

01:06:28.920 --> 01:06:30.760
<v Giovanni Barallari>I need to ping

01:06:30.900 --> 01:06:31.900
<v Giovanni Barallari>Carleton on that

01:06:32.820 --> 01:06:34.380
<v Giovanni Barallari>because we met this year

01:06:34.420 --> 01:06:36.400
<v Giovanni Barallari>at this year, PyCon Italy,

01:06:36.940 --> 01:06:38.040
<v Giovanni Barallari>and we talk about it.

01:06:39.259 --> 01:06:42.240
<v Giovanni Barallari>So maybe in a month or so,

01:06:42.660 --> 01:06:44.440
<v Michael Kennedy>we will also have like a patch for Django.

01:06:44.720 --> 01:06:44.880
<v Michael Kennedy>Sweet.

01:06:45.190 --> 01:06:51.360
<v Michael Kennedy>Yeah, I just had Carlton on to talk about

01:06:52.260 --> 01:06:55.700
<v Michael Kennedy>basically all the async work in Django 6 and 6.0 and so on.

01:06:55.730 --> 01:06:56.360
<v Giovanni Barallari>So that was fun.

01:06:56.400 --> 01:06:58.600
<v Giovanni Barallari>I watched the episode and I was like,

01:06:58.900 --> 01:07:01.420
<v Giovanni Barallari>huh, sounds familiar, huh, huh.

01:07:01.960 --> 01:07:02.980
<v Michael Kennedy>Yeah, we've talked about that.

01:07:03.030 --> 01:07:03.380
<v Michael Kennedy>I see.

01:07:03.820 --> 01:07:04.060
<v Michael Kennedy>Interesting.

01:07:04.480 --> 01:07:04.660
<v Michael Kennedy>All right.

01:07:04.720 --> 01:07:07.520
<v Michael Kennedy>Well, I got one final main topic for us.

01:07:07.860 --> 01:07:08.020
<v Michael Kennedy>Yep.

01:07:08.280 --> 01:07:08.680
<v Michael Kennedy>Oh, yeah.

01:07:08.720 --> 01:07:10.200
<v Michael Kennedy>What about windows?

01:07:12.260 --> 01:07:12.520
<v Michael Kennedy>So.

01:07:12.800 --> 01:07:15.180
<v Michael Kennedy>And I'm not putting pressure on you to do it.

01:07:15.260 --> 01:07:15.700
<v Michael Kennedy>No, no, no.

01:07:16.060 --> 01:07:17.560
<v Michael Kennedy>I'm just rounding out the conversation.

01:07:18.040 --> 01:07:18.300
<v Michael Kennedy>Okay.

01:07:18.660 --> 01:07:26.480
<v Giovanni Barallari>So if you ask me like a few months ago, my answer would probably be like that short video

01:07:26.920 --> 01:07:31.740
<v Giovanni Barallari>extract from a series like where you had the actor.

01:07:32.780 --> 01:07:32.940
<v Giovanni Barallari>Whatever.

01:07:33.080 --> 01:07:41.700
<v Giovanni Barallari>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.800 --> 01:07:48.640
<v Giovanni Barallari>uh so i i'm not so first of all like the usual problem so supporting windows in granian

01:07:49.180 --> 01:07:55.220
<v Giovanni Barallari>it's a pain man like a real pain like i'm not joking right because it has like all these

01:07:55.560 --> 01:08:02.500
<v Giovanni Barallari>quirk behaviors not just in python not just in terms of threads like do you know that win that

01:08:02.520 --> 01:08:11.220
<v Giovanni Barallari>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.430 --> 01:08:17.859
<v Giovanni Barallari>socket it becomes blocking and so you cannot use it like anywhere um so windows for start like

01:08:18.029 --> 01:08:26.299
<v Giovanni Barallari>windows is a pain and is a very weird operating system second i don't have like a windows environment

01:08:27.440 --> 01:08:29.120
<v Giovanni Barallari>because finally in 2026,

01:08:29.380 --> 01:08:32.200
<v Giovanni Barallari>I was able to finally have just macOS and Linux

01:08:32.580 --> 01:08:34.980
<v Giovanni Barallari>on all my machines, also for gaming.

01:08:35.319 --> 01:08:36.580
<v Giovanni Barallari>So I'm so happy.

01:08:37.960 --> 01:08:39.420
<v Giovanni Barallari>So those are, like I say,

01:08:39.759 --> 01:08:43.240
<v Giovanni Barallari>the two main preambles on Windows support.

01:08:44.499 --> 01:08:48.359
<v Giovanni Barallari>But also because implementing the library

01:08:48.400 --> 01:08:52.120
<v Giovanni Barallari>I use Antonio to manage the Polar,

01:08:52.339 --> 01:08:53.819
<v Giovanni Barallari>the actual event loop,

01:08:54.859 --> 01:08:58.220
<v Giovanni Barallari>It supports Windows, but you know, weird way.

01:08:58.580 --> 01:09:03.520
<v Giovanni Barallari>So, months ago I was like almost a hundred percent convinced now,

01:09:03.839 --> 01:09:08.799
<v Giovanni Barallari>never windows, never now, thanks to fable by chatting with fable.

01:09:09.240 --> 01:09:18.380
<v Giovanni Barallari>I think we found a way to hack into, into meal, this library to, to fake

01:09:18.779 --> 01:09:20.140
<v Giovanni Barallari>some stuff about windows.

01:09:20.220 --> 01:09:24.359
<v Giovanni Barallari>So it will interpret some stuff as TCP sockets.

01:09:24.460 --> 01:09:27.540
<v Giovanni Barallari>even if they are like file descriptors or anything.

01:09:28.140 --> 01:09:30.980
<v Giovanni Barallari>So I won't make a promise here

01:09:31.380 --> 01:09:33.900
<v Giovanni Barallari>because then I have to do it.

01:09:34.240 --> 01:09:37.759
<v Giovanni Barallari>But ideally, end of the year,

01:09:38.120 --> 01:09:41.020
<v Giovanni Barallari>we might see partial Windows support in Tom.

01:09:41.190 --> 01:09:41.319
<v Giovanni Barallari>Sweet.

01:09:41.640 --> 01:09:43.420
<v Giovanni Barallari>So that's the idea right now.

01:09:44.120 --> 01:09:47.640
<v Giovanni Barallari>I still hate Microsoft, to be clear here.

01:09:47.640 --> 01:09:49.740
<v Giovanni Barallari>Like, man, I just like Windows.

01:09:51.720 --> 01:09:54.340
<v Giovanni Barallari>So if any of the listeners,

01:09:54.450 --> 01:09:56.860
<v Giovanni Barallari>like if you're programming in Windows

01:09:57.560 --> 01:09:58.740
<v Giovanni Barallari>and not using Weasel,

01:09:59.160 --> 01:10:00.720
<v Giovanni Barallari>please put down in the comments why.

01:10:01.220 --> 01:10:04.960
<v Giovanni Barallari>Like, why are you self-inflicting this to you?

01:10:05.140 --> 01:10:07.440
<v Giovanni Barallari>Like, explain me the rationale

01:10:08.100 --> 01:10:11.180
<v Giovanni Barallari>into dealing with all the BS

01:10:11.480 --> 01:10:13.200
<v Giovanni Barallari>that Windows 11 is nowadays.

01:10:14.620 --> 01:10:15.580
<v Giovanni Barallari>So yeah, but anyways,

01:10:17.080 --> 01:10:18.440
<v Giovanni Barallari>I will try end of the year

01:10:18.440 --> 01:10:21.120
<v Giovanni Barallari>to have some form of Windows support in time.

01:10:21.700 --> 01:10:23.480
<v Michael Kennedy>I can tell, I can sense your excitement.

01:10:23.880 --> 01:10:25.800
<v Michael Kennedy>My Windows computer over there is Windows 10, by the way.

01:10:26.220 --> 01:10:27.520
<v Michael Kennedy>But I do think for people listening,

01:10:28.240 --> 01:10:30.600
<v Michael Kennedy>Windows Subsystem for Linux is an escape hatch

01:10:30.950 --> 01:10:31.860
<v Michael Kennedy>that you can do, right?

01:10:32.020 --> 01:10:33.920
<v Michael Kennedy>If you had a project, you really want to use this here,

01:10:34.100 --> 01:10:35.840
<v Michael Kennedy>like set up Windows Subsystem for Linux

01:10:35.950 --> 01:10:36.620
<v Michael Kennedy>and run it there, right?

01:10:36.840 --> 01:10:40.320
<v Giovanni Barallari>To be here, like the only reason I want to,

01:10:40.500 --> 01:10:42.680
<v Giovanni Barallari>like I want to try to add Windows support in Tonio

01:10:42.820 --> 01:10:43.920
<v Giovanni Barallari>is not for developers,

01:10:44.420 --> 01:10:47.880
<v Giovanni Barallari>is actually to provide developers a way to say,

01:10:48.050 --> 01:10:50.300
<v Giovanni Barallari>okay, I want to build a program in Python

01:10:50.300 --> 01:10:53.180
<v Giovanni Barallari>with Tonio that has to work like everywhere.

01:10:53.700 --> 01:10:54.240
<v Giovanni Barallari>Because right now, again,

01:10:54.440 --> 01:10:56.280
<v Giovanni Barallari>like if you code anything in Tonio,

01:10:56.700 --> 01:10:58.480
<v Giovanni Barallari>it works only on POSIX systems,

01:10:58.640 --> 01:10:59.860
<v Giovanni Barallari>so only Linux and macOS.

01:11:00.280 --> 01:11:02.100
<v Giovanni Barallari>So that's the reason why I want to have

01:11:02.300 --> 01:11:04.360
<v Giovanni Barallari>at least some form of Windows support

01:11:04.480 --> 01:11:06.620
<v Giovanni Barallari>because, I mean, it's not about developers.

01:11:07.340 --> 01:11:09.080
<v Giovanni Barallari>It's about like where the software runs

01:11:09.400 --> 01:11:09.940
<v Giovanni Barallari>at the end of the day.

01:11:10.100 --> 01:11:10.520
<v Giovanni Barallari>Right, right.

01:11:10.620 --> 01:11:12.180
<v Michael Kennedy>It's the deployment targets, yeah.

01:11:12.560 --> 01:11:12.640
<v Giovanni Barallari>Yeah.

01:11:13.040 --> 01:11:14.360
<v Michael Kennedy>All right, Joe, final call to action.

01:11:14.620 --> 01:11:15.460
<v Michael Kennedy>People have been listening

01:11:15.560 --> 01:11:17.000
<v Michael Kennedy>and they're like, this sounds pretty excellent.

01:11:17.600 --> 01:11:18.380
<v Michael Kennedy>I want to try it.

01:11:18.740 --> 01:11:19.300
<v Michael Kennedy>What do you tell them?

01:11:19.540 --> 01:11:27.760
<v Giovanni Barallari>I mean, like, again, I think we're living in the perfect moment to try stuff.

01:11:28.780 --> 01:11:33.900
<v Giovanni Barallari>We've given with this big opportunity, big new opportunity of free-threaded Python.

01:11:35.140 --> 01:11:36.960
<v Giovanni Barallari>We have AI agents.

01:11:38.500 --> 01:11:44.040
<v Giovanni Barallari>And by the way, I was surprised, but like pointing any agent, any model,

01:11:44.780 --> 01:11:48.620
<v Giovanni Barallari>frontier model to a project and say, okay, implement this in Tonio.

01:11:48.840 --> 01:11:49.480
<v Giovanni Barallari>I was surprised.

01:11:49.620 --> 01:11:52.480
<v Giovanni Barallari>They got all the API correctly most of the times,

01:11:53.660 --> 01:11:54.220
<v Giovanni Barallari>which is amazing.

01:11:54.480 --> 01:11:56.580
<v Giovanni Barallari>There's no training data about Tonio.

01:11:56.940 --> 01:12:01.360
<v Giovanni Barallari>And regardless, it was able to drive through stuff.

01:12:02.440 --> 01:12:04.880
<v Giovanni Barallari>So I guess it's the perfect moment to try stuff.

01:12:06.820 --> 01:12:11.440
<v Giovanni Barallari>I'd say if for any reason you're not happy with Asyncio,

01:12:11.700 --> 01:12:18.420
<v Giovanni Barallari>if for any reason you experience any of the pain points we talked about,

01:12:18.740 --> 01:12:23.460
<v Giovanni Barallari>I think it's like the perfect moment to try new stuff.

01:12:24.040 --> 01:12:28.440
<v Giovanni Barallari>And if you try Tonio and you have like a use case

01:12:29.000 --> 01:12:33.040
<v Giovanni Barallari>where it's not working or the API can be improved,

01:12:33.280 --> 01:12:35.540
<v Giovanni Barallari>like open an issue, open a discussion,

01:12:36.260 --> 01:12:39.120
<v Giovanni Barallari>ping me, DM me on Twitter.

01:12:39.590 --> 01:12:41.360
<v Giovanni Barallari>Oh, sorry, X the everything up.

01:12:41.540 --> 01:12:42.420
<v Giovanni Barallari>I will call it.

01:12:44.440 --> 01:12:47.120
<v Giovanni Barallari>Ping me and we'll figure out.

01:12:48.660 --> 01:12:51.100
<v Giovanni Barallari>Yeah, I think that's my message.

01:12:51.860 --> 01:12:52.580
<v Michael Kennedy>Yeah, awesome.

01:12:52.640 --> 01:12:54.520
<v Michael Kennedy>Well, it looks like a very ambitious project

01:12:54.880 --> 01:12:56.900
<v Michael Kennedy>and I think you've done a lot of interesting things.

01:12:57.080 --> 01:12:58.520
<v Michael Kennedy>So thanks for coming on and sharing it.

01:12:58.740 --> 01:13:00.800
<v Michael Kennedy>Tell people where they can stay in touch with you as well.

01:13:01.140 --> 01:13:08.700
<v Giovanni Barallari>Yes, so my GitHub handle is G-I-0-B-A-R-O.

01:13:09.500 --> 01:13:14.120
<v Michael Kennedy>You can find me with the same handle

01:13:14.140 --> 01:13:23.960
<v Giovanni Barallari>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.380 --> 01:13:35.900
<v Giovanni Barallari>And if you want to join me and Marcelo Trilesinski, again, author of Uvicorn, Starlet, and a bunch of

01:13:36.800 --> 01:13:42.700
<v Giovanni Barallari>stuff he's making with TokenMaxing, we recently started a podcast available on YouTube.

01:13:44.440 --> 01:13:46.620
<v Giovanni Barallari>at the www pod.

01:13:47.060 --> 01:13:47.160
<v Michael Kennedy>Nice.

01:13:47.600 --> 01:13:49.000
<v Michael Kennedy>So people, check them out.

01:13:49.140 --> 01:13:49.680
<v Michael Kennedy>Check out the pod.

01:13:49.840 --> 01:13:50.260
<v Michael Kennedy>That sounds fun.

01:13:50.340 --> 01:13:51.620
<v Michael Kennedy>I'll give it a look as well.

01:13:51.780 --> 01:13:52.920
<v Michael Kennedy>Joe, thanks for coming on the show.

01:13:53.160 --> 01:13:53.880
<v Michael Kennedy>Nice to catch up with you.

01:13:54.120 --> 01:13:54.840
<v Michael Kennedy>Thank you for having me.

01:13:55.140 --> 01:13:56.400
<v Michael Kennedy>It was super good.

01:13:57.700 --> 01:13:59.840
<v Michael Kennedy>This has been another episode of Talk Python To Me.

01:14:00.220 --> 01:14:00.940
<v Michael Kennedy>Thank you to our sponsors.

01:14:01.240 --> 01:14:02.420
<v Michael Kennedy>Be sure to check out what they're offering.

01:14:02.660 --> 01:14:04.000
<v Michael Kennedy>It really helps support the show.

01:14:04.560 --> 01:14:07.060
<v Michael Kennedy>This episode is sponsored by Sentry's Seer.

01:14:07.380 --> 01:14:09.800
<v Michael Kennedy>If you're tired of debugging in the dark, give Seer a try.

01:14:10.360 --> 01:14:12.500
<v Michael Kennedy>There are plenty of AI tools that help you write code,

01:14:12.720 --> 01:14:15.580
<v Michael Kennedy>but Sentry's seer is built to help you fix it when it breaks.

01:14:16.340 --> 01:14:20.420
<v Michael Kennedy>Visit talkpython.fm/sentry and use the code talkpython26,

01:14:20.880 --> 01:14:24.040
<v Michael Kennedy>all one word, no spaces, for $100 in Sentry credits.

01:14:24.740 --> 01:14:26.360
<v Michael Kennedy>And it's brought to you by us.

01:14:26.960 --> 01:14:30.080
<v Michael Kennedy>Talk Python and Python Bytes both now have MCP servers.

01:14:30.660 --> 01:14:33.460
<v Michael Kennedy>Point your AI at 10 plus years of Python episodes,

01:14:34.100 --> 01:14:35.600
<v Michael Kennedy>transcripts, and show notes free.

01:14:36.020 --> 01:14:39.800
<v Michael Kennedy>Click MCP in the nav at talkpython.fm and at Python Bytes.

01:14:40.200 --> 01:14:42.080
<v Michael Kennedy>If you or your team needs to learn Python,

01:14:42.380 --> 01:14:48.160
<v Michael Kennedy>We have over 270 hours of beginner and advanced courses on topics ranging from complete beginners

01:14:48.450 --> 01:14:52.300
<v Michael Kennedy>to async code, Flask, Django, HTMX, and even LLMs.

01:14:52.660 --> 01:14:54.860
<v Michael Kennedy>Best of all, there's no subscription in sight.

01:14:55.400 --> 01:14:57.180
<v Michael Kennedy>Browse the catalog at talkpython.fm.

01:14:57.860 --> 01:15:01.980
<v Michael Kennedy>And if you're not already subscribed to the show on your favorite podcast player, what

01:15:01.980 --> 01:15:02.540
<v Michael Kennedy>are you waiting for?

01:15:03.220 --> 01:15:04.960
<v Michael Kennedy>Just search for Python in your podcast player.

01:15:05.120 --> 01:15:05.940
<v Michael Kennedy>We should be right at the top.

01:15:06.380 --> 01:15:09.140
<v Michael Kennedy>If you enjoy that geeky rap song, you can download the full track.

01:15:09.350 --> 01:15:11.260
<v Michael Kennedy>The link is actually in your podcast blur show notes.

01:15:12.040 --> 01:15:13.420
<v Michael Kennedy>This is your host, Michael Kennedy.

01:15:13.820 --> 01:15:14.880
<v Michael Kennedy>Thank you so much for listening.

01:15:15.050 --> 01:15:15.880
<v Michael Kennedy>I really appreciate it.

01:15:16.300 --> 01:15:17.020
<v Michael Kennedy>I'll see you next time.

01:15:27.480 --> 01:15:28.420
I'm me.

01:15:29.820 --> 01:15:30.920
Get me ready to roll.

01:15:32.560 --> 01:15:33.700
Upgrade the code.

01:15:34.560 --> 01:15:36.240
No fear of getting old.

01:15:37.740 --> 01:15:39.720
We tapped into that modern vibe

01:15:40.160 --> 01:15:41.140
over Kenny's storm.

01:16:11.040 --> 01:16:11.060
Продолжение следует...