Skip to content

Fix __new__ forward reference class aliasing - #583

Open
jaideeppyne wants to merge 1 commit into
agronholm:masterfrom
jaideeppyne:fix/new-forwardref-class-alias
Open

Fix __new__ forward reference class aliasing#583
jaideeppyne wants to merge 1 commit into
agronholm:masterfrom
jaideeppyne:fix/new-forwardref-class-alias

Conversation

@jaideeppyne

Copy link
Copy Markdown
Contributor

Fixes #578.

When instrumentation sees a string self-reference in __new__, it currently rewrites the annotation to the class name and injects ClassName = cls into the method body. That avoids the class-creation-time NameError from #398, but it leaves a runtime-visible local alias. Later subclass construction then makes ClassName resolve to the subclass inside the base __new__ body.

This changes exact string self-references in __new__ to ForwardRef("ClassName") instead of injecting a local class-name alias. Forward reference evaluation still falls back to memo.self_type when the referenced name is not yet resolvable and it matches the class currently being created, preserving the #398 behavior without shadowing user code after class creation.

Added a regression that constructs a subclass under both @typechecked and import-hook instrumentation and asserts the base class name still resolves to the base class inside Base.__new__.

Validation run locally:

uv run --group test pytest -q
uv run ruff format --check src/typeguard/_transformer.py src/typeguard/_utils.py tests/test_transformer.py tests/dummymodule.py tests/test_instrumentation.py

uv run --group test mypy src is blocked in this local environment by an unrelated missing typeshed.stdlib.types import from src/typeguard/_decorators.py.

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 94.831% (-0.02%) from 94.853% — jaideeppyne:fix/new-forwardref-class-alias into agronholm:master

@agronholm

Copy link
Copy Markdown
Owner

Is this substantially different from #581?

@jaideeppyne

jaideeppyne commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

No, not substantially: both address #578 and remove the injected ClassName = cls binding. This PR uses a ForwardRef for an exact quoted self-reference in __new__ and a memo.self_type fallback in the shared forward-reference evaluator; #581 instead uses the defining class's __class__ closure and supplies the owner during class decoration.

I missed #581 when opening this. Having compared the patches, #581 also covers subclass construction, argument checking without an existing class cell, and Enum construction, without changing the shared forward-reference evaluator. I don't see a separate use case here that warrants reviewing both implementations, and I defer to #581.

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.

Instrumented __new__ binds the class's own name to cls, so it means the subclass

3 participants