Lifeguard for Lazy Imports
A fast static analysis tool to aid adoption of Lazy Imports in Python.
What are Lazy Imports?
In Python, every import statement executes immediately when a module is loaded. This overhead is incurred regardless of whether that import is actually used. PEP 810 introduces explicit Lazy Imports to Python, which defer the actual loading of a module until the imported name is first accessed. Lazy Imports can significantly reduce memory usage, startup times, and import overhead, especially in large codebases with deep dependency trees.
However, some Python patterns depend on imports executing immediately. For example:
- Module-level side effects — a module that registers a handler or modifies global state at import time will behave differently if that import is deferred.
- The registry pattern — a module that registers itself (e.g., adding to a global dict) when imported will silently fail to register under Lazy Imports.
sys.modulesmanipulation — code that reads or writessys.modulesassumes prior imports have already executed.- Metaclasses and
__init_subclass__— class creation side effects may depend on imports being resolved.
Adapting an existing codebase to use Lazy Imports can be a daunting task, especially at scale. Lifeguard identifies these incompatible patterns so you can adopt Lazy Imports with confidence.
How does Lifeguard work?
Lifeguard analyzes Python source files for a given project in parallel. It walks each module's AST to detect effects and maps Lazy-Imports-incompatible effects to errors. The analyzer takes a conservative approach towards its analysis: any module that cannot be programmatically determined to be safe to import lazily is marked unsafe by default. This means Lifeguard will err on the side of marking potentially compatible modules as incompatible, leaving potential performance optimizations on the table in favor of production safety.
For a deeper look at the analysis pipeline and architecture, see docs/architecture.md.
Project Stage: Beta
Lifeguard is in active development. We are aiming to be ready for general use by the Python 3.15 final release.
Items on our roadmap
- We are preparing GitHub actions to fully support external contributors.
- We plan to release to PyPI.
- We've tested and support Python 3.12 and 3.14. Other versions may also work. We do not yet support the
lazykeyword added in PEP 810 — but we fully intend to support this ahead of the 3.15 release. - We are actively developing a standalone linter output mode to help users identify which specific lines in their codebase are incompatible with Lazy Imports.
- We plan to add support for easy ingestion of Lifeguard's output to drive Lazy Imports enablement for advanced users (see Using the Output).
Prerequisites
- Rust (nightly) — the crate uses unstable features. Install via rustup and set with
rustup default nightly. - Git — clone with submodules:
git clone --recurse-submodules https://github.com/facebook/Lifeguard.git
If you already cloned without --recurse-submodules, run git submodule update --init --recursive.
Quick Start
The fastest way to try Lifeguard is the run-tree subcommand, which analyzes every .py file under a directory. No additional setup needed.
cargo run -- run-tree <INPUT_DIR> <OUTPUT_PATH>
For example, using the bundled sample project:
cargo run -- run-tree testdata/sample_project output.json
For a full walkthrough including interpreting the output, see GETTING_STARTED.md.
Running Lifeguard
For larger projects where you need more control, you can generate a source DB — a JSON file that tells Lifeguard the full set of Python files in your project and their module paths (see Input Format for details). Follow these steps:
- Generate the source DB. We provide a subcommand to start this file for you, but you may need to tune it by hand. (As the project matures, we hope to make this process smoother.)
cargo run -- gen-source-db <INPUT_DIR> <OUTPUT_PATH>
Optionally, if your project has library dependencies, you can point Lifeguard at your site-packages by adding a lifeguard section to your pyproject.toml:
[lifeguard]
site_packages = "/path/to/site-packages"
You can find out your site-packages path via python -m site. The gen-source-db subcommand reads this section automatically when generating the source DB.
Note: The script may not discover all of your project's dependencies. If Lifeguard reports missing modules, you may need to manually add entries to the generated source DB.
- Run Lifeguard in one of two modes:
- Default: Prints a high-level analysis of your codebase (% of compatible files, top errors, etc.) and writes the JSON output to
OUTPUT_PATH.
cargo run -- <DB_PATH> <OUTPUT_PATH>- Verbose mode: Also writes a human-readable report showing which specific lines in each module cause incompatibility.
cargo run -- <DB_PATH> <OUTPUT_PATH> --verbose-output <VERBOSE_OUTPUT_PATH> - Default: Prints a high-level analysis of your codebase (% of compatible files, top errors, etc.) and writes the JSON output to
Example Verbose Output:
## example.module.foo
### Errors
Line 17 - ImportedModuleAssignment sys
Line 38 - UnsafeFunctionCall example.demo.unsafe_method
Input Format
In some modes, Lifeguard requires a source DB — a JSON file mapping Python module paths to their locations on disk. The format is:
{
"build_map": {
"foo/bar.py": "/local/usr/disk/foo/bar.py",
"example/__init__.py": "/local/usr/disk/third-party/example/__init__.py"
}
}
You can generate this automatically using cargo run -- gen-source-db (see Running Lifeguard), or create it by hand.
Output Format
Lifeguard writes a JSON file with two fields:
{
"LAZY_ELIGIBLE": {
"module1": [],
"module2": ["module3", "module4"],
"module5": [],
},
"LOAD_IMPORTS_EAGERLY": ["module5", "module99", "module100"]
}
LAZY_ELIGIBLE
A dictionary mapping modules that are safe for Lazy Imports to a list of their dependencies that must be imported eagerly. For example:
"module1": []—module1is fully safe for Lazy Imports with no restrictions."module2": ["module3", "module4"]—module2is safe for Lazy Imports, but only ifmodule3andmodule4have already been imported.
Important: Modules that do not appear as keys in this dictionary have been analyzed as unsafe for Lazy Imports.
LOAD_IMPORTS_EAGERLY
A set of modules where all imports within the module must be loaded eagerly. Lazy Imports is essentially temporarily disabled for these modules.
Note the distinction: other modules can still lazily import a module in the LOAD_IMPORTS_EAGERLY set, but when that module does load, its own import statements must execute immediately rather than being deferred.
This set is only used for specific corner cases:
- Custom finalizers (
__del__) — unpredictable execution timing means imports must be available at finalization. exec()calls — dynamic code execution negates static analysis guarantees.sys.modulesaccess — reading or writingsys.modulescould depend on prior imports having already executed.
For more details, see docs/load_imports_eagerly.md.
Using the Output
As a standalone linter
Lifeguard can be used as a standalone linter to identify which specific lines in your codebase are incompatible with Lazy Imports. Run the analyzer with --verbose-output to get a human-readable report showing per-module errors with line numbers (see Running Lifeguard). This lets you treat Lifeguard like a linter: run it in CI or locally, review the flagged lines, and fix them. In this manner, Lifeguard is used as a guide to safely enable Lazy Imports.
To drive a lazy import loader
The JSON output is designed to drive a lazy import loader's filter function. In Python 3.15, importlib.util.lazy_import accepts a filter callback that controls which imports are deferred and which are loaded eagerly. Lifeguard's output provides the data needed to build this filter — using LAZY_ELIGIBLE to identify safe modules and their constraints, and LOAD_IMPORTS_EAGERLY to identify modules that need all imports resolved upfront.
We plan to provide tooling for easy ingestion of Lifeguard's output ahead of the Python 3.15 release. This is a work in progress.
Implementation
Lifeguard is implemented in Rust. We leverage ruff for AST traversal and re-use several crates from pyrefly. We also extend .pyi stub files to annotate known side effects in third-party libraries — for example, marking that a particular module-level function call in a dependency has observable behavior. These stubs are stored in the resources/ folder. See resources/stubs/stubs.md for details on how effect annotations work alongside standard type stubs.
License
By contributing to Lifeguard, you agree that your contributions will be licensed under the LICENSE file in the root directory of this source tree.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lifeguard_lazy_imports-0.1.0.tar.gz.
File metadata
- Download URL: lifeguard_lazy_imports-0.1.0.tar.gz
- Upload date:
- Size: 837.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be6f1c1a9957b22188efd2a06db22aa1038a875736f9713a8fcbb9a5d07cda3d
|
|
| MD5 |
94eb31b6afd7fa6110b332e954e77181
|
|
| BLAKE2b-256 |
48a17dddaf42356f99be6ef3491639feb514bd2106ffdf8b9eadd6668f3684e2
|
Provenance
The following attestation bundles were made for lifeguard_lazy_imports-0.1.0.tar.gz:
Publisher:
publish_to_pypi.yml on facebook/Lifeguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifeguard_lazy_imports-0.1.0.tar.gz -
Subject digest:
be6f1c1a9957b22188efd2a06db22aa1038a875736f9713a8fcbb9a5d07cda3d - Sigstore transparency entry: 2335119592
- Sigstore integration time:
-
Permalink:
facebook/Lifeguard@6064112ebb19150da8900b14d55e9ea8aa320b2f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/facebook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@6064112ebb19150da8900b14d55e9ea8aa320b2f -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file lifeguard_lazy_imports-0.1.0-py3-none-win_arm64.whl.
File metadata
- Download URL: lifeguard_lazy_imports-0.1.0-py3-none-win_arm64.whl
- Upload date:
- Size: 3.5 MB
- Tags: Python 3, Windows ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8845c296a583d17c65f0dfee8b7ca3847a60eb2d84a5112018167727981b673
|
|
| MD5 |
e5a211fe7c3282c1ace9a25ac423281d
|
|
| BLAKE2b-256 |
33bf588f2ded68c33e67de0ec46ff379e813f103dd7162fdb30f5c0dc9547bbc
|
Provenance
The following attestation bundles were made for lifeguard_lazy_imports-0.1.0-py3-none-win_arm64.whl:
Publisher:
publish_to_pypi.yml on facebook/Lifeguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifeguard_lazy_imports-0.1.0-py3-none-win_arm64.whl -
Subject digest:
c8845c296a583d17c65f0dfee8b7ca3847a60eb2d84a5112018167727981b673 - Sigstore transparency entry: 2298558475
- Sigstore integration time:
-
Permalink:
facebook/Lifeguard@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/facebook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file lifeguard_lazy_imports-0.1.0-py3-none-win_amd64.whl.
File metadata
- Download URL: lifeguard_lazy_imports-0.1.0-py3-none-win_amd64.whl
- Upload date:
- Size: 3.6 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a8bc4d02fd1f2125e9b3a1f0d77ad75088e5b112c24c1b8d8af3a43bdbe8b66
|
|
| MD5 |
f7b4ddbd6292da39b53e2408e288a9e7
|
|
| BLAKE2b-256 |
dfcf63d89181bd316b100282c2b11deb76f3d350e9f55e4fbc4069e4bc02d5a8
|
Provenance
The following attestation bundles were made for lifeguard_lazy_imports-0.1.0-py3-none-win_amd64.whl:
Publisher:
publish_to_pypi.yml on facebook/Lifeguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifeguard_lazy_imports-0.1.0-py3-none-win_amd64.whl -
Subject digest:
7a8bc4d02fd1f2125e9b3a1f0d77ad75088e5b112c24c1b8d8af3a43bdbe8b66 - Sigstore transparency entry: 2298558512
- Sigstore integration time:
-
Permalink:
facebook/Lifeguard@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/facebook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 4.1 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20fa0d99b9a31142d08f51522c73fccdcc59bb9fa1da1e63c440b8f812a40773
|
|
| MD5 |
7eead4893171905fd559afd1bbcfa063
|
|
| BLAKE2b-256 |
c1248f6090d7559c39e37d272b736b51d77a8d59538c0e0d5c60c0b1cba3a892
|
Provenance
The following attestation bundles were made for lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish_to_pypi.yml on facebook/Lifeguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
20fa0d99b9a31142d08f51522c73fccdcc59bb9fa1da1e63c440b8f812a40773 - Sigstore transparency entry: 2298558548
- Sigstore integration time:
-
Permalink:
facebook/Lifeguard@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/facebook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 4.0 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
223b1bc3c96b9bc6ac72a65373a997106be6518a763cec81f778a4af0278de10
|
|
| MD5 |
bee4d709f810991ca056c34e628fa1d8
|
|
| BLAKE2b-256 |
b4a15379953e22225f6f5711d1a7220fed606ce63edb42a73b99139059c2895c
|
Provenance
The following attestation bundles were made for lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
publish_to_pypi.yml on facebook/Lifeguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifeguard_lazy_imports-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
223b1bc3c96b9bc6ac72a65373a997106be6518a763cec81f778a4af0278de10 - Sigstore transparency entry: 2298558489
- Sigstore integration time:
-
Permalink:
facebook/Lifeguard@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/facebook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file lifeguard_lazy_imports-0.1.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: lifeguard_lazy_imports-0.1.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.8 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5ada42720f98ca1a36ed2c3031e5d8f6d512337c0d36b6490f8c4f6a78997c4
|
|
| MD5 |
2514c848331bcf9d9ee8b34cc206e340
|
|
| BLAKE2b-256 |
7a7e175e7d325d4e2b64f43694dea77acb7d9be2f8e060ec950ab9ebef865aa9
|
Provenance
The following attestation bundles were made for lifeguard_lazy_imports-0.1.0-py3-none-macosx_11_0_arm64.whl:
Publisher:
publish_to_pypi.yml on facebook/Lifeguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifeguard_lazy_imports-0.1.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
e5ada42720f98ca1a36ed2c3031e5d8f6d512337c0d36b6490f8c4f6a78997c4 - Sigstore transparency entry: 2298558525
- Sigstore integration time:
-
Permalink:
facebook/Lifeguard@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/facebook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file lifeguard_lazy_imports-0.1.0-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: lifeguard_lazy_imports-0.1.0-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 3.9 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9eb58110e9036a1a29935ba1e6e6a457675e580e8e2ec5b38b753ce76bbf4cd7
|
|
| MD5 |
c33b9d14586f4064c7b5e98fc5809ec4
|
|
| BLAKE2b-256 |
4358b50854693c08155e1797777faad39ce643451eccdcff947dd71b98e95456
|
Provenance
The following attestation bundles were made for lifeguard_lazy_imports-0.1.0-py3-none-macosx_10_12_x86_64.whl:
Publisher:
publish_to_pypi.yml on facebook/Lifeguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifeguard_lazy_imports-0.1.0-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
9eb58110e9036a1a29935ba1e6e6a457675e580e8e2ec5b38b753ce76bbf4cd7 - Sigstore transparency entry: 2298558536
- Sigstore integration time:
-
Permalink:
facebook/Lifeguard@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/facebook
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@f0993f1108bea3986a784625d8c349f95eeb9dd0 -
Trigger Event:
workflow_dispatch
-
Statement type: