Monitor performance issues & errors in your code

#214: Dive into CPython 3.8 and beyond Transcript

Recorded on Sunday, May 5, 2019.

00:00 Michael Kennedy: Python 3 is coming soon. It's scheduled for release at the end of October 2019, and you can already download test versions today. Given that Python ships on an 18-month cycle, it's time to talk about what's coming for us Python developers in the fall. On this episode, I meet up with Lukasz Langa and Anthony Shaw to chat about the highlights of this upcoming version of Python. Also quick show note, we recorded this on location in Cleveland at PyCon 2019. There may be a small amount of background noise, but I think you'll barely notice. This is Talk Python to Me, Episode 214, recorded May 5, 2019. Welcome to Talk Python to Me, a weekly podcast on Python. The language, the libraries, the ecosystem, and the personalities. This is your host, Michael Kennedy. Follow me on Twitter, where I'm @MKennedy. Keep up with the show and listen to past episodes at, and follow the show on Twitter via @talkpython. This episode is brought to you by Microsoft. Be sure to check out what they're offering during their segment, it really helps support the show. Lukasz, Anthony, welcome back to Talk Python, both of you guys.

01:14 Panelists: Hello. Hi Michael, good to be back.

01:15 Michael Kennedy: Yeah, it's great to have you back. We are not recording over Skype far distances, but in fact we're right here at PyCon, and who knows how the audio, how this is going to come out, but hopefully it'll sound good enough, but it's great to be here on-site in Cleveland with both of you.

01:29 Panelists: Yeah, it's a unique experience to actually see you doing the recording. Yeah, it's really good to be here in Cleveland. The sun has finally come out today after the third day of PyCon.

01:38 Michael Kennedy: The fog was pretty incredibly epic. That was pretty special, and yeah, Lukasz, the latency on this video call is incredible, man. It's so, it's lifelike.

01:46 Panelists: Yeah, so this video call is like 3D, amazing.

01:49 Michael Kennedy: Yeah it's amazing, so thank you both for being here. We're going to talk about Python 3.8, and maybe looking a little bit beyond that some of the PEPs that are out there and all of the cool stuff there, but let's just start with PyCon. How's your experience this year? It felt a little different to me, I'll say why in a minute, but you go first.

02:05 Panelists: I'm Lukasz Langa, I've been co-chairing the Language Summit this year with Mariatta, and this is an absolutely new experience for me. So that was interesting, actually herding all the carts at the right time to the right spots, pre-selecting talks, actually making sure that everybody has the opportunity to speak, making sure that everybody is engaged. That was new, so I was very happy to have it over with after that day, I heard it went pretty well, so I was happy about this. The rest of PyCon was also rather intense so far for me. I've had a talk about Black, and did like an extended Q&A that I just performed during the poster session on Sunday morning.

02:44 Michael Kennedy: That's really awesome, you know, I feel like Black is one of these things that has just taken off. Like I often ask people what module do they recommend, or package, or what is like special that they've seen. It's way more than any other single answer is Black. That's awesome, congratulations.

03:00 Panelists: Yeah, thank you, I'm very happy about it. In fact, I think there was at least five talks that mentioned using Black as a good thing in them. Seems like we've solved an issue that people had, solved a problem that people had.

03:13 Michael Kennedy: Yeah, we've had these linters, and they tell us when you're doing wrong, and we're just like, can't you just fix it? Like, I don't want to be told what's wrong, I just want it to be better.

03:21 Panelists: Well the difference between Black and other formatters of which we have a few in the Python community, is well Black was kind of brave, obnoxious enough to tell you like that, it's going to just be done in one way. It's not really configurable, but that kind of changes it to a workflow tool, or if you decide to use the tool, now the question of all the formatting sort of disappears. The question of how to format your code is no longer a problem when you're developing your own projects.

03:49 Michael Kennedy: Yeah, and if you're on a team, you don't have to have this debate anymore of how we do stuff, right. You just run Black, and that's how you do it.

03:55 Panelists: Absolutely.

03:56 Michael Kennedy: Awesome, okay, Anthony, how's your PyCon going? What do you notice this year that's special?

04:00 Panelists: Yeah, this is the first PyCon I've actually given a talk, so that was definitely a milestone for me.

04:05 Michael Kennedy: Yeah, what was the talk?

04:05 Panelists: It was on code complexity, and I talked about Wiley, which is a project I've been working on, and the principles of code complexity and why complexity is bad.

04:14 Michael Kennedy: So how awesome cyclomatic complexity is?

04:16 Panelists: I actually talk about how it's inevitable, the more uses you have, to have more cyclomatic complexity. People ask me how the talk went, and my response so far has been I don't remember, because I was so nervous that it was just a sort of adrenaline-fueled dream. So that was really cool, then yesterday, there was mentored sprints.

04:36 Michael Kennedy: That's new, right?

04:36 Panelists: Which is a new thing this year. That's probably the highlight actually for PyCon so far, is actually just being in there and mentoring people to contribute to different projects, and working with someone who ended up being able to send in a pull request to CPython by the end of the day, and I think it was their first one, which was awesome.

04:56 Michael Kennedy: Yeah, that's super cool, that's got to help adoption and contributions, right. Because even me personally, I think about okay, well I'd love to do something, but I really don't even know how to get started, how to build it, what the right rules or expectations are. Like, it's a lot of work, and if somebody who can sit down who's done that, or is at least knowledgeable about that and walks you through it, like the second time onward is much more likely to be smooth.

05:20 Panelists: Yeah, and it was a proper amount of time allocated as well. It was, I think it was just over four hours on the second day in the afternoon. So it's good enough time to actually sit and work on a proper issue or a proper feature, and actually work through it from start to finish.

05:34 Michael Kennedy: Yeah, and you actually got a PR in place, not just started or whatever.

05:38 Panelists: Yeah, exactly.

05:38 Michael Kennedy: Yeah, that's awesome. To me it felt like when I walked into the expo hall that the booths were a little bit bigger. There were a little bit more people. It just seemed like a little bit, I don't know, a little more people, a little more energy, even than last year, and last year was amazing. I don't know, did you have this feeling?

05:54 Panelists: Yeah, I was particularly impressed with the portrait of Guido in the Capital One booth, which you've got to see online if you haven't seen a picture of it already.

