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). See the post here for more on the development of this tool!
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.
Quick run
There's 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>
Try --format=lazy-modules to get copy-paste lines or even --apply=list to
have the tool update your lazy modules automatically! For maximum laziness, try
--apply=dynamic.
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.
See the full documentation for details, examples, and the standalone CLI runner.
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!
Authoring __lazy_modules__
Use a static, sorted list of strings:
__lazy_modules__ = [
"argparse",
"numpy",
"pathlib",
]
You can also use sets; sets are slower to construct, but faster to check - around 2 items is enough to make sets faster.
Dynamic values (i.e. a custom object assigned to __lazy_modules__) are also
supported. If flake8-lazy detects a non-static assignment it treats the file as
fully covered and suppresses all LZY1xx/LZY2xx diagnostics. Use
--apply=dynamic to have the tool write a simple catch-all object:
class AllLazy:
@staticmethod
def __contains__(_: str) -> bool:
return True
__lazy_modules__ = AllLazy()
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. - Handles static
sys.version_infochecks - 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.
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:
$ flake8-lazy path/to/file.py
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"]
To rewrite files in place with the recommended declaration, use --apply:
flake8-lazy --apply=list path/to/file.py another_file.py
Available modes: list, set, native (3.15+ syntax), dynamic. The command
exits with status code 1 if any error is found.
Local development
See CONTRIBUTING.md for local development instructions.
FAQ
Why is this not in Ruff?
It's really new, and it's a bit complex. Maybe someday it will be? :)
It's missing something!
Open an issue! If it's clear and detailed and in-scope, I might even be able to assign it to copilot!
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
Release history Release notifications | RSS feed
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.7.0.tar.gz.
File metadata
- Download URL: flake8_lazy-0.7.0.tar.gz
- Upload date:
- Size: 36.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aefbcc2b8583dc4614ee1f3dc339fe90542d55ff639a03a83be5701995f43179
|
|
| MD5 |
de02ebe7df8012c0d03d23c2c0593147
|
|
| BLAKE2b-256 |
b76bc149c5cd5937d01c4cb1a1674550ceb33bbc4fc9632ca6b320ad245979c5
|
Provenance
The following attestation bundles were made for flake8_lazy-0.7.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.7.0.tar.gz -
Subject digest:
aefbcc2b8583dc4614ee1f3dc339fe90542d55ff639a03a83be5701995f43179 - Sigstore transparency entry: 1649243229
- Sigstore integration time:
-
Permalink:
henryiii/flake8-lazy@fb7509bb707283dd8f845b6cac321d2ba9d5b709 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/henryiii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@fb7509bb707283dd8f845b6cac321d2ba9d5b709 -
Trigger Event:
release
-
Statement type:
File details
Details for the file flake8_lazy-0.7.0-py3-none-any.whl.
File metadata
- Download URL: flake8_lazy-0.7.0-py3-none-any.whl
- Upload date:
- Size: 28.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3a187104c8cb4fb395a0b464dd30ba3d926945f42cc43f789da8608cb850307
|
|
| MD5 |
5ccf63b08086b1e999cc02cfe58d81d5
|
|
| BLAKE2b-256 |
91efc742a37bf62f27885e7863750a8565cbb886a50e15f9f092b72b9f5b0e5b
|
Provenance
The following attestation bundles were made for flake8_lazy-0.7.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.7.0-py3-none-any.whl -
Subject digest:
e3a187104c8cb4fb395a0b464dd30ba3d926945f42cc43f789da8608cb850307 - Sigstore transparency entry: 1649243318
- Sigstore integration time:
-
Permalink:
henryiii/flake8-lazy@fb7509bb707283dd8f845b6cac321d2ba9d5b709 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/henryiii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@fb7509bb707283dd8f845b6cac321d2ba9d5b709 -
Trigger Event:
release
-
Statement type: