Detect imports that can be lazy
Project description
flake8-lazy
flake8-lazy is a flake8 plugin that finds imports which can be made lazy in Python 3.15 (following PEP 810).
flake8-lazy helps keep import-time overhead low by detecting imports that can be
declared as lazy in __lazy_modules__. For this package itself,
flake8-lazy --help runs roughly twice as fast when using Python 3.15's new
lazy import system.
Error messages will mention __lazy_modules__ since that is backward compatible
with older Python versions, but the lazy keyword is supported too.
Install
python -m pip install flake8-lazy
Usually you would include this in some sort of dependency-group in your project,
e.g. dev or lint.
flake8 will automatically discover the plugin. There's also a standalone
flake8-lazy runner. If you use uv or pipx, you can run it from anywhere
without installation:
uvx flake8-lazy <filenames>
# OR
pipx run flake8-lazy <filenames>
Rule codes
1xx: Missing lazy declarations
LZY101: Missing lazy stdlib module in__lazy_modules__LZY102: Missing lazy third-party or local module in__lazy_modules__
2xx: __lazy_modules__ validation
LZY201:__lazy_modules__list is not sortedLZY202: Module listed in__lazy_modules__is never importedLZY203: Module listed in__lazy_modules__appears more than onceLZY204:__lazy_modules__is assigned after importing modules it namesLZY205: Module name in__lazy_modules__is relative (.name) instead of absolute
3xx: Native lazy keyword (Python 3.15+)
LZY301: Lazy import insidesuppress(ImportError)is misleadingLZY302: Module is declared lazy by bothlazykeyword and__lazy_modules__LZY303: Module is imported both eagerly and lazily
4xx: Lazy import safety and semantics
LZY401: Module is declared lazy but accessed at the top levelLZY402: Module is an enclosing package for this file and should not be lazy
Basic example
__lazy_modules__ = ["argparse", "requests"]
import argparse
import requests
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("url")
args = parser.parse_args()
response = requests.get(args.url, timeout=5)
print(response.status_code)
In this example, requests is only used inside main, so it can be lazy. The
checker expects it in __lazy_modules__ and emits LZY102 until you add it.
Running --help will not import requests, resulting in a more responsive app!
How detection works
flake8-lazy inspects module-scope imports and module runtime usage.
- Counts top-level
importandfrom ... import ...statements. - Currently treats annotation-only usage as lazy-capable
(
from __future__ import annotationsif not using 3.14+). - Treats usage inside
if typing.TYPE_CHECKING:as type-only. - Skips
from __future__ import .... - Requires exact module entries for nested imports.
- Treats enclosing package names as non-lazy for a file. For example, in
a/b/c.py,aanda.bshould not be listed as lazy.
Nested import note:
import email.header
__lazy_modules__ = ["email"] # Not enough
This emits LZY101; the required entry is "email.header". PEP 810 requires
full module names.
Missing relative imports use f"{__spec__.parent}.name".
Enclosing package note:
# file: a/b/c.py
__lazy_modules__ = ["a", "a.b", "requests"]
# Python 3.15+ also applies
# lazy import a
# lazy import a.b
This emits LZY402 for a and a.b. Those are enclosing packages for the
current file, so declaring them lazy is unnecessary and can be removed.
CLI
The project also provides a direct CLI runner:
flake8-lazy path/to/file.py another_file.py
# or
uvx flake8-lazy path/to/file.py another_file.py
The default output format matches flake8-style diagnostics:
path/to/file.py:12:0: LZY102 module 'numpy' should be listed in __lazy_modules__
You can also ask for a copy-pasteable recommendation instead:
flake8-lazy --format lazy-modules path/to/file.py
path/to/file.py: __lazy_modules__ = ["numpy", "pandas"]
This prints the sorted __lazy_modules__ value the checker recommends for each
file when it differs from the file's current static __lazy_modules__
declaration. The command still exits with status code 1 if the file has any
diagnostics.
To rewrite files in place with the recommended declaration, use --apply:
flake8-lazy --apply path/to/file.py another_file.py
--apply replaces an existing top-level __lazy_modules__ assignment when
present. If there is no assignment yet, one is inserted near the top of the file
after leading comments/docstrings (and after from __future__ import ... lines,
to keep valid Python syntax).
The command exits with status code 1 if any error is found.
Authoring __lazy_modules__
Use a static, sorted list of strings:
__lazy_modules__ = [
"argparse",
"numpy",
"pathlib",
]
Dynamic values are intentionally ignored for now.
Local development
Run tests:
nox -s tests
# or
uv run pytest
Run linting:
nox -s lint
# or
prek -a
Build docs:
nox -s docs --non-interactive
Serve docs locally:
nox -s docs
Bump the version:
uv version <new_version>
Acknowledgements
GitHub Copilot in VS Code was used to help develop this package. The Scientific Python Development Guide template was used as a starting point.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 flake8_lazy-0.4.0.tar.gz.
File metadata
- Download URL: flake8_lazy-0.4.0.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26f59a35be7fea1bd34d1c91c8f7eaa9bf05922946419476b4973901a918d940
|
|
| MD5 |
737091f7f44591b526e6c19fa323f4ae
|
|
| BLAKE2b-256 |
0fb6ac0f9bba1087f3c541e104358b1b09df77dc27637b8744df0e91eb31055b
|
Provenance
The following attestation bundles were made for flake8_lazy-0.4.0.tar.gz:
Publisher:
cd.yml on henryiii/flake8-lazy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flake8_lazy-0.4.0.tar.gz -
Subject digest:
26f59a35be7fea1bd34d1c91c8f7eaa9bf05922946419476b4973901a918d940 - Sigstore transparency entry: 1173923435
- Sigstore integration time:
-
Permalink:
henryiii/flake8-lazy@81fd8af33d6c5bbe8758d63d58fc4ae021d5e846 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/henryiii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@81fd8af33d6c5bbe8758d63d58fc4ae021d5e846 -
Trigger Event:
release
-
Statement type:
File details
Details for the file flake8_lazy-0.4.0-py3-none-any.whl.
File metadata
- Download URL: flake8_lazy-0.4.0-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
529cea0642794d4e086fbb151d1d7cee41b4f77d99e9bb61e44f4c8041d3cf03
|
|
| MD5 |
af9221bdac6c7b304c362e942f5e21b0
|
|
| BLAKE2b-256 |
1bb54d2e077caa04ae4e601453395aca6a3e9abdbb183e6ae60692faaf07def8
|
Provenance
The following attestation bundles were made for flake8_lazy-0.4.0-py3-none-any.whl:
Publisher:
cd.yml on henryiii/flake8-lazy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flake8_lazy-0.4.0-py3-none-any.whl -
Subject digest:
529cea0642794d4e086fbb151d1d7cee41b4f77d99e9bb61e44f4c8041d3cf03 - Sigstore transparency entry: 1173923546
- Sigstore integration time:
-
Permalink:
henryiii/flake8-lazy@81fd8af33d6c5bbe8758d63d58fc4ae021d5e846 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/henryiii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@81fd8af33d6c5bbe8758d63d58fc4ae021d5e846 -
Trigger Event:
release
-
Statement type: