[red-knot] Class literal __new__ function callable subtyping (#17533)

## Summary

From
https://typing.python.org/en/latest/spec/constructors.html#converting-a-constructor-to-callable

this covers step 2 and partially step 3 (always respecting the
`__new__`)

## Test Plan

Update is_subtype_of.md

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
Matthew Mckee
2025-04-23 06:40:33 +01:00
committed by GitHub
parent aa46047649
commit e45f23b0ec
3 changed files with 99 additions and 19 deletions

View File

@@ -1166,6 +1166,56 @@ static_assert(is_subtype_of(TypeOf[C], Callable[[int], int]))
static_assert(is_subtype_of(TypeOf[C], Callable[[], str]))
```
#### Classes with `__new__`
```py
from typing import Callable
from knot_extensions import TypeOf, static_assert, is_subtype_of
class A:
def __new__(cls, a: int) -> int:
return a
static_assert(is_subtype_of(TypeOf[A], Callable[[int], int]))
static_assert(not is_subtype_of(TypeOf[A], Callable[[], int]))
class B: ...
class C(B): ...
class D:
def __new__(cls) -> B:
return B()
class E(D):
def __new__(cls) -> C:
return C()
static_assert(is_subtype_of(TypeOf[E], Callable[[], C]))
static_assert(is_subtype_of(TypeOf[E], Callable[[], B]))
static_assert(not is_subtype_of(TypeOf[D], Callable[[], C]))
static_assert(is_subtype_of(TypeOf[D], Callable[[], B]))
```
#### Classes with `__call__` and `__new__`
If `__call__` and `__new__` are both present, `__call__` takes precedence.
```py
from typing import Callable
from knot_extensions import TypeOf, static_assert, is_subtype_of
class MetaWithIntReturn(type):
def __call__(cls) -> int:
return super().__call__()
class F(metaclass=MetaWithIntReturn):
def __new__(cls) -> str:
return super().__new__(cls)
static_assert(is_subtype_of(TypeOf[F], Callable[[], int]))
static_assert(not is_subtype_of(TypeOf[F], Callable[[], str]))
```
### Bound methods
```py