Preserve the class type through proxy_for_class (#418) - #502
Merged
pschanely merged 1 commit intoAug 12, 2026
Conversation
proxy_for_class was annotated as returning `object`, so callers lost every attribute of the class they asked for and had to reach for a cast or a `# type: ignore` to touch the result. Bind the argument and the return with the existing _T type variable. The main path already returns an instance of the requested class (WithEnforcement(cls)(...)), so the annotation describes what the function was doing all along. The TypedDict branch returns a plain dict, which is what a TypedDict instance is at runtime; mypy cannot connect that to _T on its own, so it takes a cast with a comment rather than widening the signature back out. Three `# type: ignore[attr-defined]` comments in core_test.py -- exactly the workaround the issue reports -- are no longer needed and are removed, which is what pins the improvement: without the signature change mypy fails on those three lines.
Owner
|
Lovely. Thank you! |
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.
Fixes #418.
proxy_for_classwas annotated(typ: Type, varname: str) -> object, so callers got back something with no attributes and had to cast or silence mypy to use it.The change
reusing the
_Talready defined incore.py. This is a pure annotation change — no runtime behavior differs.The annotation matches what the function already does: the main path returns
WithEnforcement(cls)(*args.args, **args.kwargs), an instance of the requested class. The parameterized-generic case works out too, since mypy typesContainer[int]in expression position astype[Container[int]], so_Tbinds toContainer[int]and the result keeps its parameter.The one place needing help is the TypedDict branch, which returns a dict comprehension. A TypedDict instance is a dict at runtime, so the value genuinely satisfies the declared return type, but mypy can't connect that through
_T. I used acastwith a comment there rather than widening the signature back out — the alternative would give up the improvement for every other class to accommodate one branch.What pins it
core_test.pycarried three# type: ignore[attr-defined]comments — the exact workaround the issue describes:They are removed. Since mypy runs over all Python files via the pre-commit hook, those lines now act as the regression check for this signature — verified by reverting
core.pyand leaving the tests as-is:With the change,
mypy --ignore-missing-imports --scripts-are-modules crosshair/core.py crosshair/core_test.pyreportsSuccess: no issues found.Testing
crosshair/core_test.py: 95 passed, 3 skipped.black --checkandisort --check-onlyclean on both files.flake8reports the same three pre-existingF824warnings incore.pybefore and after; nothing new.