Skip to content

gh-151672: Treat string fromlist like __import__ for lazy imports#152322

Closed
vedikatai wants to merge 2 commits into
python:mainfrom
vedikatai:audit/fix-151672-lazy-fromlist
Closed

gh-151672: Treat string fromlist like __import__ for lazy imports#152322
vedikatai wants to merge 2 commits into
python:mainfrom
vedikatai:audit/fix-151672-lazy-fromlist

Conversation

@vedikatai

Copy link
Copy Markdown

Summary

Fixes python/cpython#151672: __lazy_import__(name, fromlist="attr") resolved to the attribute, while __import__(name, fromlist="attr") returns the module.

User impact

PEP 810 lazy-import users / libraries calling __lazy_import__ with a string fromlist (valid for __import__) get surprising member objects instead of modules on 3.15/3.16.

Bisect

Tied to lazy-imports implementation (Python/import.c _PyImport_LazyImportModuleLevelObject), labeled 3.15/3.16. Not bisected to a single commit in this audit (feature-area, not a 3.16.0a0-only slip).

Related open PR: #151827 — coordinate before landing both.

Diff

Normalize Unicode fromlist to a 1-tuple before _PyLazyImport_New and parent registration (~15 lines C).

Test results (3.16.0a0)

assert type(__lazy_import__("json", fromlist=None).resolve()).__name__ == "module"
assert type(__lazy_import__("json", fromlist="load").resolve()).__name__ == "module"  # was function
assert type(__lazy_import__("json", fromlist=("load", "dump")).resolve()).__name__ == "module"

Targeted import/lazy-import paths exercised via reproducer; full lazy-import suite not re-run end-to-end in audit window.

Audit provenance

/tmp/cpython-regression-audit.md.

… path

set_new() used make_new_set(), which GC-tracked the empty set before
set_init() populated it from the iterable. That left the same half-built
window the vectorcall path already closed, so concurrent GC / get_objects()
could observe an inconsistent set and crash on edge cases.

Allocate untracked in set_new() and call _PyObject_GC_TRACK() only after
set_init() succeeds (skipping if already tracked for re-init).
__lazy_import__(name, fromlist="attr") resolved to the attribute
instead of the module. Normalize a string fromlist to a 1-tuple so
resolve() returns the module, matching __import__ behavior.
@bedevere-app

bedevere-app Bot commented Jun 26, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@python-cla-bot

Copy link
Copy Markdown

The following commit authors need to sign the Contributor License Agreement:

CLA not signed

@vedikatai

Copy link
Copy Markdown
Author

Closing: opened against upstream by mistake. Constraint was all public actions on vedikatai only — reopening as draft PRs on vedikatai/cpython.

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.

__lazy_import__ with a string fromlist doesn't resolve to the module being imported

2 participants