06:02 Michael Kennedy: Yeah, their booth was really artistic, that was pretty cool, nice. All right so it's great to be here at PyCon, but let's talk about the future. Let's start with the idea of when is Python 3.8 going to be out? How precisely do we know that, and Lukasz, maybe just say a little bit about like, you're mostly in control of this at the moment, right? Like the release schedule and management and whatnot, right?

06:26 Panelists: Currently I'm serving at the release manager of Python 3.8 and I wrote the schedule for where everything's going to go. Traditionally Python has been released every 18 months, which puts Python 3.8 at the end of this year. In particular, give or take, it should be the end of October. The reason why I'm not saying a particular date, even though it is in the PEP, is those things tend to be a bit fluid. Like in the past three alphas that I released, we've been a day early or a day late pretty much every time.

06:58 Michael Kennedy: Yeah, would you be willing to hold up a release if there's some important feature that's two days away?

07:03 Panelists: In fact this time, there was a small issue, well small in size, but big in significance, that held up Alpha 4 that I had a bit of time for early in the week, which kind of makes me late for my own schedule now, and I have to release Alpha 4 tomorrow at the start of sprints, but those things are more important to get right than to get on time, and this is something.

07:28 Michael Kennedy: Yeah, especially when it's 18 months. If you were shipping monthly updates, whatever, just ship what you got, right, but if it's 18 months, that couple of days, that means a big, big deal, right?

07:37 Panelists: This is exactly the point. We try to make the consecutive releases, even on the alpha level, consecutively better and not introduce a break that is going to be later reverted since every release, including alphas, is being tested by our users, and we're happy to see actually alpha releases being increasingly used by the community to test their own libraries and applications. That used to be our problem, it was only after the beta releases, and in fact our C's very often were the first releases the users would see. Nowadays, with PyPI working better with CI options being out there for pretty much everybody, we see more adoption of alpha releases, which is great, but that is also a bigger responsibility on the alpha releases because you are no longer free to just make a breaking change and then later revert it, well it's alpha anyway, well no it isn't, because...

08:28 Michael Kennedy: Didn't you see what it was? It's an alpha, come on.

08:30 Panelists: Yes. Now we would need to have feature toggles that look at the alpha version to see whether a particular bug exists or not, and we don't want that.

08:39 Michael Kennedy: This might be a slightly political question, but have the releases of Python recently become more stable? Like is it, it feels to me that people have almost zero trepidation or worry about just adopting 3.7 when they had 3.6, or 3.6 when they had 3.5 these days, and it seemed like there was more concern about what's going to break when I go to the next semi-major version. Has that changed, or is that like perception?

09:05 Panelists: For a number of years, the only version that people used was 2.7, and that was for a long time. So it's created this false perception that Python is invulnerable. That every particular update of it never introduces issues that were not there before.

09:21 Michael Kennedy: Because they were just minor fixes on the thing that was unchanging, right?

09:24 Panelists: Exactly, so that was good, at the same time a blessing and a curse since every particular version of Python, including Python 2.4 to Python 2.5, Python 2.5 to Python 2.6. It did introduce internal changes that made large projects actually complex to migrate. The biggest example from the Python 2 world it was when Python 2.5 was released. Zope at the time, created such a problem for itself that it took them enough time that they migrated to Python 2.6 directly. So that previous version worked on Python 2.4, whereas the next one directly on Python 2.6, they did not migrate in time for 2.5.

10:07 Michael Kennedy: Right, it took so long to migrate, they're like, let's just aim for the next one.

10:09 Panelists: Yes, so this is all just a long, long introduction to just you know, let you know that in Python 3, there are also changes. Many of those changes are deliberate, right. We are changing how the internal memory representation of objects looks like. We are introducing and removing byte codes. We can change how modules get initialized. There's multiple things that might break real applications, but they're breaking them in ways where it is impossible for us to guarantee eternal compatibility with those things.

10:43 Michael Kennedy: Sure, but this greater adoption of alpha versions and testing and CI probably doesn't hurt, right?

10:48 Panelists: Some kind of crashers, right. Downright bugs and whatnot, that I feel we are doing a better job these days with. Also just because Python 3 finally gets enough adoption that those versions get vetted much better.

11:04 Michael Kennedy: Right, absolutely.

11:05 Panelists: When I started contributing, we were just working on Python 3.2. So for a number of years, almost none of the things I worked on were actually very heavily used in the industry. Like nowadays the situation is different, right. So fortunately the time between that change and actually having real users report on it is way shorter, which makes the quality just better.

11:27 Michael Kennedy: Yeah, that's awesome. Anthony, let me ask you what your perception of that's like. You work for a pretty major company that probably has stability in mind and stuff. What's your perspective, and what are you seeing at like Dimension Data and places like that?

11:39 Panelists: It's definitely become easier for people to install newer versions of Python, which is really helping. So in terms of I guess moving towards things running as microservices on Docker, for example, there's this, it's not a single system running a specific version that's got to support all these different applications.

11:55 Michael Kennedy: Right, it's not cross your fingers, we're upgrading the server.

11:58 Panelists: Yeah, we're not running on the mainframe anymore. So I think that that's really helping in terms of there's more automation for systems deployment, and there's a lot more tooling being used to automate like the building of new environments. There's this kind of idea of an immutable infrastructure now where you basically create compute infrastructure in the cloud and you build it on a specific version.

12:20 Michael Kennedy: And you don't change it right, ever?

12:22 Panelists: Yeah, so I think that has actually made it easier to move to newer versions, because you can just build up new infrastructure with the new version, test how it works, and you can inspect it properly. Whereas like 10 years ago, you're talking about, we need to go and buy a million dollars of hardware to build an identical environment to see if this new version's going to work for us.

12:43 Michael Kennedy: Right, and maybe you have like downtime the whole weekend, the team stays all night, and they do the testing and the rollout. You know these days, when I go to websites and I see "We're down for maintenance," or we have even crazier, like "We have scheduled maintenance over this like, two to four hour window." I'm just like, what are they doing? What possibly could take four hours to upgrade? I understand maybe there's a migration and you're down for a few moments, but four hours, it doesn't take. Anyway, it just seems pretty wild when you see it, but that used to be common, right. So I guess, yeah, so it doesn't matter as much.

13:14 Panelists: Yeah, it still matters for some really big applications. I know some of the biggest software vendors still have four-hour maintenance windows every Saturday. We definitely have to live through some of that pain at the moment, but more and more, I think people are using this sort of automated deployment and automated infrastructure, which is making it a lot easier to upgrade.

13:33 Michael Kennedy: That's awesome, what about the beta version, or the alpha version even, of 3.8. You guys said late October for...

13:41 Panelists: The main release, 3.8.0.

13:41 Michael Kennedy: The main release. But work that back, when will we see stuff that we can start playing with, can we already? I know Anthony, you like get it and build from source a lot and play with it, and you guys do as well, but when does the average person who just wants to install a beta or something like that, get access to it?

13:59 Panelists: Any alpha release is released in both the forum of sources that you can freely build. If you are cloning the repository from GitHub, there's tags that tell you exactly when a particular release was made, but also for alphas, betas, and later release candidates and actual versions that we release, we do have binaries, right. So for both macOS and Windows, we have plenty of binaries that you can use to test out your software. I would advise to do it as early as possible. Especially 3.8, was kind of like a shy release.

14:34 Michael Kennedy: Right, because we had the whole governance thing. All the major changes were sort of put on hold, right?

14:39 Panelists: Yes, now we are just four weeks before the first beta, which is the feature freeze for us, right. Since beta 1 to the main 3.8.0 release, we are just fixing bugs. In some unlikely cases, maybe even reverting features that we identified are not ready for primetime, and that's unlikely to happen. Like, what is more likely to happen, is like this is the time where you know that breaking changes are no longer being, new breaking changes are no longer being accepted. So it's a great time to actually start using your CI to test your libraries, your applications on Python 3.8 as well. Expect problems, there's things that we have not identified even though we have our other extensive regression suite of tests, but it's great to be able to identify those things early so by the time the distributors come in and package Python 3.8, it's quality is good and we're transparent, we just can run your application with minimal churn.

15:37 Michael Kennedy: That's pretty cool, can you test it with tox, or what's the best way to sort of test on 3.7 and 3.8 beta or whatever?

15:43 Panelists: There's many possible ways, depending on what particular operating system you're using. For projects that are already using a CI system like Travis. There are ways to just utilize the latest development version of Python, and at the moment it's 3.8. So by just saying that you would also like to run your tests on the development version of Python, you're going to get beta version like in a month that currently is going to be a form of alpha. So that is probably easiest, because you don't have to actually install anything locally on your computer, which tends to make things complex when you have many interpreter versions with you. The Homebrew project likes to default to one Python 3 version, one Python 2 version. So there's pyenv that you can use to have multiple installations, and obviously yes, there's tox. They actually work rather well together so you can set up your matrix of tests that later are run online, but you can also run them locally, which is what I'm doing.

16:40 Michael Kennedy: Yeah okay, that seems pretty easy. All right, Anthony, let's kick off this PEP section, and talking about the actual features, which as we all know, appear as Python enhancement proposals and they go through a life cycle and whatnot. If people are wondering what PEPs are out there, what might make it into 3.8, where would they go to find that out?

17:00 Panelists: So they come to website, and there's a list of PEPs. There's also a PEP index on the list of PEPs. I've also made like a small web app called PEP-Explorer, where you can go and search and filter and pull specific Python versions and get the status of the PEPs. So I use PEP-Explorer because I spend time looking at PEPs and reading about them, and trying to understand what's coming in future versions. So yeah, if you were just curious, then I'd say the PEP-Explorer is probably a good way to go.

17:27 Michael Kennedy: Yeah, the PEP-Explorer is pretty awesome. It's just a nice little grid. It's on GitHub pages, right? Yeah, and I'll just link to that, and of course people can go to, but yeah, it's really nice to just keep track of that, and I find that super helpful. All right, so let's kick it off, maybe since you're holding the mic, we'll go with you first. What's one of the notable things that's coming that you want to talk about, what PEP or feature?

17:52 Panelists: So I thought I'd cover the two ones that changed the language first. So there's assignment expressions, colloquially known as the walrus operator.

18:02 Michael Kennedy: The walrus operator, yeah so this is PEP 572.

18:06 Panelists: Yeah, PEP 572, so in Python if you want to assign a value to a variable, you use the equals symbol. That doesn't return anything, so if you just do a = 1 in the REPL, then that won't return anything in the REPL. An assignment expression is basically a way of combining the assignment of a value to a variable and returning the variable back again. So the reason you would want to do that is in some statements, for example, within list comprehensions, within while statements for example, within if statements, and the thing in the if statement, the comparison for example, you can actually do assignments inside the comparison, and it just removes some additional code that you might have to do. Also there's a few other examples in list and dictionary comprehensions where you can do some fairly smart things inside the comprehension.

18:58 Michael Kennedy: Yeah when I first saw this, I thought interesting, I don't know it's really needed, but I wasn't super against it or anything. But certainly seeing it in the list comprehension space, and seeing it used in other places as well, I think I'm pretty positive on this language change, it's pretty nice. Certainly, anytime you need an expression right, within like some kind of comprehension, maybe a lambda or something like that, this often is the only way to, like if you want to create a variable, but also test it in a list comprehension, and that might be the response of a function. You could maybe have to call that twice, once when you test it, and once when you put it into the list. Now you could assign it and then test it, right. So these things get simpler.

19:42 Panelists: Yeah, they get simpler. I think looking at the syntax, people's initial response is often, "I can't see where I would use that." It takes awhile for these types of pieces of syntax to become common, because once you know the patterns and which you would use it, and you've memorized them, and then you start to use it more and more over time.

20:01 Michael Kennedy: This portion of Talk Python to Me is brought to you by Microsoft and Azure Pipelines. Azure Pipelines is a CI/CD service that supports Windows, Linux, and Mac. It lets you run automatic builds and tests of your Python code on each commit or pull request. It is fully integrative to GitHub, and it lets you define your continuous integration and delivery pipelines with a simple YAML file. Azure Pipelines is free for individuals and small teams. If you're maintaining an open-source project, you'll even get unlimited build minutes and 10 concurrent pipelines. Many Python projects are already using Azure Pipelines. So get started for free at

20:39 Panelists: Then the second PEP I guess changes the language slightly, is positional-only arguments. Basically this is PEP 570, which is also being accepted and merged into Python 3.8. It wasn't part of Alpha 3, so it'll be in the Alpha 4 release I believe, yep. Basically this one is you add a forward slash in the list of parameters in a function definition, so that it says that it's only positional arguments in this function. The reason for that is basically to protect an API to ensure that people only use positional arguments and they don't start to use them as keyword arguments.

