[ty] Fix descriptor lookups for most types that overlap with None (#19120)
This commit is contained in:
@@ -201,6 +201,36 @@ type IntOrStr = int | str
|
||||
reveal_type(IntOrStr.__or__) # revealed: bound method typing.TypeAliasType.__or__(right: Any) -> _SpecialForm
|
||||
```
|
||||
|
||||
## Method calls on types not disjoint from `None`
|
||||
|
||||
Very few methods are defined on `object`, `None`, and other types not disjoint from `None`. However,
|
||||
descriptor-binding behaviour works on these types in exactly the same way as descriptor binding on
|
||||
other types. This is despite the fact that `None` is used as a sentinel internally by the descriptor
|
||||
protocol to indicate that a method was accessed on the class itself rather than an instance of the
|
||||
class:
|
||||
|
||||
```py
|
||||
from typing import Protocol, Literal
|
||||
from ty_extensions import AlwaysFalsy
|
||||
|
||||
class Foo: ...
|
||||
|
||||
class SupportsStr(Protocol):
|
||||
def __str__(self) -> str: ...
|
||||
|
||||
class Falsy(Protocol):
|
||||
def __bool__(self) -> Literal[False]: ...
|
||||
|
||||
def _(a: object, b: SupportsStr, c: Falsy, d: AlwaysFalsy, e: None, f: Foo | None):
|
||||
a.__str__()
|
||||
b.__str__()
|
||||
c.__str__()
|
||||
d.__str__()
|
||||
# TODO: these should not error
|
||||
e.__str__() # error: [missing-argument]
|
||||
f.__str__() # error: [missing-argument]
|
||||
```
|
||||
|
||||
## Error cases: Calling `__get__` for methods
|
||||
|
||||
The `__get__` method on `types.FunctionType` has the following overloaded signature in typeshed:
|
||||
@@ -234,16 +264,18 @@ method_wrapper(C())
|
||||
method_wrapper(C(), None)
|
||||
method_wrapper(None, C)
|
||||
|
||||
# Passing `None` without an `owner` argument is an
|
||||
# error: [invalid-argument-type] "Argument to method wrapper `__get__` of function `f` is incorrect: Expected `~None`, found `None`"
|
||||
reveal_type(object.__str__.__get__(object(), None)()) # revealed: str
|
||||
|
||||
# TODO: passing `None` without an `owner` argument fails at runtime.
|
||||
# Ideally we would emit a diagnostic here:
|
||||
method_wrapper(None)
|
||||
|
||||
# Passing something that is not assignable to `type` as the `owner` argument is an
|
||||
# error: [no-matching-overload] "No overload of method wrapper `__get__` of function `f` matches arguments"
|
||||
method_wrapper(None, 1)
|
||||
|
||||
# Passing `None` as the `owner` argument when `instance` is `None` is an
|
||||
# error: [no-matching-overload] "No overload of method wrapper `__get__` of function `f` matches arguments"
|
||||
# TODO: passing `None` as the `owner` argument when `instance` is `None` fails at runtime.
|
||||
# Ideally we would emit a diagnostic here.
|
||||
method_wrapper(None, None)
|
||||
|
||||
# Calling `__get__` without any arguments is an
|
||||
|
||||
Reference in New Issue
Block a user