Skip to content

fix Incorrect invalid-type-var check when persisting get method of a fully-annotated dict #2812#2816

Open
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2812
Open

fix Incorrect invalid-type-var check when persisting get method of a fully-annotated dict #2812#2816
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2812

Conversation

@asukaminato0721
Copy link
Contributor

Summary

Fixes #2812

The cause was: invalid-type-var sanitization already exempted generic Forall methods, but it was not exempting generic overload signatures carried inside Overload and bound-method overloads. I added that handling there, so storing dict[str, str].get on an instance attribute no longer falsely reports leaked _T.

Test Plan

add test

@meta-cla meta-cla bot added the cla signed label Mar 17, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as ready for review March 17, 2026 07:37
Copilot AI review requested due to automatic review settings March 17, 2026 07:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a false-positive invalid-type-var error when persisting a bound overload method (e.g., dict[str, str].get) onto an instance attribute, by ensuring type-variable sanitization treats overload-carried Forall type parameters as locally bound.

Changes:

  • Extend invalid-type-var sanitization to collect type parameters bound by Forall overload signatures (both Type::Overload and BoundMethodType::Overload).
  • Add a regression test covering assignment of dict[str, str].get to an instance attribute and verifying the resulting callable type.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
pyrefly/lib/alt/class/class_field.rs Updates type-parameter collection to account for Forall tparams inside overload signatures during attribute type sanitization.
pyrefly/lib/test/attributes.rs Adds regression test for assigning a bound overload (dict.get) to an attribute and asserting its inferred call types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@github-actions
Copy link

Diff from mypy_primer, showing the effect of this PR on open source code:

beartype (https://github.com/beartype/beartype)
- ERROR beartype/_util/cache/map/utilmapunbounded.py:113:14-31: Attribute `_key_to_value_get` cannot depend on type variable `_T`, which is not in the scope of class `CacheUnboundedStrong` [invalid-type-var]

scikit-learn (https://github.com/scikit-learn/scikit-learn)
- ERROR sklearn/cluster/_agglomerative.py:1309:14-26: Attribute `pooling_func` cannot depend on type variable `_ArrayT`, which is not in the scope of class `FeatureAgglomeration` [invalid-type-var]
- ERROR sklearn/cluster/_agglomerative.py:1309:14-26: Attribute `pooling_func` cannot depend on type variable `_ScalarT`, which is not in the scope of class `FeatureAgglomeration` [invalid-type-var]

setuptools (https://github.com/pypa/setuptools)
- ERROR setuptools/command/editable_wheel.py:454:14-19: Attribute `_file` cannot depend on type variable `_StrPathT`, which is not in the scope of class `_LinkTree` [invalid-type-var]
- ERROR setuptools/command/editable_wheel.py:454:14-19: Attribute `_file` cannot depend on type variable `_BytesPathT`, which is not in the scope of class `_LinkTree` [invalid-type-var]

pip (https://github.com/pypa/pip)
- ERROR src/pip/_vendor/rich/theme.py:89:14-17: Attribute `get` cannot depend on type variable `_T`, which is not in the scope of class `ThemeStack` [invalid-type-var]

zope.interface (https://github.com/zopefoundation/zope.interface)
- ERROR src/zope/interface/tests/test_adapter.py:95:14-17: Attribute `get` cannot depend on type variable `_T`, which is not in the scope of class `CustomMapping` [invalid-type-var]

rich (https://github.com/Textualize/rich)
- ERROR rich/theme.py:89:14-17: Attribute `get` cannot depend on type variable `_T`, which is not in the scope of class `ThemeStack` [invalid-type-var]

sphinx (https://github.com/sphinx-doc/sphinx)
- ERROR sphinx/domains/__init__.py:134:14-31: Attribute `objtypes_for_role` cannot depend on type variable `_T`, which is not in the scope of class `Domain` [invalid-type-var]
- ERROR sphinx/domains/__init__.py:135:14-30: Attribute `role_for_objtype` cannot depend on type variable `_T`, which is not in the scope of class `Domain` [invalid-type-var]

jinja (https://github.com/pallets/jinja)
- ERROR src/jinja2/environment.py:352:14-21: Attribute `filters` cannot depend on type variable `V`, which is not in the scope of class `Environment` [invalid-type-var]

@github-actions
Copy link

Primer Diff Classification

✅ 8 improvement(s) | 8 project(s) total | -9 errors

8 improvement(s) across beartype, scikit-learn, setuptools, pip, zope.interface, rich, sphinx, jinja.

Project Verdict Changes Error Kinds Root Cause
beartype ✅ Improvement -1 invalid-type-var collect_forall_tparams()
scikit-learn ✅ Improvement -1 invalid-type-var collect_forall_tparams()
setuptools ✅ Improvement -1 invalid-type-var pyrefly/lib/alt/class/class_field.rs
pip ✅ Improvement -1 invalid-type-var collect_forall_tparams()
zope.interface ✅ Improvement -1 invalid-type-var collect_overload_tparams()
rich ✅ Improvement -1 invalid-type-var collect_overload_tparams()
sphinx ✅ Improvement -2 invalid-type-var pyrefly/lib/alt/class/class_field.rs
jinja ✅ Improvement -1 invalid-type-var pyrefly/lib/alt/class/class_field.rs
Detailed analysis

✅ Improvement (8)

beartype (-1)

This is an improvement. The removed error was a false positive - storing dict[Hashable, object].get as self._key_to_value_get is valid Python code. The method's generic type parameter _T (used in dict.get's overloaded signatures) is scoped to the method itself, not the containing class. The PR correctly exempts method-level type parameters from triggering invalid-type-var errors when the method is stored as an attribute.
Attribution: The changes to collect_forall_tparams() in pyrefly/lib/alt/class/class_field.rs now properly handle type parameters in overloaded methods and bound methods. The fix adds handling for Type::Overload and BoundMethodType::Overload cases, exempting their type parameters from the invalid-type-var check.

scikit-learn (-1)

This is an improvement. The removed error was a false positive - pyrefly was incorrectly flagging valid code where a generic function (np.mean) is assigned to an instance attribute. Generic functions have their own type variable scope independent of the class they're assigned to. The PR correctly fixes this by exempting type variables from generic functions/overloads when checking for invalid type variable usage in class attributes.
Attribution: The changes to collect_forall_tparams() in pyrefly/lib/alt/class/class_field.rs now properly handle overloaded and bound methods by collecting their type parameters into the forall_bound set, preventing them from being flagged as invalid.

setuptools (-1)

This is an improvement. The removed error was a false positive where pyrefly incorrectly claimed that _file (which stores dict.get) cannot depend on type variable _StrPathT. However, looking at the code, _StrPathT is defined at module level (line 49) as a TypeVar, and _file is storing a bound method dict.get which has its own internal type parameter. The error message seems confused - it's likely referring to dict.get's internal _T parameter, not _StrPathT. The PR correctly fixed this by exempting overloaded bound methods from the invalid-type-var check, recognizing that their type parameters are self-contained and don't 'leak' into the class scope.
Attribution: The PR diff shows changes to pyrefly/lib/alt/class/class_field.rs in the collect_forall_tparams function. It added handling for Type::Overload and BoundMethodType::Overload cases, which were previously not exempted from the invalid-type-var check. This explains why storing dict.get (an overloaded bound method) on an attribute no longer triggers the error.

pip (-1)

This is an improvement. The error was a false positive - when storing a bound method like dict.get as an instance attribute, the method's type variables (like _T in the overloaded signatures) are scoped to the method itself, not to the class storing the reference. The PR correctly fixes pyrefly to handle this pattern.
Attribution: The changes to collect_forall_tparams() in pyrefly/lib/alt/class/class_field.rs now properly collect type parameters from overloaded bound methods via the new collect_overload_tparams() helper, exempting them from invalid-type-var checks.

zope.interface (-1)

Pyrefly was incorrectly reporting invalid-type-var when storing dict.get (a generic overloaded method) as a class attribute. The method's type variable _T is scoped to the method itself, not the class, making this a valid pattern. The PR correctly fixed this by exempting overloaded bound methods from the invalid-type-var sanitization check.
Attribution: The changes to collect_overload_tparams() and handling of BoundMethodType::Overload in pyrefly/lib/alt/class/class_field.rs fixed the false positive by properly exempting overloaded bound methods from the invalid-type-var check

rich (-1)

This is an improvement. The removed error was a false positive - pyrefly was incorrectly claiming that storing dict.get (a bound method with its own generic type parameter _T) as an instance attribute leaked that type variable. The type variable _T is scoped to the get method itself, not to the class storing the bound method. This is a valid and common Python pattern where a method is stored as an attribute for convenience. The fix correctly exempts bound method type parameters from the invalid-type-var check.
Attribution: The changes to collect_overload_tparams() and collect_forall_tparams() in pyrefly/lib/alt/class/class_field.rs now properly handle type parameters in overloaded bound methods, preventing the false positive.

sphinx (-2)

These were false positives. Pyrefly was incorrectly reporting that storing dict.get (a bound method with a generic default parameter) as an instance attribute violated type variable scoping rules. The type variable _T is properly scoped to the get method itself, not escaping into the class. The PR correctly fixed this by exempting type parameters in overloaded bound methods from the invalid-type-var check.
Attribution: The changes in pyrefly/lib/alt/class/class_field.rs added handling for type parameters in overloaded methods and bound method overloads in the collect_forall_tparams function, specifically recognizing that these type parameters should not trigger invalid-type-var errors.

jinja (-1)

This is an improvement. Pyrefly was incorrectly flagging a valid pattern where a dict's copy is assigned to an instance attribute. The error claimed type variable V was leaking into the class scope, but this was a false positive - likely related to type variables in dict's overloaded methods like get(). The PR correctly fixed this by exempting overloaded bound methods from invalid-type-var checks. Neither mypy nor pyright flag this pattern, confirming it was a pyrefly bug.
Attribution: The changes to pyrefly/lib/alt/class/class_field.rs in the collect_forall_tparams function added handling for Type::Overload and BoundMethodType::Overload cases, which fixed the false positive by properly recognizing that type parameters in overloaded bound methods should not trigger invalid-type-var errors.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (8 LLM)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect invalid-type-var check when persisting get method of a fully-annotated dict

2 participants