21:18 Michael Kennedy: Yeah it's pretty interesting. It's like the anti-keyword only argument one, right. So with the keyword argument one, I don't know that many people actually know about it, but it's pretty cool. So if you say function f(*, arg1, arg2, arg3), those all have to be explicitly called as keyword arguments. This is like, I want to make it impossible at least in this section of the parameters, to call them as keyword arguments, right?

21:47 Panelists: Yeah, it's a cool feature, and it's also going to help with a lot of the standard library, that's the other justification. There's a lot of the Python standard library where the API needs to be protected so that it can be iterated on, where this feature's basically going to help lock that down. Also in 3.7, there were some improvements to the performance of method calls. That performance improvement doesn't work with keyword arguments.

22:09 Michael Kennedy: I see.

22:10 Panelists: So basically you could potentially use it as a way of enforcing that performance improvement.

22:15 Michael Kennedy: Okay, that's interesting. The example that I saw, I think, if I remember this correctly, was just like range. Like even just knowing when you see stuff, if you quickly read it, like you could have range and say stop and then start, and then step. Or you could have start and then stop and then step, or you could have step and then start, and just seeing those, I mean even the words are kind of similar, and just going, no I just want you to always just say start and then stop and then step or whatever, right. I'm just requiring them to not have this sort of almost arbitrariness of the order of the parameters seem like an interesting idea there as well.

22:48 Panelists: Yeah, and an additional detail is the fact that many of the functions that are implemented in C don't implement keyword arguments, so they're effectively positional only by the sheer fact that they are just being implemented in C. This just enables us to express those same APIs in Python faithfully, so that alternative implementations recreate the API in exactly the same way.

23:12 Michael Kennedy: Oh yeah, that's interesting. So you don't want like the leaky abstraction of the C implementation to leak out and maybe break PyPy or something like this.

23:21 Panelists: Yeah, so currently the issue is actually the opposite, where PyPy does not necessarily care that some argument is positional only in CPython, so they allow for keyword use of it, and then that piece of code is problematic going back to CPython. So that is just carrying of making your library code, your application code, sort of exchangeable between runtimes.

23:46 Michael Kennedy: Yeah, interesting, what about Cython?

23:47 Panelists: Cython is its own kind of thing, because it's a language that is being compiled, or rather transpiled to a bunch of C or C++, which is then compiled to a C module. They are kind of free to do a lot of modifications that Python itself is not free to do, because they are compile time modifications.

24:06 Michael Kennedy: Right, their transpiler can make the adjustment it needs anyway, right?

24:10 Panelists: Yes, the source code that you are reading is not the source code that is being executed.

24:15 Michael Kennedy: Interesting, all right. What's the next PEP or feature you want to talk about?

24:18 Panelists: Let's cover a few of them, and in fact the slew of PEPs is all related to typing. Let me start with something old, which is PEP 544 Protocols. That PEP should have been accepted a long time ago, but it did not because of the governance situation.

24:35 Michael Kennedy: So Protocol, or is this kind of like interface inheritance type of thing, or what's going on with Protocol here?

24:41 Panelists: Protocol essentially is a way to introduce duck typing to static typing to type checkers. So you can have interfaces, or like protocols, they are called protocols across the Python documentation too, which is why we're using that name. But you can have essentially implicit interfaces that are being implemented by a class, by a type, and then the type checker is able to act on them when you express a need for a given one as an argument to a function. For example, if your function accepts anything that has a read method, now you can express that type.

25:16 Michael Kennedy: That's really cool, I'm super excited about this.

25:17 Panelists: Me, too.

25:19 Michael Kennedy: Because if you take two things, like maybe a set and a dictionary, but you want to express I'm going to have those types and I want to work with them, but really all I care about is I can iterate it. That's probably not the perfect example, but you know like, it's hard to kind of make the type system express that now, and this just says, well if it has an add and a pop method, we're good, like whatever right, is that Protocols?

25:39 Panelists: Yes, so Protocols is the answer to a question that we've received a lot early on when PEP 484 came out, like the original formation of static typing for Python, but isn't static typing in direct opposition to what we have been telling everybody to do for all those years, which is duck typing? If it quacks like a duck and looks like a duck, it is a duck we don't care if its instance is working. We just care that the calls find the right methods with the right arguments and everything is fine. So now with Protocols, you can actually structurally, express this. That all you care about is a given field or a given method.

26:20 Michael Kennedy: I like it, I know you're a proponent of type hints and Mypy and all that kind of stuff. How do you see the state of that these days?

26:28 Panelists: Well we're definitely on the rise there. At my time at Instagram and at Facebook, we've seen a lot of improvements both in terms of security, a team velocity, and as well just being able to comprehend the source code when types were introduced to the biggest PHP component of Since I guess 2013, I wanted to see something similar in Python. So PEP 484 came out, soon enough Python 3 started getting adopted more and more, and this is when annotations, which are the nice way to express types, have been gaining adoption. These days, from what I've heard at the conference now, 90% of functions in the Instagram code base, which is north of two million lines of code at the moment, is covered in types, which is amazing. That is a big achievement, so definitely this trend is on the rise, which I am very happy about.

27:30 Michael Kennedy: Yeah, that's awesome, Anthony what are your thoughts on type hints, type annotations. Do you like them, have they changed your code, do you use them?

27:37 Panelists: I actually use them very rarely. In 3.7, there's the type annotations, the delayed evaluation type annotations.

27:45 Michael Kennedy: Yeah, it's gotten a little nicer in that way.

27:46 Panelists: In 3.7, which makes it a bit easier in terms of what you have to import and when, but the only reason I use them seldomly is because I mainly work on libraries, which I publish to PyPI, which are used by people who have Python 3.5, 3.6, and some 2.7 as well. So I really have to cover the lowest common denominator when it comes to users, because they're mainly utility libraries that I work on, not sort of single-deployment applications.

28:16 Michael Kennedy: Yeah, or something like Black that doesn't really get consumed directly, but is more executed, right, like Black or pytest or something. Yeah interesting, what's the next one?

28:24 Panelists: So the next PEP would be PEP 585, that I actually wrote. Well it's still in draft form. So to kind of set the stage for the PEP, what Anthony said is there's plenty of cases where currently typing that was added rather carefully to the language requires you to import names that you're later using as types. There's some situations where you are introducing names to your global scope just for type aliasing or to introduce type variables.

28:56 Michael Kennedy: Right, for example if you've got a function and you want to say its return type is this object, you now have to import at the top. If you had never actually had that part called, maybe it would have never been imported until lazily, or there's changes in behavior because of that, right?

