[red-knot] improve function/bound method type display (#17294)
## Summary * Partial #17238 * Flyby from discord discussion - `todo_type!` now statically checks for no parens in the message to avoid issues between debug & release build tests ## Test Plan many mdtests are changing
This commit is contained in:
@@ -289,7 +289,7 @@ def _(c: Callable[[int, Unpack[Ts]], int]):
|
||||
from typing import Callable
|
||||
|
||||
def _(c: Callable[[int], int]):
|
||||
reveal_type(c.__init__) # revealed: Literal[__init__]
|
||||
reveal_type(c.__init__) # revealed: def __init__(self) -> None
|
||||
reveal_type(c.__class__) # revealed: type
|
||||
|
||||
# TODO: The member lookup for `Callable` uses `object` which does not have a `__call__`
|
||||
|
||||
@@ -1373,7 +1373,7 @@ from typing import Any
|
||||
class Foo(Any): ...
|
||||
|
||||
reveal_type(Foo.bar) # revealed: Any
|
||||
reveal_type(Foo.__repr__) # revealed: Literal[__repr__] & Any
|
||||
reveal_type(Foo.__repr__) # revealed: (def __repr__(self) -> str) & Any
|
||||
```
|
||||
|
||||
Similar principles apply if `Any` appears in the middle of an inheritance hierarchy:
|
||||
@@ -1672,7 +1672,7 @@ Some attributes are special-cased, however:
|
||||
|
||||
```py
|
||||
reveal_type(f.__get__) # revealed: <method-wrapper `__get__` of `f`>
|
||||
reveal_type(f.__call__) # revealed: <bound method `__call__` of `Literal[f]`>
|
||||
reveal_type(f.__call__) # revealed: <method-wrapper `__call__` of `f`>
|
||||
```
|
||||
|
||||
### Int-literal attributes
|
||||
@@ -1681,7 +1681,7 @@ Most attribute accesses on int-literal types are delegated to `builtins.int`, si
|
||||
integers are instances of that class:
|
||||
|
||||
```py
|
||||
reveal_type((2).bit_length) # revealed: <bound method `bit_length` of `Literal[2]`>
|
||||
reveal_type((2).bit_length) # revealed: bound method Literal[2].bit_length() -> int
|
||||
reveal_type((2).denominator) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
@@ -1698,8 +1698,10 @@ Most attribute accesses on bool-literal types are delegated to `builtins.bool`,
|
||||
bools are instances of that class:
|
||||
|
||||
```py
|
||||
reveal_type(True.__and__) # revealed: <bound method `__and__` of `Literal[True]`>
|
||||
reveal_type(False.__or__) # revealed: <bound method `__or__` of `Literal[False]`>
|
||||
# revealed: bound method Literal[True].__and__(**kwargs: @Todo(todo signature **kwargs)) -> @Todo(return type of overloaded function)
|
||||
reveal_type(True.__and__)
|
||||
# revealed: bound method Literal[False].__or__(**kwargs: @Todo(todo signature **kwargs)) -> @Todo(return type of overloaded function)
|
||||
reveal_type(False.__or__)
|
||||
```
|
||||
|
||||
Some attributes are special-cased, however:
|
||||
@@ -1714,8 +1716,9 @@ reveal_type(False.real) # revealed: Literal[0]
|
||||
All attribute access on literal `bytes` types is currently delegated to `builtins.bytes`:
|
||||
|
||||
```py
|
||||
reveal_type(b"foo".join) # revealed: <bound method `join` of `Literal[b"foo"]`>
|
||||
reveal_type(b"foo".endswith) # revealed: <bound method `endswith` of `Literal[b"foo"]`>
|
||||
reveal_type(b"foo".join) # revealed: bound method Literal[b"foo"].join(iterable_of_bytes: @Todo(generics), /) -> bytes
|
||||
# revealed: bound method Literal[b"foo"].endswith(suffix: @Todo(Support for `typing.TypeAlias`), start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
|
||||
reveal_type(b"foo".endswith)
|
||||
```
|
||||
|
||||
## Instance attribute edge cases
|
||||
|
||||
@@ -350,30 +350,30 @@ reveal_type(no() + no()) # revealed: Unknown
|
||||
def f():
|
||||
pass
|
||||
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f + f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `-` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `-` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f - f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `*` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `*` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f * f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `@` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `@` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f @ f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `/` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `/` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f / f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `%` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `%` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f % f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `**` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `**` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f**f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `<<` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `<<` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f << f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `>>` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `>>` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f >> f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `|` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `|` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f | f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `^` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `^` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f ^ f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `&` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `&` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f & f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `//` is unsupported between objects of type `Literal[f]` and `Literal[f]`"
|
||||
# error: [unsupported-operator] "Operator `//` is unsupported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
reveal_type(f // f) # revealed: Unknown
|
||||
```
|
||||
|
||||
@@ -57,7 +57,8 @@ We can access attributes on objects of all kinds:
|
||||
import sys
|
||||
|
||||
reveal_type(inspect.getattr_static(sys, "dont_write_bytecode")) # revealed: bool
|
||||
reveal_type(inspect.getattr_static(inspect, "getattr_static")) # revealed: Literal[getattr_static]
|
||||
# revealed: def getattr_static(obj: object, attr: str, default: Any | None = ellipsis) -> Any
|
||||
reveal_type(inspect.getattr_static(inspect, "getattr_static"))
|
||||
|
||||
reveal_type(inspect.getattr_static(1, "real")) # revealed: property
|
||||
```
|
||||
@@ -143,8 +144,9 @@ from typing import Any
|
||||
def _(a: Any, tuple_of_any: tuple[Any]):
|
||||
reveal_type(inspect.getattr_static(a, "x", "default")) # revealed: Any | Literal["default"]
|
||||
|
||||
# TODO: Ideally, this would just be `Literal[index]`
|
||||
reveal_type(inspect.getattr_static(tuple_of_any, "index", "default")) # revealed: Literal[index] | Literal["default"]
|
||||
# TODO: Ideally, this would just be `def index(self, value: Any, start: SupportsIndex = Literal[0], stop: SupportsIndex = int, /) -> int`
|
||||
# revealed: (def index(self, value: Any, start: SupportsIndex = Literal[0], stop: SupportsIndex = int, /) -> int) | Literal["default"]
|
||||
reveal_type(inspect.getattr_static(tuple_of_any, "index", "default"))
|
||||
```
|
||||
|
||||
[official documentation]: https://docs.python.org/3/library/inspect.html#inspect.getattr_static
|
||||
|
||||
@@ -32,20 +32,20 @@ the latter case, it returns a *bound method* object:
|
||||
```py
|
||||
from inspect import getattr_static
|
||||
|
||||
reveal_type(getattr_static(C, "f")) # revealed: Literal[f]
|
||||
reveal_type(getattr_static(C, "f")) # revealed: def f(self, x: int) -> str
|
||||
|
||||
reveal_type(getattr_static(C, "f").__get__) # revealed: <method-wrapper `__get__` of `f`>
|
||||
|
||||
reveal_type(getattr_static(C, "f").__get__(None, C)) # revealed: Literal[f]
|
||||
reveal_type(getattr_static(C, "f").__get__(C(), C)) # revealed: <bound method `f` of `C`>
|
||||
reveal_type(getattr_static(C, "f").__get__(None, C)) # revealed: def f(self, x: int) -> str
|
||||
reveal_type(getattr_static(C, "f").__get__(C(), C)) # revealed: bound method C.f(x: int) -> str
|
||||
```
|
||||
|
||||
In conclusion, this is why we see the following two types when accessing the `f` attribute on the
|
||||
class object `C` and on an instance `C()`:
|
||||
|
||||
```py
|
||||
reveal_type(C.f) # revealed: Literal[f]
|
||||
reveal_type(C().f) # revealed: <bound method `f` of `C`>
|
||||
reveal_type(C.f) # revealed: def f(self, x: int) -> str
|
||||
reveal_type(C().f) # revealed: bound method C.f(x: int) -> str
|
||||
```
|
||||
|
||||
A bound method is a callable object that contains a reference to the `instance` that it was called
|
||||
@@ -56,7 +56,7 @@ via `__func__`):
|
||||
bound_method = C().f
|
||||
|
||||
reveal_type(bound_method.__self__) # revealed: C
|
||||
reveal_type(bound_method.__func__) # revealed: Literal[f]
|
||||
reveal_type(bound_method.__func__) # revealed: def f(self, x: int) -> str
|
||||
```
|
||||
|
||||
When we call the bound method, the `instance` is implicitly passed as the first argument (`self`):
|
||||
@@ -80,13 +80,13 @@ When we access methods from derived classes, they will be bound to instances of
|
||||
class D(C):
|
||||
pass
|
||||
|
||||
reveal_type(D().f) # revealed: <bound method `f` of `D`>
|
||||
reveal_type(D().f) # revealed: bound method D.f(x: int) -> str
|
||||
```
|
||||
|
||||
If we access an attribute on a bound method object itself, it will defer to `types.MethodType`:
|
||||
|
||||
```py
|
||||
reveal_type(bound_method.__hash__) # revealed: <bound method `__hash__` of `MethodType`>
|
||||
reveal_type(bound_method.__hash__) # revealed: bound method MethodType.__hash__() -> int
|
||||
```
|
||||
|
||||
If an attribute is not available on the bound method object, it will be looked up on the underlying
|
||||
@@ -181,10 +181,10 @@ class B:
|
||||
return "a"
|
||||
|
||||
def f(a_or_b: A | B, any_or_a: Any | A):
|
||||
reveal_type(a_or_b.f) # revealed: <bound method `f` of `A`> | <bound method `f` of `B`>
|
||||
reveal_type(a_or_b.f) # revealed: (bound method A.f() -> int) | (bound method B.f() -> str)
|
||||
reveal_type(a_or_b.f()) # revealed: int | str
|
||||
|
||||
reveal_type(any_or_a.f) # revealed: Any | <bound method `f` of `A`>
|
||||
reveal_type(any_or_a.f) # revealed: Any | (bound method A.f() -> int)
|
||||
reveal_type(any_or_a.f()) # revealed: Any | int
|
||||
```
|
||||
|
||||
@@ -198,7 +198,7 @@ python-version = "3.12"
|
||||
```py
|
||||
type IntOrStr = int | str
|
||||
|
||||
reveal_type(IntOrStr.__or__) # revealed: <bound method `__or__` of `typing.TypeAliasType`>
|
||||
reveal_type(IntOrStr.__or__) # revealed: bound method typing.TypeAliasType.__or__(right: Any) -> _SpecialForm
|
||||
```
|
||||
|
||||
## Error cases: Calling `__get__` for methods
|
||||
@@ -270,7 +270,7 @@ class Meta(type):
|
||||
class C(metaclass=Meta):
|
||||
pass
|
||||
|
||||
reveal_type(C.f) # revealed: <bound method `f` of `Literal[C]`>
|
||||
reveal_type(C.f) # revealed: bound method Literal[C].f(arg: int) -> str
|
||||
reveal_type(C.f(1)) # revealed: str
|
||||
```
|
||||
|
||||
@@ -322,8 +322,8 @@ class C:
|
||||
def f(cls: type[C], x: int) -> str:
|
||||
return "a"
|
||||
|
||||
reveal_type(C.f) # revealed: <bound method `f` of `Literal[C]`>
|
||||
reveal_type(C().f) # revealed: <bound method `f` of `type[C]`>
|
||||
reveal_type(C.f) # revealed: bound method Literal[C].f(x: int) -> str
|
||||
reveal_type(C().f) # revealed: bound method type[C].f(x: int) -> str
|
||||
```
|
||||
|
||||
The `cls` method argument is then implicitly passed as the first argument when calling the method:
|
||||
@@ -360,8 +360,8 @@ When a class method is accessed on a derived class, it is bound to that derived
|
||||
class Derived(C):
|
||||
pass
|
||||
|
||||
reveal_type(Derived.f) # revealed: <bound method `f` of `Literal[Derived]`>
|
||||
reveal_type(Derived().f) # revealed: <bound method `f` of `type[Derived]`>
|
||||
reveal_type(Derived.f) # revealed: bound method Literal[Derived].f(x: int) -> str
|
||||
reveal_type(Derived().f) # revealed: bound method type[Derived].f(x: int) -> str
|
||||
|
||||
reveal_type(Derived.f(1)) # revealed: str
|
||||
reveal_type(Derived().f(1)) # revealed: str
|
||||
@@ -379,22 +379,22 @@ class C:
|
||||
@classmethod
|
||||
def f(cls): ...
|
||||
|
||||
reveal_type(getattr_static(C, "f")) # revealed: Literal[f]
|
||||
reveal_type(getattr_static(C, "f")) # revealed: def f(cls) -> Unknown
|
||||
reveal_type(getattr_static(C, "f").__get__) # revealed: <method-wrapper `__get__` of `f`>
|
||||
```
|
||||
|
||||
But we correctly model how the `classmethod` descriptor works:
|
||||
|
||||
```py
|
||||
reveal_type(getattr_static(C, "f").__get__(None, C)) # revealed: <bound method `f` of `Literal[C]`>
|
||||
reveal_type(getattr_static(C, "f").__get__(C(), C)) # revealed: <bound method `f` of `Literal[C]`>
|
||||
reveal_type(getattr_static(C, "f").__get__(C())) # revealed: <bound method `f` of `type[C]`>
|
||||
reveal_type(getattr_static(C, "f").__get__(None, C)) # revealed: bound method Literal[C].f() -> Unknown
|
||||
reveal_type(getattr_static(C, "f").__get__(C(), C)) # revealed: bound method Literal[C].f() -> Unknown
|
||||
reveal_type(getattr_static(C, "f").__get__(C())) # revealed: bound method type[C].f() -> Unknown
|
||||
```
|
||||
|
||||
The `owner` argument takes precedence over the `instance` argument:
|
||||
|
||||
```py
|
||||
reveal_type(getattr_static(C, "f").__get__("dummy", C)) # revealed: <bound method `f` of `Literal[C]`>
|
||||
reveal_type(getattr_static(C, "f").__get__("dummy", C)) # revealed: bound method Literal[C].f() -> Unknown
|
||||
```
|
||||
|
||||
### Classmethods mixed with other decorators
|
||||
|
||||
@@ -207,7 +207,7 @@ first argument:
|
||||
def wrong_signature(f: int) -> str:
|
||||
return "a"
|
||||
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal[f]`"
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `def f(x) -> Unknown`"
|
||||
@wrong_signature
|
||||
def f(x): ...
|
||||
|
||||
|
||||
@@ -563,18 +563,18 @@ from inspect import getattr_static
|
||||
def f(x: object) -> str:
|
||||
return "a"
|
||||
|
||||
reveal_type(f) # revealed: Literal[f]
|
||||
reveal_type(f) # revealed: def f(x: object) -> str
|
||||
reveal_type(f.__get__) # revealed: <method-wrapper `__get__` of `f`>
|
||||
reveal_type(f.__get__(None, type(f))) # revealed: Literal[f]
|
||||
reveal_type(f.__get__(None, type(f))) # revealed: def f(x: object) -> str
|
||||
reveal_type(f.__get__(None, type(f))(1)) # revealed: str
|
||||
|
||||
wrapper_descriptor = getattr_static(f, "__get__")
|
||||
|
||||
reveal_type(wrapper_descriptor) # revealed: <wrapper-descriptor `__get__` of `function` objects>
|
||||
reveal_type(wrapper_descriptor(f, None, type(f))) # revealed: Literal[f]
|
||||
reveal_type(wrapper_descriptor(f, None, type(f))) # revealed: def f(x: object) -> str
|
||||
|
||||
# Attribute access on the method-wrapper `f.__get__` falls back to `MethodWrapperType`:
|
||||
reveal_type(f.__get__.__hash__) # revealed: <bound method `__hash__` of `MethodWrapperType`>
|
||||
reveal_type(f.__get__.__hash__) # revealed: bound method MethodWrapperType.__hash__() -> int
|
||||
|
||||
# Attribute access on the wrapper-descriptor falls back to `WrapperDescriptorType`:
|
||||
reveal_type(wrapper_descriptor.__qualname__) # revealed: str
|
||||
@@ -587,7 +587,7 @@ class C: ...
|
||||
|
||||
bound_method = wrapper_descriptor(f, C(), C)
|
||||
|
||||
reveal_type(bound_method) # revealed: <bound method `f` of `C`>
|
||||
reveal_type(bound_method) # revealed: bound method C.f() -> str
|
||||
```
|
||||
|
||||
We can then call it, and the instance of `C` is implicitly passed to the first parameter of `f`
|
||||
|
||||
@@ -591,9 +591,9 @@ try:
|
||||
reveal_type(x) # revealed: B | D
|
||||
reveal_type(x) # revealed: B | D
|
||||
x = foo
|
||||
reveal_type(x) # revealed: Literal[foo]
|
||||
reveal_type(x) # revealed: def foo(param=A) -> Unknown
|
||||
except:
|
||||
reveal_type(x) # revealed: Literal[1] | Literal[foo]
|
||||
reveal_type(x) # revealed: Literal[1] | (def foo(param=A) -> Unknown)
|
||||
|
||||
class Bar:
|
||||
x = could_raise_returns_E()
|
||||
@@ -603,9 +603,9 @@ except:
|
||||
reveal_type(x) # revealed: Literal[Bar]
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | Literal[foo] | Literal[Bar]`
|
||||
reveal_type(x) # revealed: Literal[foo] | Literal[Bar]
|
||||
reveal_type(x) # revealed: (def foo(param=A) -> Unknown) | Literal[Bar]
|
||||
|
||||
reveal_type(x) # revealed: Literal[foo] | Literal[Bar]
|
||||
reveal_type(x) # revealed: (def foo(param=A) -> Unknown) | Literal[Bar]
|
||||
```
|
||||
|
||||
[1]: https://astral-sh.notion.site/Exception-handler-control-flow-11348797e1ca80bb8ce1e9aedbbe439d
|
||||
|
||||
@@ -102,18 +102,18 @@ class C[T]:
|
||||
def f(self, x: T) -> str:
|
||||
return "a"
|
||||
|
||||
reveal_type(getattr_static(C[int], "f")) # revealed: Literal[f[int]]
|
||||
reveal_type(getattr_static(C[int], "f")) # revealed: def f(self, x: int) -> str
|
||||
reveal_type(getattr_static(C[int], "f").__get__) # revealed: <method-wrapper `__get__` of `f[int]`>
|
||||
reveal_type(getattr_static(C[int], "f").__get__(None, C[int])) # revealed: Literal[f[int]]
|
||||
# revealed: <bound method `f` of `C[int]`>
|
||||
reveal_type(getattr_static(C[int], "f").__get__(None, C[int])) # revealed: def f(self, x: int) -> str
|
||||
# revealed: bound method C[int].f(x: int) -> str
|
||||
reveal_type(getattr_static(C[int], "f").__get__(C[int](), C[int]))
|
||||
|
||||
reveal_type(C[int].f) # revealed: Literal[f[int]]
|
||||
reveal_type(C[int]().f) # revealed: <bound method `f` of `C[int]`>
|
||||
reveal_type(C[int].f) # revealed: def f(self, x: int) -> str
|
||||
reveal_type(C[int]().f) # revealed: bound method C[int].f(x: int) -> str
|
||||
|
||||
bound_method = C[int]().f
|
||||
reveal_type(bound_method.__self__) # revealed: C[int]
|
||||
reveal_type(bound_method.__func__) # revealed: Literal[f[int]]
|
||||
reveal_type(bound_method.__func__) # revealed: def f(self, x: int) -> str
|
||||
|
||||
reveal_type(C[int]().f(1)) # revealed: str
|
||||
reveal_type(bound_method(1)) # revealed: str
|
||||
@@ -124,7 +124,7 @@ reveal_type(C[int].f(C[int](), 1)) # revealed: str
|
||||
class D[U](C[U]):
|
||||
pass
|
||||
|
||||
reveal_type(D[int]().f) # revealed: <bound method `f` of `D[int]`>
|
||||
reveal_type(D[int]().f) # revealed: bound method D[int].f(x: int) -> str
|
||||
```
|
||||
|
||||
## Methods can mention other typevars
|
||||
|
||||
@@ -7,7 +7,7 @@ Builtin symbols can be explicitly imported:
|
||||
```py
|
||||
import builtins
|
||||
|
||||
reveal_type(builtins.chr) # revealed: Literal[chr]
|
||||
reveal_type(builtins.chr) # revealed: def chr(i: int | SupportsIndex, /) -> str
|
||||
```
|
||||
|
||||
## Implicit use of builtin
|
||||
@@ -15,7 +15,7 @@ reveal_type(builtins.chr) # revealed: Literal[chr]
|
||||
Or used implicitly:
|
||||
|
||||
```py
|
||||
reveal_type(chr) # revealed: Literal[chr]
|
||||
reveal_type(chr) # revealed: def chr(i: int | SupportsIndex, /) -> str
|
||||
reveal_type(str) # revealed: Literal[str]
|
||||
```
|
||||
|
||||
|
||||
@@ -103,8 +103,8 @@ else:
|
||||
```py
|
||||
from b import f
|
||||
|
||||
# TODO: We should disambiguate in such cases, showing `Literal[b.f, c.f]`.
|
||||
reveal_type(f) # revealed: Literal[f, f]
|
||||
# TODO: We should disambiguate in such cases between `b.f` and `c.f`.
|
||||
reveal_type(f) # revealed: (def f() -> Unknown) | (def f() -> Unknown)
|
||||
```
|
||||
|
||||
## Reimport with stub declaration
|
||||
|
||||
@@ -1034,8 +1034,8 @@ from exporter import *
|
||||
# At runtime, `f` is imported but `g` is not; to avoid false positives, however,
|
||||
# we treat `a` as though it does not have `__all__` at all,
|
||||
# which would imply that both symbols would be present.
|
||||
reveal_type(f) # revealed: Literal[f]
|
||||
reveal_type(g) # revealed: Literal[g]
|
||||
reveal_type(f) # revealed: def f() -> str
|
||||
reveal_type(g) # revealed: def g() -> int
|
||||
```
|
||||
|
||||
### `__all__` conditionally defined in a statically known branch
|
||||
@@ -1198,7 +1198,7 @@ f()
|
||||
```py
|
||||
from a import *
|
||||
|
||||
reveal_type(f) # revealed: Literal[f]
|
||||
reveal_type(f) # revealed: def f() -> Unknown
|
||||
|
||||
# TODO: we're undecided about whether we should consider this a false positive or not.
|
||||
# Mutating the global scope to add a symbol from an inner scope will not *necessarily* result
|
||||
|
||||
@@ -321,7 +321,7 @@ def _(flag: bool):
|
||||
# TODO... `int` might be ideal here?
|
||||
reveal_type(x) # revealed: int | Unknown
|
||||
|
||||
# error: [not-iterable] "Object of type `Iterable2` may not be iterable because its `__iter__` attribute (with type `<bound method `__iter__` of `Iterable2`> | None`) may not be callable"
|
||||
# error: [not-iterable] "Object of type `Iterable2` may not be iterable because its `__iter__` attribute (with type `(bound method Iterable2.__iter__() -> Iterator) | None`) may not be callable"
|
||||
for y in Iterable2():
|
||||
# TODO... `int` might be ideal here?
|
||||
reveal_type(y) # revealed: int | Unknown
|
||||
|
||||
@@ -63,7 +63,7 @@ def bar(world: str, *args, **kwargs) -> float:
|
||||
x = foo if flag() else bar
|
||||
|
||||
if x:
|
||||
reveal_type(x) # revealed: Literal[foo, bar]
|
||||
reveal_type(x) # revealed: (def foo(hello: int) -> bytes) | (def bar(world: str, *args, **kwargs) -> int | float)
|
||||
else:
|
||||
reveal_type(x) # revealed: Never
|
||||
```
|
||||
|
||||
@@ -146,7 +146,7 @@ class C:
|
||||
@property
|
||||
def attr(self) -> int:
|
||||
return 1
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `(Any, Any, /) -> None`, found `Literal[attr]`"
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `(Any, Any, /) -> None`, found `def attr(self) -> None`"
|
||||
@attr.setter
|
||||
def attr(self) -> None:
|
||||
pass
|
||||
@@ -156,7 +156,7 @@ class C:
|
||||
|
||||
```py
|
||||
class C:
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `((Any, /) -> Any) | None`, found `Literal[attr]`"
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `((Any, /) -> Any) | None`, found `def attr(self, x: int) -> int`"
|
||||
@property
|
||||
def attr(self, x: int) -> int:
|
||||
return 1
|
||||
@@ -294,10 +294,10 @@ Properties also have `fget` and `fset` attributes that can be used to retrieve t
|
||||
and setter functions, respectively.
|
||||
|
||||
```py
|
||||
reveal_type(attr_property.fget) # revealed: Literal[attr]
|
||||
reveal_type(attr_property.fget) # revealed: def attr(self) -> int
|
||||
reveal_type(attr_property.fget(c)) # revealed: int
|
||||
|
||||
reveal_type(attr_property.fset) # revealed: Literal[attr]
|
||||
reveal_type(attr_property.fset) # revealed: def attr(self, value: str) -> None
|
||||
reveal_type(attr_property.fset(c, "a")) # revealed: None
|
||||
|
||||
# error: [invalid-argument-type]
|
||||
|
||||
@@ -13,7 +13,7 @@ if returns_bool():
|
||||
chr: int = 1
|
||||
|
||||
def f():
|
||||
reveal_type(chr) # revealed: int | Literal[chr]
|
||||
reveal_type(chr) # revealed: int | (def chr(i: int | SupportsIndex, /) -> str)
|
||||
```
|
||||
|
||||
## Conditionally global or builtin, with annotation
|
||||
@@ -28,5 +28,5 @@ if returns_bool():
|
||||
chr: int = 1
|
||||
|
||||
def f():
|
||||
reveal_type(chr) # revealed: int | Literal[chr]
|
||||
reveal_type(chr) # revealed: int | (def chr(i: int | SupportsIndex, /) -> str)
|
||||
```
|
||||
|
||||
@@ -51,10 +51,10 @@ inside the module:
|
||||
import typing
|
||||
|
||||
reveal_type(typing.__name__) # revealed: str
|
||||
reveal_type(typing.__init__) # revealed: <bound method `__init__` of `ModuleType`>
|
||||
reveal_type(typing.__init__) # revealed: bound method ModuleType.__init__(name: str, doc: str | None = ellipsis) -> None
|
||||
|
||||
# These come from `builtins.object`, not `types.ModuleType`:
|
||||
reveal_type(typing.__eq__) # revealed: <bound method `__eq__` of `ModuleType`>
|
||||
reveal_type(typing.__eq__) # revealed: bound method ModuleType.__eq__(value: object, /) -> bool
|
||||
|
||||
reveal_type(typing.__class__) # revealed: Literal[ModuleType]
|
||||
|
||||
|
||||
@@ -37,17 +37,17 @@ reveal_type(f) # revealed: Literal[1]
|
||||
|
||||
def f(): ...
|
||||
|
||||
reveal_type(f) # revealed: Literal[f]
|
||||
reveal_type(f) # revealed: def f() -> Unknown
|
||||
|
||||
def f(x: int) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
reveal_type(f) # revealed: Literal[f]
|
||||
reveal_type(f) # revealed: def f(x: int) -> int
|
||||
|
||||
f: int = 1
|
||||
reveal_type(f) # revealed: Literal[1]
|
||||
|
||||
def f(): ...
|
||||
|
||||
reveal_type(f) # revealed: Literal[f]
|
||||
reveal_type(f) # revealed: def f() -> Unknown
|
||||
```
|
||||
|
||||
@@ -78,7 +78,7 @@ error: lint:not-iterable
|
||||
|
|
||||
26 | # error: [not-iterable]
|
||||
27 | for y in Iterable2():
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because it has no `__iter__` method and its `__getitem__` attribute (with type `<bound method `__getitem__` of `Iterable2`> | None`) may not be callable
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because it has no `__iter__` method and its `__getitem__` attribute (with type `(bound method Iterable2.__getitem__(key: int) -> int) | None`) may not be callable
|
||||
28 | # TODO... `int` might be ideal here?
|
||||
29 | reveal_type(y) # revealed: int | Unknown
|
||||
|
|
||||
|
||||
@@ -48,7 +48,7 @@ error: lint:not-iterable
|
||||
|
|
||||
19 | # error: [not-iterable]
|
||||
20 | for x in Iterable1():
|
||||
| ^^^^^^^^^^^ Object of type `Iterable1` may not be iterable because it has no `__iter__` method and its `__getitem__` attribute (with type `<bound method `__getitem__` of `Iterable1`> | None`) may not be callable
|
||||
| ^^^^^^^^^^^ Object of type `Iterable1` may not be iterable because it has no `__iter__` method and its `__getitem__` attribute (with type `(bound method Iterable1.__getitem__(item: int) -> str) | None`) may not be callable
|
||||
21 | # TODO: `str` might be better
|
||||
22 | reveal_type(x) # revealed: str | Unknown
|
||||
|
|
||||
@@ -75,7 +75,7 @@ error: lint:not-iterable
|
||||
|
|
||||
24 | # error: [not-iterable]
|
||||
25 | for y in Iterable2():
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because it has no `__iter__` method and its `__getitem__` method (with type `<bound method `__getitem__` of `Iterable2`> | <bound method `__getitem__` of `Iterable2`>`) may have an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because it has no `__iter__` method and its `__getitem__` method (with type `(bound method Iterable2.__getitem__(item: int) -> str) | (bound method Iterable2.__getitem__(item: str) -> int)`) may have an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
26 | reveal_type(y) # revealed: str | int
|
||||
|
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ error: lint:not-iterable
|
||||
|
|
||||
16 | # error: [not-iterable]
|
||||
17 | for x in Iterable1():
|
||||
| ^^^^^^^^^^^ Object of type `Iterable1` may not be iterable because its `__iter__` method (with type `<bound method `__iter__` of `Iterable1`> | <bound method `__iter__` of `Iterable1`>`) may have an invalid signature (expected `def __iter__(self): ...`)
|
||||
| ^^^^^^^^^^^ Object of type `Iterable1` may not be iterable because its `__iter__` method (with type `(bound method Iterable1.__iter__() -> Iterator) | (bound method Iterable1.__iter__(invalid_extra_arg) -> Iterator)`) may have an invalid signature (expected `def __iter__(self): ...`)
|
||||
18 | reveal_type(x) # revealed: int
|
||||
|
|
||||
|
||||
@@ -78,7 +78,7 @@ error: lint:not-iterable
|
||||
|
|
||||
27 | # error: [not-iterable]
|
||||
28 | for x in Iterable2():
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because its `__iter__` attribute (with type `<bound method `__iter__` of `Iterable2`> | None`) may not be callable
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because its `__iter__` attribute (with type `(bound method Iterable2.__iter__() -> Iterator) | None`) may not be callable
|
||||
29 | # TODO: `int` would probably be better here:
|
||||
30 | reveal_type(x) # revealed: int | Unknown
|
||||
|
|
||||
|
||||
@@ -59,7 +59,7 @@ error: lint:not-iterable
|
||||
|
|
||||
30 | # error: [not-iterable]
|
||||
31 | for x in Iterable1():
|
||||
| ^^^^^^^^^^^ Object of type `Iterable1` may not be iterable because it may not have an `__iter__` method and its `__getitem__` attribute (with type `<bound method `__getitem__` of `Iterable1`> | None`) may not be callable
|
||||
| ^^^^^^^^^^^ Object of type `Iterable1` may not be iterable because it may not have an `__iter__` method and its `__getitem__` attribute (with type `(bound method Iterable1.__getitem__(item: int) -> str) | None`) may not be callable
|
||||
32 | # TODO: `bytes | str` might be better
|
||||
33 | reveal_type(x) # revealed: bytes | str | Unknown
|
||||
|
|
||||
@@ -86,7 +86,7 @@ error: lint:not-iterable
|
||||
|
|
||||
35 | # error: [not-iterable]
|
||||
36 | for y in Iterable2():
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because it may not have an `__iter__` method and its `__getitem__` method (with type `<bound method `__getitem__` of `Iterable2`> | <bound method `__getitem__` of `Iterable2`>`) may have an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
| ^^^^^^^^^^^ Object of type `Iterable2` may not be iterable because it may not have an `__iter__` method and its `__getitem__` method (with type `(bound method Iterable2.__getitem__(item: int) -> str) | (bound method Iterable2.__getitem__(item: str) -> int)`) may have an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
37 | reveal_type(y) # revealed: bytes | str | int
|
||||
|
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ in strict mode.
|
||||
```py
|
||||
def f(x: type):
|
||||
reveal_type(x) # revealed: type
|
||||
reveal_type(x.__repr__) # revealed: <bound method `__repr__` of `type`>
|
||||
reveal_type(x.__repr__) # revealed: bound method type.__repr__() -> str
|
||||
|
||||
class A: ...
|
||||
|
||||
@@ -50,7 +50,7 @@ x: type = A() # error: [invalid-assignment]
|
||||
```py
|
||||
def f(x: type[object]):
|
||||
reveal_type(x) # revealed: type
|
||||
reveal_type(x.__repr__) # revealed: <bound method `__repr__` of `type`>
|
||||
reveal_type(x.__repr__) # revealed: bound method type.__repr__() -> str
|
||||
|
||||
class A: ...
|
||||
|
||||
|
||||
@@ -500,7 +500,7 @@ def g(x: Any) -> int:
|
||||
|
||||
c: Callable[[Any], str] = f
|
||||
|
||||
# error: [invalid-assignment] "Object of type `Literal[g]` is not assignable to `(Any, /) -> str`"
|
||||
# error: [invalid-assignment] "Object of type `def g(x: Any) -> int` is not assignable to `(Any, /) -> str`"
|
||||
c: Callable[[Any], str] = g
|
||||
```
|
||||
|
||||
|
||||
@@ -80,11 +80,11 @@ reveal_type(~No) # revealed: Unknown
|
||||
def f():
|
||||
pass
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is unsupported for type `Literal[f]`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is unsupported for type `def f() -> Unknown`"
|
||||
reveal_type(+f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is unsupported for type `Literal[f]`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is unsupported for type `def f() -> Unknown`"
|
||||
reveal_type(-f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is unsupported for type `Literal[f]`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is unsupported for type `def f() -> Unknown`"
|
||||
reveal_type(~f) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user