[codex] Preserve specialized TypeVar bounds#3291
[codex] Preserve specialized TypeVar bounds#3291cneuralnetwork wants to merge 1 commit intofacebook:mainfrom
Conversation
Bounded TypeVars that appear as class type parameters can carry specialized generic bounds such as P[Any]. The recursive targs truncation was dropping those arguments whenever the bound class had restricted type parameters, which made protocol classmethod lookup leak the bound class's own TypeVar instead of the specialized Any argument. Make truncation detect an actual recursive path back to the same class before erasing class arguments. Add a protocol regression test covering facebook#3285.
|
Diff from mypy_primer, showing the effect of this PR on open source code: antidote (https://github.com/Finistere/antidote)
- ERROR src/antidote/lib/interface_ext/qualifier.py:79:23-55: `QualifiedBy` is not assignable to upper bound `Predicate` of type variable `InPredicate` [bad-specialization]
- ERROR src/antidote/lib/interface_ext/qualifier.py:85:45-77: `QualifiedBy` is not assignable to upper bound `Predicate` of type variable `InPredicate` [bad-specialization]
rotki (https://github.com/rotki/rotki)
- ERROR rotkehlchen/chain/decoding/decoder.py:216:30-80: Argument `T_TxNotDecodedFilterQuery` is not assignable to parameter `filter_query` with type `T_TxNotDecodedFilterQuery` in function `rotkehlchen.db.dbtx.DBCommonTx.get_transaction_hashes_not_decoded` [bad-argument-type]
steam.py (https://github.com/Gobot1234/steam.py)
- ERROR steam/chat.py:418:38-99: `ClanMember | GroupMember | PartialMember` is not assignable to attribute `author` with type `ClanMessageAuthorT` [bad-assignment]
- ERROR steam/chat.py:418:38-99: `ClanMember | GroupMember | PartialMember` is not assignable to attribute `author` with type `GroupMessageAuthorT` [bad-assignment]
static-frame (https://github.com/static-frame/static-frame)
+ ERROR static_frame/core/frame.py:3808:16-3811:10: Returned type `InterfaceConsolidate[Self@Frame]` is not assignable to declared return type `InterfaceConsolidate[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3808:36-3811:10: `Self@Frame` is not assignable to upper bound `Batch | Bus | Frame | FrameAssignILoc | FrameGO | FrameHE | Index | IndexHierarchy | MaskedArray[Any, Any] | Series | SeriesAssign | SeriesHE | TypeBlocks | Yarn | ndarray[Any, Any]` of type variable `TVContainer_co` [bad-specialization]
+ ERROR static_frame/core/frame.py:3826:16-37: Returned type `InterfaceValues[Self@Frame]` is not assignable to declared return type `InterfaceValues[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3826:31-37: `Self@Frame` is not assignable to upper bound `Frame | Index | IndexHierarchy | Series` of type variable `TVContainer_co` [bad-specialization]
+ ERROR static_frame/core/frame.py:3881:16-3883:10: Returned type `InterfaceTranspose[Self@Frame]` is not assignable to declared return type `InterfaceTranspose[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3881:34-3883:10: `Self@Frame` is not assignable to upper bound `Frame | IndexHierarchy` of type variable `TVContainer_co` [bad-specialization]
+ ERROR static_frame/core/frame.py:3893:16-3896:10: Returned type `InterfaceFillValue[Self@Frame]` is not assignable to declared return type `InterfaceFillValue[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3893:34-3896:10: `Self@Frame` is not assignable to upper bound `Frame | Series` of type variable `TVContainer_co` [bad-specialization]
+ ERROR static_frame/core/frame.py:3934:16-3940:10: Returned type `IterNodeAxis[Self@Frame]` is not assignable to declared return type `IterNodeAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3934:28-3940:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:3947:16-3953:10: Returned type `IterNodeAxis[Self@Frame]` is not assignable to declared return type `IterNodeAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3947:28-3953:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:3960:16-3966:10: Returned type `IterNodeConstructorAxis[Self@Frame]` is not assignable to declared return type `IterNodeConstructorAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3960:39-3966:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:3973:16-3979:10: Returned type `IterNodeConstructorAxis[Self@Frame]` is not assignable to declared return type `IterNodeConstructorAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3973:39-3979:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:3986:16-3992:10: Returned type `IterNodeAxis[Self@Frame]` is not assignable to declared return type `IterNodeAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3986:28-3992:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:3999:16-4005:10: Returned type `IterNodeAxis[Self@Frame]` is not assignable to declared return type `IterNodeAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:3999:28-4005:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4013:16-4019:10: Returned type `IterNodeGroupAxis[Self@Frame]` is not assignable to declared return type `IterNodeGroupAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4013:33-4019:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4026:16-4032:10: Returned type `IterNodeGroupAxis[Self@Frame]` is not assignable to declared return type `IterNodeGroupAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4026:33-4032:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4040:16-4046:10: Returned type `IterNodeGroupAxis[Self@Frame]` is not assignable to declared return type `IterNodeGroupAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4040:33-4046:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4053:16-4059:10: Returned type `IterNodeGroupAxis[Self@Frame]` is not assignable to declared return type `IterNodeGroupAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4053:33-4059:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4067:16-4073:10: Returned type `IterNodeDepthLevelAxis[Self@Frame]` is not assignable to declared return type `IterNodeDepthLevelAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4067:38-4073:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4080:16-4086:10: Returned type `IterNodeDepthLevelAxis[Self@Frame]` is not assignable to declared return type `IterNodeDepthLevelAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4080:38-4086:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4094:16-4100:10: Returned type `IterNodeDepthLevelAxis[Self@Frame]` is not assignable to declared return type `IterNodeDepthLevelAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4094:38-4100:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4107:16-4113:10: Returned type `IterNodeDepthLevelAxis[Self@Frame]` is not assignable to declared return type `IterNodeDepthLevelAxis[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4107:38-4113:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4121:16-4127:10: Returned type `IterNodeGroupOtherReducible[Self@Frame]` is not assignable to declared return type `IterNodeGroupOtherReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4121:43-4127:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4134:16-4140:10: Returned type `IterNodeGroupOtherReducible[Self@Frame]` is not assignable to declared return type `IterNodeGroupOtherReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4134:43-4140:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4148:16-4154:10: Returned type `IterNodeGroupOtherReducible[Self@Frame]` is not assignable to declared return type `IterNodeGroupOtherReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4148:43-4154:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4161:16-4167:10: Returned type `IterNodeGroupOtherReducible[Self@Frame]` is not assignable to declared return type `IterNodeGroupOtherReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4161:43-4167:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4180:16-4186:10: Returned type `IterNodeWindowReducible[Self@Frame]` is not assignable to declared return type `IterNodeWindowReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4180:39-4186:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4198:16-4204:10: Returned type `IterNodeWindowReducible[Self@Frame]` is not assignable to declared return type `IterNodeWindowReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4198:39-4204:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4216:16-4222:10: Returned type `IterNodeWindowReducible[Self@Frame]` is not assignable to declared return type `IterNodeWindowReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4216:39-4222:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4234:16-4240:10: Returned type `IterNodeWindowReducible[Self@Frame]` is not assignable to declared return type `IterNodeWindowReducible[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4234:39-4240:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4246:16-4252:10: Returned type `IterNodeAxisElement[Self@Frame]` is not assignable to declared return type `IterNodeAxisElement[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4246:35-4252:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
+ ERROR static_frame/core/frame.py:4257:16-4263:10: Returned type `IterNodeAxisElement[Self@Frame]` is not assignable to declared return type `IterNodeAxisElement[Frame]` [bad-return]
+ ERROR static_frame/core/frame.py:4257:35-4263:10: `Self@Frame` is not assignable to upper bound `Bus | Frame | Quilt | Series | Yarn` of type variable `TContainerAny` [bad-specialization]
core (https://github.com/home-assistant/core)
- ERROR homeassistant/helpers/data_entry_flow.py:83:25-47: Argument `dict[str, Any]` is not assignable to parameter `context` with type `_FlowContextT | None` in function `homeassistant.data_entry_flow.FlowManager.async_init` [bad-argument-type]
spark (https://github.com/apache/spark)
- ERROR python/pyspark/pandas/tests/indexes/test_category.py:179:35-52: `int | None` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
+ ERROR python/pyspark/pandas/tests/indexes/test_category.py:179:35-52: `int | None` is not assignable to upper bound `BaseOffset | CategoricalDtype | ExtensionDtype | Interval | Period | bool | bytes | complex | date | datetime | dtype[generic] | float | int | list[str] | str | time | timedelta | type[bool | complex | object | str]` of type variable `S1` [bad-specialization]
|
Primer Diff Classification❌ 1 regression(s) | ✅ 4 improvement(s) | ➖ 1 neutral | 6 project(s) total | +57, -6 errors 1 regression(s) across static-frame. error kinds:
Detailed analysis❌ Regression (1)static-frame (+56)
All reasoning is consistent and factually accurate.
✅ Improvement (4)antidote (-2)
rotki (-1)
steam.py (-1)
core (-1)
➖ Neutral (1)spark (+1, -1)
Suggested fixesSummary: The PR correctly narrows recursive TArg truncation but exposes a pre-existing bug where Self@Frame is not recognized as satisfying a TypeVar upper bound that includes Frame. 1. In
2. As a targeted workaround in
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (1 heuristic, 5 LLM) |
Fixes #3285.
Summary
This fixes a false-positive
bad-argument-typeerror when looking up a classmethod through atype[T]whereTis a protocol-bounded TypeVar whose bound is itself specialized, for exampleT: SQLExpr[Any].The PR changes recursive type-argument truncation so it only erases generic arguments when there is an actual recursive path back to the same class. Non-recursive specialized bounds such as
P[Any]are now preserved.Root Cause
TParams::truncate_recursive_targswas intentionally preventing fixpoint growth for recursive TypeVar bounds. The old predicate was too broad: it stripped type arguments from anyClassTypewhose formal type parameters had non-trivial restrictions.That meant a bound like:
could be truncated from
P[Any]to barePbecauseP's own type parameter had a bound. Later, attribute lookup ontype[P_co]instantiated classmethod parameters from bareP, so the method leakedP's class TypeVar instead of using the specializedAnyfrom the bound.In the original issue this made:
look like:
instead of the expected:
Fix
The truncation logic now walks the restriction graph and only strips
ClassTypearguments when the class's embedded type parameters can reach back to the same class through their own bounds or constraints.That keeps the recursion guard for shapes like
Bar[Any]whereBarparticipates in its own bound cycle, while preserving ordinary specialized bounds used for protocol classmethod lookup.Tests
Added a regression test covering a reduced protocol form of the issue:
Validation run locally:
I also reran a
reveal_typeprobe for the original reproducer and confirmed the classmethod parameter is now specialized as(Sequence[Any]) -> Any.AI Disclosure
This PR was prepared with help from Codex.