29:10 Panelists: Yeah, this is often problematic, right. Like, what is even more just cumbersome for the user is that there is plenty of either built-in types or abstract-based classes that have their equivalence in the typing module. Meaning if you want to express that some argument is a list of string, you have to import an uppercase list from the typing module, and say uppercase list of string. I always found that clumsy. I always found that it is something new that you have to explain to new programmers that are first interacting with typing, and there's not really a great reason for that. It was just you know, we wanted the actual lowercase list to be orthogonal and not know anything about the static typing concept, which is mostly used by an external type checker that does not have a big runtime component. It does have a little right, because you can inherit from a generic type, so you can actually create your own data structure where you say that this is, I don't know, a collection of types T, right. So that this is a possibility. But for very many cases, this runtime complement is just a hindrance. It's something that you have to remember to import, the names look different because they're uppercase and lowercase. They might look exactly the same, like in form of set, but they actually mean something else now. The point being, so that's the first issue. The second issue is that this is something that sits in memory. This is something that you spend time on when you're starting up your program. So I always felt like this is something that we can maybe live without, hence PEP 563, which actually postpones evaluation of annotations. That was introduced in Python 3.7. Stemming from that, having that foot in the door, that now the annotations are not evaluated anymore, we can regain some of the usability that people expect just by the fact that this can be still valid Python syntax, but it doesn't have to be valid as runtime. So we can get away without importing things from typing. The type checker will know exactly what you mean anyway. We can come back to using a lowercase list of string instead of uppercase list of string, and a few other things.

31:22 Michael Kennedy: Do you still do the bracket of string on the lowercase list type?

31:25 Panelists: Yes, yes, so we will never do like pointy brackets for that like in Java or C++, because our LL parser is unable to deal with that case. Maybe if we switched to a different one, of which there is discussion, maybe then that would be possible, but at that point, it will still be way too late...

31:45 Michael Kennedy: Yeah, I think it's fine the way it works. It's different, but it's just totally...

31:48 Panelists: It's different, but it's a way of expression.

31:51 Michael Kennedy: There's nothing that makes the angle brackets in templates or generics necessarily the right way.

31:56 Panelists: Yes exactly, it's like as long as humans understand what those things mean, the goal has been achieved. So yeah, the rest of the PEP 585 is just an attempt to reform some of the pre-existing constructs in the typing module, like creating new types, casting, aliasing our type variables into variable annotations so that they are also not evaluated at import time, which enables again, usage of types that are not imported and some of those tricks with syntax like lowercase list and dict and whatnot. So that's PEP 585.

32:31 Michael Kennedy: Yeah, that's cool. While we're on this performance and type annotations and stuff, what's the story of MypyC?

32:40 Panelists: Oh this is actually a very interesting story. Mypy has traditionally been slow, like to the point of running it over the entire Instagram code base was taking over five minutes. So this was a thing that you could do in continuous integration, but you could not absolutely like run it in an editor or whatnot. We had some hacky workarounds to at least make people and editors happy. I wrote like a silly Flake8 Mypy plugin at some point that kind of brought us somewhere, it was useful for awhile. But all of that was just not very great, so in the meantime, Mypy started implementing incremental typing, meaning the graph of your modules, which did not change, can be cached, so that with every change like most of your computation is already pre-done. That is evolving to this point now, with a well-populated cache, that cuts the time to around 40, 50 seconds.

33:35 Michael Kennedy: Yeah, so that's like a six, seven times improvement.

33:37 Panelists: Yeah, it's a big improvement, so that's good. But still, the cold type checking was rather slowish. In the meantime, Facebook started developing its own type checker for Python. Well more with the goal of creating a static analysis tooling that just uses types, so the type checker part was only the base of the static analysis that was being performed on that very code with the important use case of doing security checks. One of the goals of that new type checker was like, we have to be faster than Mypy, right. So that created a competition, and competition is always good. So in the meantime, he revived his original idea that hey, if we have types, we can actually try to compile the Python code in a way that runs it way faster now.

34:28 Michael Kennedy: What is it compiled to?

34:30 Panelists: So that's interesting, right. My basic compiler actually creates a C extension. It actually transpiles to C. This sounds weird until you think about the C API that Python provides, and the Python C API is meant to be consumed by C, so it is just natural that you would have a generator that emits valid C4 in your given use case. It turns out that with just a few constraints on how your program works, you can achieve 20 to 30 times performance boost with that, so that's great. Then a real production application like Mypy, it's consistently four times faster.

35:11 Michael Kennedy: This portion of Talk Python is sponsored by Microsoft and Visual Studio Code. Visual Studio Code is a free open source lightweight code editor that runs on Mac, Linux, and Windows with rich Python support. Download Visual Studio Code and install the Python Extension to get coding with support for tools you love like Jupyter, Black formatting, pylint, pytest, and more. And just announced this month, you can now work with remote Python code bases using the new Visual Studio Code Remote Extensions. Use the full power of Visual Studio Code when coding in containers, in Windows Subsystem for Linux, and over SSH connections. Yep, that's right, auto-completions, debugging, determinal source control, your favorite extensions. Everything works just right in the remote environment. Get started with Visual Studio Code now at Do you see use cases for that outside just Mypy? Like random person doing data science that needs their Python parts to go faster?

36:08 Panelists: Currently MypyC tries to limit their scope since they perceive the attempts by previous projects that meant to speed up Python. Those attempts will fail mostly on trying to be 100% compatible with every single feature of Python. So they are focusing on a subset, but they're growing the subset as much as they need it. The big missing piece currently is there is no async/await support and with that support I could actually have Black compiled.

36:40 Michael Kennedy: Oh, that'd be cool.

36:42 Panelists: Also significantly speed up the formatter, which is already pretty performance it already does pretty well but that would just make it so much better for the users. In fact, I think I managed to get Sully, the core developer of MypyC, rather excited about the prospect of having Black as the next production customer of MypyC, so we'll see, I have my fingers crossed.

37:06 Michael Kennedy: Yeah, that's exciting. All right Anthony, what's the next one on our list of cool features in 3.8?

37:10 Panelists: So this one actually is still in draft, it hasn't actually been decided, and potentially might be deferred to a later release if it gets accepted, but when I've talked about features, at least proposed PEPs, this one gets quite a bit of attention, and I call it runtime audit hooks. Basically the PEP is a way of setting a callable when certain system methods within the Python standard library get called, for example opening a network socket or requesting a URL, or opening a file, or lots of different cases I guess of sort of low-level standard library functions or methods. When they get called, then you get notified.

37:50 Michael Kennedy: That's super cool. So like if for some reason I'm in a lock-down environment, and I want to use some package or write some app, and we think it's not talking to the network or the file system, but it turns out all of a sudden it's opening sockets or DNS stuff, that might be something to inspect.

38:08 Panelists: Yeah, so potentially you could lock down a Python distribution or a Python process to not be able to open certain URLs or open network sockets under certain circumstances.

38:20 Michael Kennedy: That's cool, so with the hooks, do I get to say I saw what you did and okay, or I saw what you did and no. Is it like a place to stop it or to control it?

38:28 Panelists: Yeah, the default, just as an FYI, but if you wanted to throw, raise a runtime error or something else, in line then, it would actually stop the request through to the function.

38:40 Michael Kennedy: That's pretty awesome, I think this is pretty interesting. I know there's some restricted environments in even like app stores and stuff that maybe it would be cool to package this up and use it. So yeah, definitely nice. Lukasz, what do you think about this one?

38:52 Panelists: Well I actually think this is very important. If you ever worked for a break on an organization, very often like the audit trail of what actually happened is important, not just for security reasons, very often cascading errors that end up with an entire site being down are very hard to foresee. They're very easy to make mistakes, or like long fixed, they're all patched, there's not a big red switch that if you just press the button, the site goes down. It's very often something that it was hard to combine, and having the trail of oh, this happened first, and then another thing happened later, that is very valuable. So I see this feature not only as a security feature, but a just you know, post-mortem kind of feature as well.

39:39 Michael Kennedy: That's cool, Anthony, do you envision this might enable a different set of tooling? Like we have visual debuggers now. Could you maybe have other types of analysis and tooling and whatnot?

39:51 Panelists: Yeah, in terms of tooling I guess there's a lot of things in the standard library that you might want to add hooks in, and also an easy way of putting hooks into additional modules as well, and then having people to catch those and deal with those separately. I can definitely think of a few examples of libraries, de-serialization libraries, not naming any specifically, that have...

40:13 Michael Kennedy: Rhymes with sickle?

40:15 Panelists: That have security backdoors just in terms of the way they work, and so unless you explicitly specify to load it with a safe mode, then you can actually run...

40:25 Michael Kennedy: That was a different one and a different rhyme maybe.

40:27 Panelists: Yeah, XML as well is another one. There are sort of known, I guess security back doors in certainly libraries, and basically this could be a way of protecting against those.

40:37 Michael Kennedy: Okay yeah, that's great, because we should not be doing these operations while loading this file.

40:42 Panelists: Yeah, if you're loading a YAML file or an XML file, it shouldn't be opening network sockets.

40:48 Michael Kennedy: Yeah probably not, or issuing sub-process commands, or any of these not-so-lovely things, that's right. All right Lukasz, what's next on our list?

40:58 Panelists: I would like to just say that, you know, there's quite a few PEPs that are still in draft form, and the authors have an entire four more weeks in which they can decide to finish their PEP and publish it, so things might change, but the ones that I am like personally interested in is always, of course, typing. So let me just cover two more. The first one is PEP 586, so that's Literal Types and second one is 589, which is TypedDict. Both of them are kind of an example of our type system kind of starting conservatively, and then growing based on need, right. So Literal Types are very interesting, because there are a bunch of calls where the behavior, like the return type, or you know, the cascading other arguments that you're going to use in the function, depend not on a type of an argument, but on the actual value that you are passing.

41:49 Michael Kennedy: Right, positive integer, negative integer...

41:51 Panelists: Well, the parametric types are kind of hard, but what we are doing with literal types is something like the open built-in. Like with the open built-in, you have a certain number of modes, and depending on whether you're saying rb or r, the resulting IO is either bytes or is strings, and currently there is certain hackery both in Mypy and Pyre to just work around this, but it would be good if the actual type system supported this feature. So Literal is all about being able to express those types so that if you pass None here, it's going to behave differently, if you pass on string here it's going to behave differently if that string is rb or is wb or is r, you know, and so on and so on. So that's Literal Types, then there's some very, very interesting etch cases and deep thought in that PEP, it is surprisingly long and complex, I'm not going to go into this now.

42:46 Michael Kennedy: I can imagine, yeah.

42:48 Panelists: Another one is though, TypedDict, right. Originally dictionaries have been envisioned in the static typing as oh, it does this key value store, so there's keys of a certain type, and there's values of a certain type. What happens in practice is that a lot of preexisting Python applications do not use named tuples, do not use adders or data classes, which are very new. Instead, they use kind of lightweight classes in the form of dictionaries that have keys and values of various types. So there can be named, which is a string, but there can be a birth year, which is going to be an int, right. Based on the actual name of that key, you're going to have differing types, so that was very cumbersome to express in the previous form.

43:40 Michael Kennedy: Almost like a schema, yeah.

43:42 Panelists: Yes, it's very much like schemas. So now there is a way to describe a typed dictionary in the form of a data class-like type, where you just express it like class, like saying this dictionary is going to have keys that are like this, and this key is going to be a string, this other key is going to be an int. That solves already a lot, but then the interesting part is when those things start nesting. That actually enables you to construct rather complex schemas that can be used directly in JSON or in other forms of serialization. So that PEP alone is also very useful in practice. Even though you could just say, people are doing it wrong, they should be used named tuples or other forms of typing instead, well you kind of have to be pragmatic, you know. You see preexisting valid-use cases of this, and you have to just adhere to those.

44:35 Michael Kennedy: Yeah interesting, I guess since you're really into typing and you're on the core dev side of things, what do you think about libraries? Especially I'm thinking of like web frame works that use typing for like serialization and stuff. So like Molten for example, you can have a class that has fields, but also those fields have types, and then you say this web function takes this class, but it's like really a form submission, and it'll like convert stuff to integers, or like validating it sometimes. Is that, in your mind, awesome, or is that an abuse of the type system?

45:13 Panelists: So this is interesting right, because obviously as long as the type system is kind of an extension of the type system we're using for type checking, or maybe is even exactly the same, that is using a shared vocabulary, that's great. In a way, we support that, we would wish to see type hints in more places. In fact, in 3.7, I extended single dispatch so that now you can just use annotations on arguments instead of saying, register of int, you can just say register, and using the annotations of the first argument, it'll behave as you expect. So you can use type annotations at runtime for whatever you want as long as the type system is kind of you know, the same with what we're using it for. Some use cases use annotations, the function annotations, in incompatible ways, and that creates issues because an increasing amount of tooling, like Visual Studio Code, PyCharm and whatnot, gets confused by seeing something that you know is clearly not a type in the place where types are expected.

46:17 Michael Kennedy: Yeah, yeah, an example of that, so this example I gave you with Molten seems like it's consistent. The type checkers says it takes an int, it's actually an int at runtime, it is, but I can't remember, but some of the other frameworks maybe REST frameworks, they would say like, you could say that this parameter is a header, and what it actually is is the string value with that name out of the header. It's like the value comes from the header, but like at runtime, it's not a header. It's not a dictionary, whatever the header is, it's actually a string or an int or something. Seemed like it was really cool and clever, but also like incongruent with what Python intended.

46:53 Panelists: A certain amount of those things are valid use cases. Let's say in the case of adders, adders create valid classes for you from minimal information that you provide in source code. So this class is being fully functional at runtime, however, the type checker does not know this. It just sees just some magic decorator, and just this minimal set of attributes on it, and it does not know that a certain amount of built-in methods have been created, and a certain amount of functionality within it and whatnot has been added. So at least in the case of Mypy and Pyre, additional functionality had to be implemented in those type checkers to understand that those types actually behave a bit differently from regular classes. But that's just something that users want, something that users need, so we're going to go based on that.

47:44 Michael Kennedy: And it is probably worth it, yeah.

47:45 Panelists: We're going to be extending that.

47:47 Michael Kennedy: Cool cool, Anthony, what do you got next on our list here?

47:50 Panelists: That's actually everything we have for 3.8.

47:52 Michael Kennedy: So ship it, we're good?

47:52 Panelists: Yeah I think we're done now. Actually that PEP in particular, the typed dictionary PEP, I have been thinking, if anyone's in a JSON schema project, it's really cool, it's basically a way of defining a schema for JSON documents. Can definitely see that if this PEP gets accepted, somebody will build tooling to integrate between the JSON schemas and this new TypedDict type.

48:16 Michael Kennedy: Yeah, and it makes perfect sense. I mean dictionaries are so similar to JSON in a sense, and they both have this sort of dynamicness, but mixed types, and I mean they're very very sort of mappable.

48:27 Panelists: Yeah, so I think it's just a matter of time until someone builds a library where you give it a JSON schema and it will generate a TypedDict class, and then you use that class. Similar in the same way that you, being in a ORM, when you would describe like a data class and then you would deploy it as a database. It's basically like a similar way of reflecting documents.

48:46 Michael Kennedy: That's cool, I can definitely see it for serialization. Like you say, this function takes a TypedDict, but what actually it is is a form post or a JSON post, or like a REST call or something, it's cool. So let's see, did we talk about multiprocessing? Is that coming in 3.8, or is that beyond?

49:02 Panelists: That's in 3.8, so one particular thing that is not PEP worthy, but it's still a very interesting new feature is that traditionally multiprocessing, which has been created to solve the GIL problem, has solved it partially. What I mean by this is yes, there is a master process that creates a bunch of children, and then delegates work through it, so you can just call Python functions, and those Python functions actually are executed on the other side in the child process, but the way this is achieved was that function call has been pickling the arguments of the function you were calling, and that ended up being on the child side, that child unpickled the arguments, it did the computation it needed, and then if there was a return value it wanted, it actually had to pickle that return value again and pass it back to the master process, and the master process unpickled the returned value again.

49:55 Michael Kennedy: And if that's big, it's very slow for example, right?

49:57 Panelists: Exactly, so for like small things, that was mostly fine, but if you had like a gigantic haystack and you were looking for a needle in it, just pickling that haystack was taking, and then unpickling it on the other side, it was taking a lot of time.

50:11 Michael Kennedy: We were going to run that on all six cores, so here's six copies of our 10 megabyte, whatever right?

50:17 Panelists: Yes, that is actually annoying because if you have a master process that say, it gets a web request, in the time that you are spending on the pickling of that haystack, nothing else can be actually done in Python, because the GIL is still there on the master process. So you are solving the GIL problem only partially. So now, multiprocessing introduces this new, fantastic feature where you can declare a shared memory segment and share that memory between master, well, the parents and children. What that does is you can actually get away with a lot of serialization and deserialization. So for certain kinds of tasks like surge, like filtering, this will decrease the churn just needed to pass data around. Meaning it'll bring us way closer to the world we want to see, which is that yes, there are certain Python processes, they still have the GIL, but it does not matter because we can use as many of those processes as we have cores, and everything is fine.

51:19 Michael Kennedy: Yeah, and you don't have the replication of the memory and the copying and all that, that's awesome. So I'm really excited, when I saw that come out, I'm like oh, this is going to be great, so that's in 3.8?

51:27 Panelists: Yes, that is already in.

51:28 Michael Kennedy: Cool, and what about the subinterpreter PEP 554, that's beyond?

51:34 Panelists: So that's interesting, that is kind of related.

51:37 Michael Kennedy: Yeah, they're in the same category of things, yeah.

51:38 Panelists: Yes, however, the multiprocessing feature does have limitations, right. The shared memory segment is not right for any arbitrary Python object, there's like restrictions on what types you can use. That was actually the complex functionality to be added within particular operating systems, shared memory handling is way different, so you have to understand how those differences work and which process is now responsible for creating that shared memory segment and shutting it down and freeing that memory when everything is shutting down. So that is all great work by Davin Potts. Like multiprocessing is one thing, but subinterpreters is what if you had this multiprocessing API and actually just had one process, and just used many Python interpreters within it, each with its own GIL. To achieve that, many changes in the Python C API have to be added, like much cleanup internally in terms of what constitutes local and what constitutes global state, have to be done. Eric Snow is working hard on that. As far as I can tell, this is deferred to Python 3.9. I'm eagerly awaiting that, I think this is going to be a great improvement.

52:45 Michael Kennedy: Yeah it could definitely change the threading story. I mean, multiprocessing's strong in Python. Async and await is super cool for IO-bound stuff, but threads have always been a kind of well, sometimes they're helpful, sometimes they're not, depends. This could be awesome, right. You could just dedicate a subprocessor, a subinterpreter, excuse me, to each thread, and really get free of that.

53:07 Panelists: I agree.

53:08 Michael Kennedy: Cool all right, well thank you both for sharing what's coming, pretty excited about 3.8.

53:15 Panelists: Cool, it was a pleasure.

53:15 Michael Kennedy: Yeah, it's going to be great.

53:17 Panelists: Yeah on that topic as well, I guess we got 3.9, so some of the PEPs are being deferred to 3.9. On the topic of subinterpreters, the unpacking at the startup sequence and also the initialization configuration, there's two proposals for that, one is PEP 432 and the other is PEP 587, which are interrelated, because if you have subinterpreters, you want the interpreter startup time to be fast, and also the configuration to be flexible. So I think 3.9 will definitely see some more proposals related to that. Which it's going to hopefully improve the startup time of Python 3. As we know, it's a little behind where Python 2 was, for various reasons, but it'll be a great step forward.

53:59 Michael Kennedy: Yeah that'd be really awesome, and then it also might make this subinterpreter stuff even better if those little subinterpreters can get created faster as well. I don't know how related they are, but pretty cool. All right, we used up almost all our time, so I won't keep you guys much longer, but especially Lukasz, let me ask you this. Will there be a Python 4, and does it matter? I mean, on one hand like we've got stuff that's 0.1.2 versions that have been around for 10 years with 100 releases. We've got Python 3, if we don't have major breaking changes, is there a reason to start calling it 4 and 5 and 6, or is that just going to scare people with the history? What's your perspective here?

54:39 Panelists: We are at Python 3.8 now. We're about to release 3.9 later, another 18 months later. Historically Guido expressed his distaste with numbers after the decimal point that have more than one digit. So he disliked the notion of having 2.10, 2.11, the same with 3.10, 3.11. However, we have both philosophical and technical challenges with just releasing a Python 4. Well the obvious philosophical one is that the transition between Python 2 and Python 3 was very, very challenging, right. It took us a lot of effort.

55:17 Michael Kennedy: And there's a lot of fatigue I think, in the community. Let's just not go through that again for awhile.

55:22 Panelists: Absolutely, our closets are still full of skeletons. We are really trying hard not to make that mistake again. It's not only a problem for the users, it was also unpleasant and a problem for the core developers so we are really careful to make changes in a very incremental manner now, and communicate them well, and make them gradually so that we are disrupting our users the least. Which just means calling something Python 4, well it would probably be just scary on its on, just on the power of that number. But just more practically speaking, because of this Python 2 and 3 transition, there is a ton of code in the wild that does checks exactly for the number 3 and such version and info, and those checks would become invalid if we introduced Python 4, which is one of the reasons why you know, like Linux had problems when it suddenly became Linux 3, and why we have Windows 10 now. Just for that practical reason, I do expect that we're going to see Python 3.10 first at least before we ever decide to call the next release Python 4.

56:31 Michael Kennedy: Yeah, yeah, so a 3.10's way more likely. Maybe we should call it Python 6, because then it's like two times three.

56:37 Panelists: Well I always wanted to as a proposal, to introduce calendar versioning to Python.

56:41 Michael Kennedy: Oh yeah, what do you think about calendar versioning?

56:43 Panelists: If Python 2.7 was called 2014.1.

56:46 Michael Kennedy: Actually yeah, yeah.

56:47 Panelists: Then maybe people would reconsider.

56:49 Michael Kennedy: Be like whoa, really, 2014, what's up here? We just upgraded from 2013.

56:53 Panelists: We're trying to remind people how old their Python distribution is, so maybe they'd upgrade faster.

56:57 Michael Kennedy: That's funny.

56:58 Panelists: Well, and I'm in no power to make that change, I could be in power to create a PEP about it, but this is probably not a sword I'm willing to fall on. However, let me tell you this. All of my private projects do use calendar versioning. That's the only versioning that I am familiar and comfortable with. There's obviously semantic versioning, but I don't know about others, but at least I don't see myself being as strict and consistent with applying semantic versioning every time.

57:26 Michael Kennedy: Right, like what does it mean to push the major version versus minor version, like.

57:30 Panelists: Yes, so obviously there's rules, but like the devil is in the application. Do you apply those rules consistently in every given time? I wrote an auto formatter, because I was not able to apply rules of code styling consistently every time, so I don't trust myself enough to do the same for semantic versioning, and if I'm not doing that, then my users cannot depend on you know, what they expect from semantic versioning. Hence just using calendar versioning, way easier, adopted by many popular projects like Ubuntu, like Twisted, like Adders.

58:01 Michael Kennedy: Yeah, I love the calendar versioning. I don't know that it makes sense for like, the main Python, maybe, maybe it does, maybe it doesn't. It would be effective on showing how old some stuff is, but certainly I feel like semantic versioning requires, like on libraries, it requires some expertise in that library. Like I depend on library A, it depends on library B, I see that when I pip installed it, it's 0.1.3. Six months later, is that out of date? I have no idea, like I don't even know like roughly how old that is, but if I saw the calendar version on all the dependencies or stuff I'm not super familiar with, I'd be like oh yeah, actually this is pretty much new or it's old. It makes it easier for newcomers, I think. All right guys, thank you for being on the show and sharing all this, and looking forward to when you actually release 3.8.

58:48 Panelists: I'm looking forward to that too, thank you very much.

58:50 Michael Kennedy: I can imagine.

58:50 Panelists: Thanks, Michael.

58:51 Michael Kennedy: Yep, bye. This has been another episode of Talk Python to Me. Our guests in this episode have been Lukasz Langa and Anthony Shaw, and it's been brought to you by Microsoft. If you're a Python developer, Microsoft has you covered. From VS Code and their modern editor plugins, to Azure Pipelines for continuous integration, and serverless Python functions on Azure. Check them out at Want to level up your Python? If you're just getting started, try my Python Jumpstart by Building 10 Apps course, or if you're looking for something more advanced, check out our new Async course that digs into all the different types of Async programming you can do in Python. Of course, if you're interested in more than one of these, be sure to check our Everything Bundle. It's like a subscription that never expires. Be sure to subscribe to the show. Open your favorite pod catcher and search for Python. We should be right at the top. You can also find the iTunes feed at /iTunes, the Google Play feed at /play,

59:49 Panelists: and the direct RSS feed at /rss on This is your host, Michael Kennedy. Thanks so much for listening, I really appreciate it. Now get out there and write some Python code.

Back to show page
Talk Python's Mastodon Michael Kennedy's Mastodon