mirror of
https://github.com/astral-sh/ruff
synced 2026-01-06 22:24:05 -05:00
[ty] Treat Callable dunder members as bound method descriptors (#20860)
## Summary Dunder methods (at least the ones defined in the standard library) always take an instance of the class as the first parameter. So it seems reasonable to generally treat them as bound method descriptors if they are defined via a `Callable` type. This removes just a few false positives from the ecosystem, but solves three user-reported issues: closes https://github.com/astral-sh/ty/issues/908 closes https://github.com/astral-sh/ty/issues/1143 closes https://github.com/astral-sh/ty/issues/1209 In addition to the change here, I also considered [making `ClassVar`s bound method descriptors](https://github.com/astral-sh/ruff/pull/20861). However, there was zero ecosystem impact. So I think we can also close https://github.com/astral-sh/ty/issues/491 with this PR. closes https://github.com/astral-sh/ty/issues/491 ## Test Plan Added regression test
This commit is contained in:
@@ -204,3 +204,37 @@ class Calculator:
|
||||
|
||||
reveal_type(Calculator().square_then_round(3.14)) # revealed: Unknown | int
|
||||
```
|
||||
|
||||
## Use case: Treating dunder methods as bound-method descriptors
|
||||
|
||||
pytorch defines a `__pow__` dunder attribute on [`TensorBase`] in a similar way to the following
|
||||
example. We generally treat dunder attributes as bound-method descriptors since they all take a
|
||||
`self` argument. This allows us to type-check the following code correctly:
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
|
||||
def pow_impl(tensor: Tensor, exponent: int) -> Tensor:
|
||||
raise NotImplementedError
|
||||
|
||||
class Tensor:
|
||||
__pow__: Callable[[Tensor, int], Tensor] = pow_impl
|
||||
|
||||
Tensor() ** 2
|
||||
```
|
||||
|
||||
The following example is also taken from a real world project. Here, the `__lt__` dunder attribute
|
||||
is not declared. The attribute type is therefore inferred as `Unknown | Callable[…]`, but we still
|
||||
treat it as a bound-method descriptor:
|
||||
|
||||
```py
|
||||
def make_comparison_operator(name: str) -> Callable[[Matrix, Matrix], bool]:
|
||||
raise NotImplementedError
|
||||
|
||||
class Matrix:
|
||||
__lt__ = make_comparison_operator("lt")
|
||||
|
||||
Matrix() < Matrix()
|
||||
```
|
||||
|
||||
[`tensorbase`]: https://github.com/pytorch/pytorch/blob/f3913ea641d871f04fa2b6588a77f63efeeb9f10/torch/_tensor.py#L1084-L1092
|
||||
|
||||
Reference in New Issue
Block a user