Skip to main content

Lifeguard for Lazy Imports

PyPI - Version License: MIT

A fast static analysis tool to aid adoption of Lazy Imports in Python.

Lifeguard

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.modules manipulation — code that reads or writes sys.modules assumes 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've tested and support Python 3.12 and 3.14. Other versions may also work. To analyze the explicit lazy import syntax from PEP 810, pass --python-version 3.15.
  • 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).

Install from PyPI

Lifeguard is published on PyPI with prebuilt wheels for Linux, macOS, and Windows (x86-64 and ARM64). It requires Python 3.12 or newer and no Rust toolchain:

pip install lifeguard-lazy-imports
lifeguard run-tree /path/to/project output.json --verbose-output verbose.txt

python -m lifeguard_lazy_imports is equivalent to the lifeguard command. The cargo run -- examples below build and run the tool from source; with the installed package, replace cargo run -- with lifeguard. PyPI releases are cut manually and can lag behind the main branch. Run lifeguard --help to see what your installed version supports.

Prerequisites for Building from Source

  • Rust (nightly) — install via rustup. Cargo uses the nightly pinned in rust-toolchain.toml; there is no need to change your global default toolchain.
  • 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 discovers .py files under a directory and follows resolvable top-level imports. File and directory names below the input root must be ASCII Python identifiers; other paths are skipped.

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:

  1. 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. Both gen-source-db and run-tree read this section from <INPUT_DIR>/pyproject.toml. Relative site_packages paths are resolved against INPUT_DIR. You can override the setting with --site-packages /path/to/site-packages.

Note: Discovery follows top-level import statements and may not discover all dependencies, such as imports nested in functions or conditional blocks outside the input tree. If Lifeguard reports missing modules, you may need to manually add entries to the generated source DB. For explicit lazy syntax, pass --python-version 3.15 to both source discovery and analysis.

  1. 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>
    

Example Verbose Output:

## example.module.foo
### Errors
ImportedModuleAssignment (1)
  Line 17 - sys

UnsafeFunctionCall (1)
  Line 38 - 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"]
}

With --verbose-output, the JSON also includes IMPLICIT_IMPORTS (a module-to-dependencies mapping) and IMPORT_CYCLES (lists of modules in each cycle). Use --sorted-output for deterministic ordering of these fields.

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": [] — module1 is fully safe for Lazy Imports with no restrictions.
  • "module2": ["module3", "module4"] — module2 is safe for Lazy Imports, but only if module3 and module4 have 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.modules access — reading or writing sys.modules could 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, sys.set_lazy_imports_filter() installs a 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.

Metadata

Release files for lifeguard-lazy-imports 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lifeguard-lazy-imports 0.6.0
File Size Uploaded Provenance
lifeguard_lazy_imports-0.6.0.tar.gz 940.4 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for lifeguard-lazy-imports 0.6.0
File
lifeguard_lazy_imports-0.6.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
lifeguard_lazy_imports-0.6.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
lifeguard_lazy_imports-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
lifeguard_lazy_imports-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
lifeguard_lazy_imports-0.6.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
lifeguard_lazy_imports-0.6.0-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 26.3 MB

Release files / lifeguard_lazy_imports-0.6.0.tar.gz

Download URL lifeguard_lazy_imports-0.6.0.tar.gz
Size 940.4 kB
Tags Source
SHA-256 checksum
How to use checksums
c290cc13c66b4eb744e784350a5c52d950c81b4ad03949b3bff283bee0b4ed33
BLAKE2b-256 checksum
How to use checksums
5f7cba3ea69a7ea04e2b8797096adeda0d21259dc8f382b27dd26b97d013c973
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / lifeguard_lazy_imports-0.6.0-py3-none-win_arm64.whl

Download URL lifeguard_lazy_imports-0.6.0-py3-none-win_arm64.whl
Size 3.9 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
cdd22fde2f1898b63a0c51752fc57e18bb7f208903396540801d795f4d0f0a61
BLAKE2b-256 checksum
How to use checksums
ba7aa50a73842d7985cd0b8d8158fa7ccb8a5013f9090be44206431ff3761817
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / lifeguard_lazy_imports-0.6.0-py3-none-win_amd64.whl

Download URL lifeguard_lazy_imports-0.6.0-py3-none-win_amd64.whl
Size 4.0 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
e647b2f9429d5c0ba5b9b8c216b3635ec1e135274234d2fa5863b1f932bd7101
BLAKE2b-256 checksum
How to use checksums
b21471281b8cfbad507a9f672998a734e6889e91ad932815a05d6638191f3694
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / lifeguard_lazy_imports-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL lifeguard_lazy_imports-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 4.6 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
9f9fda0643d76f52e359dffb29f77cefb184619e73e3c903db75eeb6efc13c6e
BLAKE2b-256 checksum
How to use checksums
30e132f2e22875205488744a003d61b58cd79aaea1cfcff2a10e1da2a634a17c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / lifeguard_lazy_imports-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL lifeguard_lazy_imports-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.4 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
b1d1ef376022ed77832ed8bce544a9fa015e6de54cf00a796ad7ad63d22d6b98
BLAKE2b-256 checksum
How to use checksums
a4d715a55ced119a3496204494dd15eceb591342e314eb0970880cded685a266
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / lifeguard_lazy_imports-0.6.0-py3-none-macosx_11_0_arm64.whl

Download URL lifeguard_lazy_imports-0.6.0-py3-none-macosx_11_0_arm64.whl
Size 4.2 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
fffb37f3538fdb404ab8ac854f6221e6cd4cc3171edd41ecccaf3fd5c0553a58
BLAKE2b-256 checksum
How to use checksums
89f304bb3eee21d537124b4c4b9108edb13d1601620b53baf57e19edb733d0c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / lifeguard_lazy_imports-0.6.0-py3-none-macosx_10_12_x86_64.whl

Download URL lifeguard_lazy_imports-0.6.0-py3-none-macosx_10_12_x86_64.whl
Size 4.3 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
42d2aa51f7730809345b7ef58b43ea865535bbb34eb97851d61c533a9d0ea209
BLAKE2b-256 checksum
How to use checksums
b1860f84c3ef7dfdfd9af9707d2eefbf3a4257071c7134065b70a5ee688e6f06
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for lifeguard-lazy-imports 0.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 7 files in this release have been uploaded with provenance metadata.

Select from 7 files

Attestation for lifeguard_lazy_imports-0.6.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page