mirror of https://github.com/astral-sh/ruff
[ty] Don't use implicit superclass annotation when converting a class constructor into a `Callable` (#22011)
This fixes a bug @zsol found running ty against pyx. His original repro
is:
```py
class Base:
def __init__(self) -> None: pass
class A(Base):
pass
def foo[T](callable: Callable[..., T]) -> T:
return callable()
a: A = foo(A)
```
The call at the bottom would fail, since we would infer `() -> Base` as
the callable type of `A`, when it should be `() -> A`.
The issue was how we add implicit annotations to `self` parameters.
Typically, we turn it into `self: Self`. But in cases where we don't
need to introduce a full typevar, we turn it into `self: [the class
itself]` — in this case, `self: Base`. Then, when turning the class
constructor into a callable, we would see this non-`Self` annotation and
think that it was important and load-bearing.
The fix is that we skip all implicit annotations when determining
whether the `self` annotation should take precedence in the callable's
return type.
This commit is contained in:
parent
c02bd11b93
commit
2214a46139
|
|
@ -407,4 +407,22 @@ def f_okay(c: Callable[[], None]):
|
||||||
c.__qualname__ = "my_callable" # okay
|
c.__qualname__ = "my_callable" # okay
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## From a class
|
||||||
|
|
||||||
|
### Subclasses should return themselves, not superclass
|
||||||
|
|
||||||
|
```py
|
||||||
|
from ty_extensions import into_callable
|
||||||
|
|
||||||
|
class Base:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class A(Base):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# revealed: () -> A
|
||||||
|
reveal_type(into_callable(A))
|
||||||
|
```
|
||||||
|
|
||||||
[gradual form]: https://typing.python.org/en/latest/spec/glossary.html#term-gradual-form
|
[gradual form]: https://typing.python.org/en/latest/spec/glossary.html#term-gradual-form
|
||||||
|
|
|
||||||
|
|
@ -1257,6 +1257,7 @@ impl<'db> ClassType<'db> {
|
||||||
let self_annotation = signature
|
let self_annotation = signature
|
||||||
.parameters()
|
.parameters()
|
||||||
.get_positional(0)
|
.get_positional(0)
|
||||||
|
.filter(|parameter| !parameter.inferred_annotation)
|
||||||
.and_then(Parameter::annotated_type)
|
.and_then(Parameter::annotated_type)
|
||||||
.filter(|ty| {
|
.filter(|ty| {
|
||||||
ty.as_typevar()
|
ty.as_typevar()
|
||||||
|
|
|
||||||
|
|
@ -2167,7 +2167,7 @@ pub(crate) struct Parameter<'db> {
|
||||||
|
|
||||||
/// Does the type of this parameter come from an explicit annotation, or was it inferred from
|
/// Does the type of this parameter come from an explicit annotation, or was it inferred from
|
||||||
/// the context, like `Self` for the `self` parameter of instance methods.
|
/// the context, like `Self` for the `self` parameter of instance methods.
|
||||||
inferred_annotation: bool,
|
pub(crate) inferred_annotation: bool,
|
||||||
|
|
||||||
kind: ParameterKind<'db>,
|
kind: ParameterKind<'db>,
|
||||||
pub(crate) form: ParameterForm,
|
pub(crate) form: ParameterForm,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue