Perfect. Hello everyone. Glad you're all here. I'm Jacob Coffee. I work at the Python software
foundation. On the infrastructure. Today, we're going to talk about my fake bread business
called the bakery. The best I could do. But really, we're going to talk about pet 810,
which I do nothing about. But someone told me the best way to learn something is to teach
it. So here we are. Hang on. We're going to cover what the problem is. The start-up time
is pretty rough with Python when it imports the world. We're going to explain 810 like the
syntax. Probably not alive demo because I didn't realize there would be so many people here and
now you made me very nervous. So real world impact measured and then use cases beyond a silly
bread business CLI tool and then how to migrate. So the problem is that when you run anything in Python,
it's going to import the world. So it's eager to import model, loads everything, and we can see
that it loads, it takes 234 milliseconds, which isn't great. If you've used Galer Rust, CLI tools,
it's very snappy. There's also problems with memory bloat. There's also some cool start tax,
which is bad when you're doing serverless things. You probably don't want to use Python for serverless
because of these reasons. And then you have nasty hacks like if type checking. So what is
Pep 810? It's explicit lazy imports for Python, and it's basically going to say instead of
bringing in the world, we have this module over here that we only call once a year or once however long,
and we don't pull it in until it's first accessed, not just when it reads the import statements.
Good news, it was accepted. So it's going to be in Python 3.15. It says it's 50 to 70% faster for
certain workloads. 30 to 40% less memory, which is good because we like trees, and it's a nice
explicit syntax. So no surprises when it happens. You can easily document it. The syntax,
normally we have just from breadcuddle, which is my bread business, it's very official, import,
and we have baked delivery and inventory, and it's just going to import the world, even if you
don't run anything. So even if I do CLI tool, dash dash help, it's still going to import the
world, it's going to take forever. It's really annoying. But with this, the new thing, we lazy import
the thing, and then you only do it when it is invoked. So that is going to be a big improvement.
So if you run dash dash help, you don't access baker, delivery, or inventory, or whatever,
and now it just loads only when you run the one thing. So how is it going to work?
So when Python sees the lazy import, like lazy import, JSON or HTTBX, it doesn't actually import
HTTBX, it's going to use proxy object, it's going to fill the void there, and this proxy goes into the
system modules namespace, I think I have that right, where HTTBX import would normally be.
But there's no file, I think that's correct, and then no code execution happens yet.
So we parse it, we say lazy import, HTTBX, creates that proxy, then you have a lazy module
proxy, then we do the waiting until we want to use it. Until you do like an HTTBX
dot get or whatever module you're calling. So you start up say fast, your help commands are quick,
all that. Okay, so the moment you actually use HTTBX, it does the real import, the proxy object
is transparently replaced in the real module, I believe I had this correct, it's the reification
process of that, and your code never knows the difference. So if it never is access and
ever loads, and that's the whole trick, it sounds like it's this big thing, but it's just the
super simple. So boom, HTTBX is loaded, and we're all happy. So that's the three sort of phases,
very, very dumb down for someone that wanted to learn it, and then share it with you all this
parse way in access phase. We do the proxy, proxy is dormant, and then we do the real import.
So boom, it's not super complicated. We're going to see maybe if we can do the live demo,
I'm terrified of this. Actually, look at this cow, it's in Scotland, it's very pretty.
We're not going to do the live demo, like we should. Oh, how are we going? Because I did this
earlier to make sure that I didn't look like a goober up here, but basically on the left side,
we have, I think I flipped around one of these, yeah, okay, so left side is the normal
running, and then you have the right side with this dash click, which is, you just look in the
code and in the slides, I have some links. You have the same thing running, it's a little bit slower,
it's like seven times, or seven point, five times faster. And then I think I used Claude to help
me because I'm bad at math. We can see some like real numbers here. So 30 to 40 milliseconds,
faster is what the robot says, but we don't, we don't really always trust the robot, do we?
So that's all you're going to get for live demo. But yeah, you can see just from like that one
four letter change, we have a much faster thing. This is a very silly demo, but you can see like,
for example, I maintain or help maintain light star, which is a web framework, you could do the
same thing for a flask or fast API. But when you have this ginormous app, and you do some
CLI command for it, it's going to take what feels like five ten seconds to load. It's not really
that long, I hope. So you have other problems. But with the lazy imports explicitly, you can now do this
without hacking around it. I mean, you could lazy import things. Now you could stuff things
inside of functions, so they're only called when the functions invoke. That's fine. But this is
part of the language now. It's official. Nice non hacky way to do that.
Okay, so we have some bitchmark results here just from my silly examples. This is going to use
the the cap of CLI framework. That's just one that I like, but actually if you,
it's supposed to be faster, but if you compare against CLI and some others, I think capa is not
the fastest, but I like the syntax. So just for me, throwing in this lazy import, I got 23% speed up
for the help command, module import time is 26% faster, and then my inventory command, which does
a whole lot, did not change. So that's the thing, you're not going to always see an improvement,
so it's not like you should go and just find a place import this with lazy import this,
so I think that's a good good solution. Some rural world impact meta. They have their own
fork of CPI fund, and then they do lazy imports and they have a 70% state of 70% start a production
in 40% memory savings, which is what the pet, 18, if you look at the the docs, says is what could
be expected for your own projects. Same thing for HRT, they have module level as the imports,
and for PICI, they have QD bindings, 35% start improvement. So that's pretty significant
for your end users or yourself, whichever. And these are not experiments, there's like rural
world, ginormous companies that do cool things or not cool things, but they have big production
companies using the code, so it works. So PEP 18 is not just for CLI tools, that's just one
use case, it's my very silly example for that. So we have type checking, so if you ever want to do
if type checking and that whole block where it's just like this ugly little block, you can just
instead do lazy import from whatever type thing you're doing and type checking block goes away.
I think it's an error prone and a lot of our linters yell at us for leaving things out or things
that maybe should go in the type checking, so it's very confusing. So and there's also some things
with like I said earlier, the serverless and cold start environment, so every land of cold,
every lambda, cold start costs money and use your user patience. So if we're seeing, for
example meta, solve the 50 to 70% speed up, then that's that's something that will really help
save some money and make users happy for your serverless runs. And also for memory constrained
environments. So these are all good things. There's a simple swap out for the things that you can
identify, need this and I don't know, it's just simple, it's good. But how I do it, so I can
take you through this. So you want to go through in profile, probably better than I have profile
here, but profile your application, whatever it may be, look for heavy things like numpy or
pandas or HTTX or anything like greater than 50 milliseconds to start with and then apply the lazy
import selectively. The module level in it was going to happen on the first axis and you should see
an improvement, you could test this with hyperfine. All of the benchmarks that I have done have been
with hyperfine, and it's just a rest of your life tool for benchmarking. It's very fast. And then
your Python can finally go, almost as fast as go. Don't quote me on that, but probably.
So I'm caveat and gotchas. Import time side effects are deferred, so you may want to like
know our document, that things that might configure logging in import time might need to be
not lazy imported. That's the don't just blindly lazy import the world. I can't wait to see
some people do that, though, and get some error reports. So other things, type checkers still
need updates, I think last night, I don't know if anyone has them, UI does not, has an issue open,
rough for the lansing part, and then my Python empire right, I have not checked, but I assume they do not.
As also, let's see, import errors move. So if before you had some optional dependency,
like I had an optional, in my project at Toml that was century, and nothing happened when I ran
the app, but I forgot to do the right flag or incantation or whatever to add
century as an optional dependency. You're not going to know about it until the app is running,
because we're not going to get an error, it seems simple, but you just have to kind of think about
these things until a century is called and then throws an error because no module phone.
Like it's not always faster, so it's best for conditional and optional imports,
and then some tips, just test with simple things, like your help, if you're actually running a
CLI, or optional dependencies, please keep your eager imports. Don't just lazy import the
things that you rely on, like logging configuration, and then document things for your co-workers,
they will love you for this. So they know this thing is lazy-loaded. When this air happens,
you don't have to Google around or ask Cloud because Cloud's not going to be up to date on this
for a while, probably. So one thing, oh, you can't, there's some docs in the official pet also.
You can't import inside functions. You can't import inside, if type checking, I don't know why you do
that, but you cannot do that. We can go over some caveats actually here.
Yeah, can't do inside functions. Can't lazy import inside classes, because then you kind of like,
it's only going to be in the global namespace at the top, but I mean, for the reason,
because these were, I guess, implicit, lazily imported. This is what people do currently to
try and speed up and get around this. And then this ugly thing that people like to do,
sometimes, where we import star, we should not do that. So I'm glad that this is a syntax here.
So, awesome, reset me. So it's not yet available. There's a reference implementation available now
through the CPI-thon through a ACPI-thon fork, and there's a link in my in the notes that I'll
share out that the lazy imports come all. I think it's the GitHub organization for that.
It was an api-thon, I think this is good, because an api-thon explicit is better than implicit.
That's, that's the most lived by. And this manifests that for us all. And let's us make good choices,
not have some footgun that, well, maybe you can footgun if you do some bad things. But for the most
part, it is good. No surprises here. And it's local. It's only going to affect the one thing that
you tag lazy on. So it's not going to say, mess up like import system, and then lazy import JSON.
It's not going to mess up system, or any kind of example. And you have that granular level of control,
which is very nice. Back on this, you can make some lazy and eager,
easily freely if you were wondering. So there's no limitations around that. And then some resources.
There's the PEP, PEP stuff, Python.org, PEP 810. There's this very bad demo page for breadcuddle,
my very official bread business. There's the CPI-thon fork. I get Hub.com slash lazy imports,
Kabbal, and then hyperfine, which I've done all my benchmarking with. That's a thank you page.
But I wanted to go over, let's see. So more like in in-depth things that I'm glad
that I have time for. So I mentioned a reification I glance over it, but we have this lazy object.
It needs to be reified or like made real. So right now we have lazy import foo. Right now foo
is just like a placeholder in the system modules. Until we call that or do something that
that invokes it if you call type on foo, then that would bring it into the system modules.
Here we have an example of this. We don't we don't have a man. Thank you.
Here we have a this is a silly example where we are not going to have this issue.
It has a typo and it incurs all in the first use. That is a bad thing. You could type
other thing, lazy import it, and it's not going to know that it never exists because it doesn't
technically until it reifies and it exists. So that is something to kind of think about.
Perfect. Now thank you. Okay, I'm Ben.
If you have any questions like I said, I did this to learn it. So I'm not going to be the expert in the room here.
We do a bunch of CPI time core developers. So if you ask me, I'm probably going to point
them, but question away. Okay, so you can ask the questions on the chat or just raise your hand
and I'll give you the mic. Any questions? Two. Two questions from the chat.
Yeah. Okay. Two questions from the chat. Okay. First question. Does it deserve the
problem with secular inputs? Circular imports? Yeah. Well, I think that might still be a problem,
but only a problem went when the lazy import is reified and it becomes a real object. Is that
sound right? You do? Yeah. You're still going to have the circular import, but it's not going
to throw the exception until that is accessed. Okay. So good question. What stops me from using lazy
inputs everywhere because I'm lazy? Nothing. Yeah. You can you can try it. There are probably
some workloads where that might work. I mean, my example was if you did log like logging and
for some reason, you lazy imported your logging not PY file and that configured all of your logging
things. We're not going to have anything done until that first call. So you might miss out on
some things. I don't know if I have that quite right. This one? I'm sorry. And the back? Yeah.
Can we? Oh. Where's the question? Thank you for the talk and for anybody here that
contributes to the paper sounds very good. Can we expect like cascading improvements? There's
really expensive imports that you suggested like pandas and numpy. Can we expect that as they put
in a lazy import internally? Yeah. We'll also get improvements even if we don't lazy import those
branches. Yeah. So with my example, that would be a light star. We provide a CLI with the
web framework that you can call. So for that example, and for yours for when numpy they bring this in
when I guess 315 is well, when 315 is out and people are only using 315 in their project. Yeah,
you can expect to see improvements just by pip updating their requirements. And then, you know,
they'll have the speed up rates. Following on from that, is there a way to feature tests this if
you're still supporting older Python versions that wouldn't have it? Yeah. So I did this
the initial work here. This is hard to talk in. But of course, you go here, already wrote a blog
as soon as I finished my talk, I realized that you go ahead basically this. So he tested this
basically by line. So you can bring this in to your project by adding this reference implementation
line to your tools about which Python you're doing. And this will be a link in my repo for this
talk. And basically here we're saying that I'm not pip on 315. I'm pip on 314 because if you try this
now and do like rough length or rough check or whatever, it's going to yell at you because they don't
support 315. And so you compile this special fancy version, then you can pip install the packages
or your project and then you can test it yourself. You go also did some hyper-fine benchmarking
and then went on to do some fully lazy benchmarking. It's all even greater importance performance.
So almost three times faster. So you can test it now. This is not very easily.
First, thank you to all the people who actually did contribute to this. I have a question
which is about the overhead. Let's suppose that you replace all your imports by the lazy import.
Like the gentleman on the chat set, would that have any drawback? Like would there be any overhead?
Let's suppose that it works, right? I don't think there's any run time overhead from
doing it, no? I think there's like zero, right? Yeah, I'm pretty sure it's zero when time over
head on that. Okay, and also for this circular imports, I might imagine that it would delay
discovering that, oh, I have a bug like much later into the runtime. Yeah. Yeah, so it like it could be bad.
That's why you should selectively do the lazy imports instead of doing a final place and then
massively replace it in your whole project. You have more questions? Yeah, out of time. Unfortunately,
if you have more questions, then please just comment and then talk to Dr. Director at Dr. Yeah.
Or put it maybe into the chat. Thank you all. Thank you!