Python 3.15 lazy imports took my cold start from 512 ms to 32 ms
Python 3.15.0 came out on 9 October. The release notes list a dozen headline changes, but for anyone running Python behind an HTTP endpoint, a CLI or a serverless function, one matters more than the rest: PEP 810, explicit lazy imports. There were no 3.15 builds available through my usual tooling yet, so I built it from the python.org source tarball (SHA-256 checked against the release page) and…
Python 3.15 introduces lazy imports, a feature that can drastically improve performance for applications that start up slowly. Lazy imports delay the loading of modules until they are actually needed, rather than loading them all at once at the beginning of the program. This change, part of PEP 810, can significantly reduce the time it takes for a program to respond to requests.
The article describes a test case involving a small handler that imports several libraries like boto3, httpx, Jinja2, Rich, and SQLAlchemy. Without lazy imports, the handler took 512 milliseconds to load and respond to a health check request. However, with lazy imports enabled, the same handler took just 32 milliseconds, making it 16 times faster.
The main benefit of lazy imports is that they cut down the time spent loading modules that aren't used in a particular request. For example, boto3 and SQLAlchemy, which are used for AWS and database operations respectively, were taking up a large portion of the loading time. By only loading these modules when they are actually needed, the program becomes much quicker.
There are a few ways to enable lazy imports. You can use the keyword `lazy import`, explicitly specify `lazy import` at the beginning of the file, or use the `-X lazy_imports=all` command line argument to enable lazy imports globally. However, enabling lazy imports globally could cause issues with import side effects, so it's recommended to use it cautiously.
One important thing to note is that errors in lazy imported modules are raised only when they are actually used, not at startup. This means that a typo or missing module in a rarely used code path could go unnoticed until production hits that path. It's advised to keep an import smoke test in CI to catch such errors early.
Another change introduced with lazy imports is that UTF-8 is now the default encoding for open() and friends when no encoding is specified. This can cause issues for machines with non-UTF-8 locales, so it's important to check for potential problems before upgrading to Python 3.15.
The article concludes with a checklist for upgrading to Python 3.15. This includes adding 3.15 to the CI matrix, checking for binary wheels for compiled dependencies, and profiling startup time with `-X importtime` to identify which third-party imports can be made lazy. It's also suggested to try `PYTHON_LAZY_IMPORTS=all` in staging to catch any potential side-effect issues before enabling lazy imports globally.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.