Repository navigation
Conversation
Python 3.15 honors __lazy_modules__ (PEP 810), so declaring it defers contextlib, email, and two of the package's own modules; earlier Pythons ignore the declaration. platform and compat.py312 move into the functions that read them, which helps on every Python. Cuts import time ~30% on 3.15 and 3-8% on 3.11 through 3.14. Closes #554 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Member
Author
|
Closing: the scope was the wrong way round. The aim is not to cut import time but to restore these imports to module scope for readability, using lazy imports to hold performance roughly where it is. Reworking against that in a fresh PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #554.
Declares
__lazy_modules__(PEP 810) and moves two imports into the functions that read them.Approach
PEP 810 landed in 3.15 as a module global, not as syntax: a module that declares
__lazy_modules__gets lazy semantics for plainimportstatements. Being just a global, it is inert on 3.11–3.14 and needs no guard — so this costs nothing in the stdlib copy and nothing at sync time.The declaration lists only what is observably deferred, so the test can assert all of it:
contextlib,email,._collections,.compat— read only inside functions.collectionsandcollections.abcqualify by the same rule but are left off, becausetypingimports them eagerly throughTypeVar; declaring them would promise a deferral that does not happen. The declaration's docstring says so, since that is the trap for the next person adding to the list.platform(read only by_compat.pypy_partial) andcompat.py312(read only by two_compat.localizefunctions) need no lazy import at all, just a function-local one in the idiom this codebase already uses forimportlib.metadata,_adapters, andinspect. That part helps on every supported Python.Not done here
re,textwrap, andpathlibare the expensive ones, and each is held eager by a single module-level statement —EntryPoint.pattern = re.compile(...),Sectioned._sample = textwrap.dedent(...), andclass PackagePath(pathlib.PurePosixPath). Deferring the first two is worth roughly another 14%, butEntryPoint.patternis a documented class attribute, so it needs an API decision (and CPython's agreement); #554 has the detail.PackagePath's base class makespathlibpermanently eager.Why not the backport
@pganssle's
lazy-importergives these semantics on 3.11+, and I measured it before settling on the native-only approach: importinglazy_importercosts ~3.7 ms on 3.11, more than the ~3.1 ms available to save, so it made import slower on every Python it applied to (+11% in the deferrable-today configuration). It would also re-indent every lazy import into awithblock, which each sync would have to undo. #554 has the numbers. The declaration is in place if a cheaper backport appears.Verification
tox -e py,py311,py312,py313,py314— 262 passed on each, mypy clean, coverage 99% with no new gaps;tox -e docsclean. The project's ownpytest-perfexercise, per env:The new test asserts that nothing in
__lazy_modules__is insys.modulesafter a freshimport importlib_metadata, comparing against a snapshot taken before the import so a localsitecustomizecannot mask a regression. It skips below 3.15, where the declaration is inert.🤖 Generated with Claude Code