Python 3.15 lazy imports are the headline feature of PEP 810, written by Pablo Galindo Salgado and Dino Viehland and shipped with CPython 3.15.0 on October 9, 2026. You put the new soft keyword lazy in front of an import, like lazy import torch, and Python does not load the module until the first time you touch it. Python is open source, and uv 0.13.0 shipped the same day and made 3.15 its default Python.
In this post I go over what the keyword does, a benchmark I ran on a small machine, the three things that broke when I tried it, and when I would use it in a startup codebase. My verdict: for any command-line tool that imports openai, numpy or torch, this is the cheapest speed-up I have seen in years. For a long-running server it does nothing useful, and I would leave it off.
Why do I care? Every internal tool we write at Trellis starts with the same three imports, and each one pays for all three just to print --help. Mettons a CI job calls such a tool 200 times a day. I made up the 200, but at 2.5 seconds per call that is 503 seconds of nothing, every day.
How much faster are Python 3.15 lazy imports?
I tested on Python 3.15.0 (the Oct 9 build), installed with uv, on a small 4-core Intel N95 machine. Each case is a fresh process, nine runs, and I report the median wall time. The packages were numpy, openai and torch 2.14.1 (CPU build). One machine, bare imports only, so the ratios are the point and the milliseconds are mine.
case median ms
bare python, does nothing 18.8
import numpy 208.5
numpy, made lazy 14.3
import openai 677.2
openai, made lazy 14.6
import torch 1816.9
torch, made lazy 21.6
CLI header, eager openai+numpy+torch 2514.6
CLI header, lazy openai+numpy+torch 14.1
The last two lines are the story. A script with three heavy imports at the top takes 2,514.6 ms to start. Put lazy in front of the same three lines and it takes 14.1 ms, under my bare python -c pass median of 18.8 ms, so that is noise at the floor, not magic. Torch alone goes from 1,816.9 ms to 21.6 ms, about 84 times faster, and openai from 677.2 ms to 14.6 ms, about 46 times.
A normal import runs the module’s code, and torch has a lot of code. A lazy one just records “someone will want this later” and moves on.
Can I get the speed-up without changing my code?
Yes, and I did not expect that. You can start the interpreter with -X lazy_imports=all, or set the PYTHON_LAZY_IMPORTS=all environment variable, and the 3.15 release notes say that makes all imports lazy by default.
I ran unmodified code, a plain import torch, with that flag: 14.2 ms. Plain import openai, numpy, torch with the flag: 20.4 ms. No edit to the source file at all.
Would I ship that? No, not as a first move. It changes when every import fails, including the ones inside your dependencies, and I only tested bare imports. But for finding out how much of your startup is imports, it is a one-flag experiment worth two minutes.
What broke when I tried it?
Three things, all on 3.15.0, with the real error messages.
You cannot use lazy inside a function. I wrote lazy import json in a function body and got SyntaxError: lazy import not allowed inside functions. Same story inside a try/except block: SyntaxError: lazy import not allowed inside try/except blocks. The release notes say it is by design: module scope only.
That second one matters more than it looks. The classic optional-dependency pattern, try: import orjson then except ImportError: fall back to something slower, does not convert. Keep it eager.
Star imports are out. lazy from os import gives SyntaxError: lazy from ... import is not allowed. Fine, nobody should star-import anyway.
Errors move. This is the one that scares me, and I think it is the real cost. I wrote lazy import not_a_real_module and the script started with no complaint. The failure only showed up when I touched the name, with a chained ImportError: lazy import of 'not_a_real_module' raised an exception during resolution, followed by the plain ModuleNotFoundError: No module named 'not_a_real_module'.
So a typo in an import used to kill your app at startup. Now it can boot, pass a health check, and die on the first request that reaches that line. Me, I would rather find out at 2 pm on a Tuesday in CI than at 3 am in production.

Does lazy import torch really make torch free?
No. Lazy only moves the cost. With lazy import torch followed by torch.zeros(1), the process took 1,858.1 ms, about the same as the 1,816.9 ms eager import. The price is paid at first use, whole.
The win depends on whether your program touches the heavy thing at all. A --help, a --version, a subcommand that only reads a file: those never touch torch, so now they never pay for it. A command that runs the model pays what it paid before.
That is also why I would skip it in a long-running API server. You pay the import once at boot while nobody waits, and lazy moves it onto the first unlucky request, 1.8 seconds. Serverless is the opposite case, where cold start is the bill. For small agents on a tight free tier, like the one in my Talorys post, the keyword at module scope is worth a look.
How would I use Python 3.15 lazy imports at a startup?
- Run your CLI entry point once with
-X lazy_imports=alland time it. If nothing changes, your startup is not imports, and that is a fine answer. - If it helped, put
lazyonly on the heavy imports at the top of the entry-point modules: torch, numpy, openai, anything over a few dozen milliseconds. Leaveosandjsonalone. - Write one smoke test that touches every lazy name, so a typo fails in CI and not in production. A loop doing
getattron each name is enough. - Leave your long-running servers eager.
One ecosystem note: litellm failed to build on 3.15 in my one try. I did not dig, so read nothing into it beyond checking your own dependencies before moving to a brand new interpreter. The openai, numpy and torch packages all installed and imported fine for me.
What about the JIT in 3.15?
The release notes also describe an upgraded experimental JIT. Their own claim, from pyperformance results, is a 7-8% geometric-mean gain on x86-64 Linux, with single benchmarks from roughly 15% slower to over 100% faster. Those are the docs’ numbers, not mine, and I did not run the JIT.
My guess, only a guess, is that an AI app spends most of its time in C extensions like torch and numpy, which a Python JIT does not touch. So I expect the JIT to matter less for us than this.
What I did not measure
It is one small machine, with bare imports and no real application startup work. I did not test memory, the free-threaded build, or a cold disk cache, so the medians reflect a warm page cache.
Bon. Next I want to run one real Trellis command with -X lazy_imports=all against the normal start, 50 runs each. If the gap is anywhere near the toy benchmark, I switch our entry points the same day and add the smoke test to CI. If it is not, I will say that here too.