BuildNot yet confirmed elsewhere1 publisher2 min readPublished
Python 3.15 lazy imports cut a 512 ms cold start to 32 ms on the route that skips boto3
Python 3.15's PEP 810 lazy imports cut a handler's health-check start from 512 ms to 32 ms in a dev.to benchmark that changed five import lines. The boto3 route fell only from 614 ms to 361 ms, so the gain depends on what a cold process runs first.
The Engineer · Build desk

Bar comparison of the benchmark handler's times. Health check route: 512 ms with eager imports, 32 ms with lazy imports. The /s3 route, which uses boto3: 614 ms with eager imports, 361 ms with lazy imports.
Same benchmark handler, eager vs lazy imports, time per route
| Item | Value | Claim |
|---|---|---|
| Health check, eager imports | 512 ms | 5 |
| Health check, lazy imports | 32 ms | 5 |
| /s3 route (boto3), eager imports | 614 ms | 8 |
| /s3 route (boto3), lazy imports | 361 ms | 8 |
What happened
- Python 3.15.0 came out on 9 October, and the author compiled it from the python.org source tarball because their usual tooling had no 3.15 builds yet.
- The test handler imports boto3, httpx, Jinja2, Rich and SQLAlchemy at the top of the file, while its health-check route needs only json.
- Each case ran 25 times as a fresh process on a 2 vCPU Linux container with a warm bytecode cache, and the post reports the median.
- Profiling with python -X importtime put boto3 at about 210 ms of cumulative import time and SQLAlchemy at about 155 ms.
Why it matters
- exposure A missing or misspelled module now fails when production first reaches that code path, so startup stops catching import errors and a CI import smoke test has to do that job.
- constraint Lazy imports need a 3.15 runtime, and services built on numpy or orjson wait on those projects, since neither had cp315 wheels on PyPI as of 11 October.
- capability Because __lazy_modules__ keeps plain import statements, teams can ship lazy loading in code that still has to run on 3.14 fleets.
- cost Upgrading for lazy imports also brings PEP 686's UTF-8 default for open(), a behaviour change on non-UTF-8 locales, mostly Windows, that has to be swept for before the move.
The 16x figure [5] is the startup cost of imports the health route never used. Under eager loading, Python pulled in 551 modules that path never touched before returning its JSON body [6]. Lazy loading took 480 ms off that route [20]. The two heaviest libraries, boto3 and SQLAlchemy, add up to about 365 ms in the post's import profile. That is roughly 71% of the eager 512 ms [22].
The /s3 route is the figure for a request that does real work. It saved 253 ms, a 41% cut or about 1.7x [21]. All of that came from the four libraries the route never loads, and it still pays for boto3 [8]. The author wrote: "Lazy imports move the cost to the first use. They do not delete it." [9]
For the 16x to carry over to a serverless cold start, the first request a fresh process handles has to stay off the heavy imports. Health checks benchmark well because they do almost nothing. We'd expect a function whose cold processes first serve a probe or a light route to land near the 32 ms row. A function whose first request calls S3 should land near the 361 ms row, with boto3 loading on that request [9]. The post timed a container [4]. It did not test a hosted function runtime or report billing.
Beyond the per-import keyword [19], the post documents a global switch. Setting `python -X lazy_imports=all` or `PYTHON_LAZY_IMPORTS=all` makes every import lazy, and `sys.get_lazy_imports()` then returns "all" [10]. Pairing that with `sys.set_lazy_imports_filter()` keeps chosen modules eager: the filter is consulted only for imports that would be lazy, and a False return holds the module eager [13]. The modules to hold back are the ones whose import does work. Plugin registration, model registries, monkeypatching and logging configuration done at import time will not run until something touches the module [15]. Of the global mode, the author wrote: "Good for an experiment in staging, risky as a default on day one." [11] Two forms cannot be lazy at all. `lazy from x import *` and `lazy from __future__ import ...` are syntax errors [16].
In our view the keyword is the right place to start. Each lazy line is explicit and shows up in code review [19]. On this handler, two of the five lines cover most of the measured import time [22]. Whether the work justifies its own budget line depends on how often a cold process's first request lands on a light route.
What to watch
- cp315 wheels for numpy and orjson appearing on PyPI, which decides when data-heavy services can adopt the change.
- A cold-start measurement on a hosted serverless runtime, where the type of the first request decides which modules load.
- Libraries that register plugins or configure logging at import time documenting whether they must stay eager under PYTHON_LAZY_IMPORTS=all.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence45
- Adoption
- Insufficient
- Hype gap+20
- Incentives30
- Confidence45
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Python 3.15.0 came out on 9 October; with no 3.15 builds available through the author's usual tooling, the author built it from the python.org source tarball, SHA-256 checked against the release page.
- [2]
The test handler imports boto3, httpx, Jinja2, Rich and SQLAlchemy at the top, but its health check path only needs json.
- [3]
The lazy version of the handler changes five import lines and nothing else.
- [4]
Each case ran 25 times as a fresh process on a 2 vCPU Linux container with the bytecode cache already warm, and the median was taken.
- [5]
The health check went from 512 ms to 32 ms, 16 times faster.
- [6]
With lazy imports, Python no longer loads 551 modules it never uses on the health check path.
- [7]
python -X importtime shows boto3 alone takes about 210 ms cumulative to import, and SQLAlchemy about 155 ms, before handler code runs.
- [8]
The /s3 path uses boto3 and still pays for it; it dropped from 614 ms to 361 ms only because httpx, Jinja2, Rich and SQLAlchemy stayed unloaded.
- [9]
"Lazy imports move the cost to the first use. They do not delete it."
- [10]
python -X lazy_imports=all or PYTHON_LAZY_IMPORTS=all makes every import lazy; sys.get_lazy_imports() returns "all" in both cases.
- [11]
"Good for an experiment in staging, risky as a default on day one."
ReportedSupportedSource: Author of the dev.to post, on the global lazy imports modeView cited source - [12]
A module can set __lazy_modules__ = ["boto3", "sqlalchemy"] and keep plain import statements, so the same file still runs on Python 3.14.
- [13]
sys.set_lazy_imports_filter() is only consulted for imports that would be lazy, and returning False keeps a module eager, so everything can be lazy except modules whose import side effects are relied on.
- [14]
A missing module now raises at first use, not at startup; a typo in a rarely used code path can hide until production hits that path, so the author advises keeping an import smoke test in CI.
- [15]
Plugin registration, model registries, monkeypatching libraries and logging configuration that happen at import time will not happen until something touches the module.
- [16]
lazy from x import * and lazy from __future__ import ... are syntax errors.
- [17]
PEP 686 in Python 3.15 makes UTF-8 the default encoding for open() when no encoding is given, changing behaviour on machines whose locale was not UTF-8, mostly Windows; python -X warn_default_encoding finds the affected calls.
- [18]
When checked on 11 October, pydantic-core and psycopg-binary had cp315 wheels on PyPI, and numpy and orjson did not.
- [19]
PEP 810 adds explicit lazy imports, written per import as lazy import x or lazy from x import y, which the author described as explicit and easy to review.
- [20]
Lazy imports saved 480 ms on the health check path.
- [21]
The /s3 path saved 253 ms, a 41% reduction, or about 1.7x faster.
- [22]
boto3 and SQLAlchemy together account for about 365 ms of import time, roughly 71% of the 512 ms eager health-check start.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toPython 3.15 lazy imports took my cold start from 512 ms to 32 ms
1 article · October 11, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- Python wheels and packagingFollow
- Python lazy importsFollow
- Serverless cold startsFollow