Skip to content

chore: lazy imports#1021

Merged
gaborbernat merged 6 commits intopypa:mainfrom
henryiii:henryiii/chore/import
Apr 10, 2026
Merged

chore: lazy imports#1021
gaborbernat merged 6 commits intopypa:mainfrom
henryiii:henryiii/chore/import

Conversation

@henryiii
Copy link
Copy Markdown
Contributor

@henryiii henryiii commented Apr 10, 2026

  • chore: use TYPE_CHECKING=False trick
  • chore: enforce TYPE_CHECKING=False trick
  • chore: add lazy imports

Description

This adds lazy imports.

$ hyperfine --warmup 10 \
    -n FullLazy ".venv/bin/python -X lazy_imports=all    -m build -h" \
    --reference ".venv/bin/python -X lazy_imports=normal -m build -h" \
    -n Eager    ".venv/bin/python -X lazy_imports=none   -m build -h"
...
  .venv/bin/python -X lazy_imports=normal -m build -h ran
    1.31 ± 0.12 times slower than FullLazy
    1.10 ± 0.11 times faster than Eager

I used uvx flake8-lazy —-apply src/**/*.py to get the lazy lists.

Changelog

  • Added changelog fragment: docs/changelog/<pr_number>.<type>.rst
    • Types: feature, bugfix, doc, removal, misc
    • Example: 123.feature.rst containing Add custom backend support - by :user:`yourname`

Checklist

  • Tests pass locally (tox)
  • Code follows project style (tox -e fix)
  • Type checks pass (tox -e type)
  • Documentation builds (tox -e docs)

henryiii added 3 commits April 9, 2026 22:33
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Comment thread src/build/__main__.py
Comment on lines +46 to +48
from build import ProjectBuilder, _ctx
from build._exceptions import BuildBackendException, BuildException, FailedProcessError
from build.env import DefaultIsolatedEnv
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why were these imports made absolute?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because one of them already was, and __main__.py modues have __spec__s that can be None so mypy complains.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mention this under "tips" at https://iscinumpy.dev/post/flake8-lazy/.

Comment thread src/build/__main__.py Outdated
from ._exceptions import BuildBackendException, BuildException, FailedProcessError
from ._types import ConfigSettings, Distribution, StrPath, SubprocessRunner
from .env import DefaultIsolatedEnv
from ._types import ConfigSettings, Distribution, StrPath, SubprocessRunner
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual types get imported in __init__ AFAICT so there's nothing to be gained from lazifying these.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rules for the imports are in flake-lazy. Basically anything that is unused at top level is lazy.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant tucking it under an if type checking block, sorry for being unclear.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the TC code, that’s supposed to catch everything that can be moved to type checking blocks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so this is for any type checking only imports we might add in the future to go under? Currently, we don’t have any that’d benefit from this.

@layday
Copy link
Copy Markdown
Member

layday commented Apr 10, 2026

Visually, this is just awful. Can maintenance be automated? Is ruff able to detect extraneous entries?

What if we just flipped on lazy imports globally in __main__?

@henryiii
Copy link
Copy Markdown
Contributor Author

Yes, I wrote a tool for maintenance, it’s flake8-lazy. Being intelligent with this is tricky, and there are a few edge cases, such as the from an import b syntax being ambiguous (I assume B is not a module). I would hope it does make its way to ruff in some form.

There is a way to make all imports lazy by adding three lines, but it ideally needs to be in every file.

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
@gaborbernat gaborbernat merged commit f8d54fe into pypa:main Apr 10, 2026
65 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants