mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:18 +00:00
Merge branch 'main' into dcreager/real-constraint-sets
* main: (21 commits) [ty] Literal promotion refactor (#20646) [ty] Add tests for nested generic functions (#20631) [`cli`] Add conflict between `--add-noqa` and `--diff` options (#20642) [ty] Ensure first-party search paths always appear in a sensible order (#20629) [ty] Use `typing.Self` for the first parameter of instance methods (#20517) [ty] Remove unnecessary `parsed_module()` calls (#20630) Remove `TextEmitter` (#20595) [ty] Use fully qualified names to distinguish ambiguous protocols in diagnostics (#20627) [ty] Ecosystem analyzer: relax timeout thresholds (#20626) [ty] Apply type mappings to functions eagerly (#20596) [ty] Improve disambiguation of class names in diagnostics (#20603) Add the *The Basics* title back to CONTRIBUTING.md (#20624) [`playground`] Fix quick fixes for empty ranges in playground (#20599) Update dependency ruff to v0.13.2 (#20622) [`ruff`] Fix minor typos in doc comments (#20623) Update dependency PyYAML to v6.0.3 (#20621) Update cargo-bins/cargo-binstall action to v1.15.6 (#20620) Fixed documentation for try_consider_else (#20587) [ty] Use `Top` materializations for `TypeIs` special form (#20591) [ty] Simplify `Any | (Any & T)` to `Any` (#20593) ...
This commit is contained in:
commit
06aed22426
113 changed files with 2440 additions and 1129 deletions
|
@ -33,11 +33,6 @@ class Shape:
|
|||
reveal_type(x) # revealed: Self@nested_func_without_enclosing_binding
|
||||
inner(self)
|
||||
|
||||
def implicit_self(self) -> Self:
|
||||
# TODO: first argument in a method should be considered as "typing.Self"
|
||||
reveal_type(self) # revealed: Unknown
|
||||
return self
|
||||
|
||||
reveal_type(Shape().nested_type()) # revealed: list[Shape]
|
||||
reveal_type(Shape().nested_func()) # revealed: Shape
|
||||
|
||||
|
@ -53,6 +48,150 @@ class Outer:
|
|||
return self
|
||||
```
|
||||
|
||||
## Type of (unannotated) `self` parameters
|
||||
|
||||
In instance methods, the first parameter (regardless of its name) is assumed to have the type
|
||||
`typing.Self`, unless it has an explicit annotation. This does not apply to `@classmethod` and
|
||||
`@staticmethod`s.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.11"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import Self
|
||||
|
||||
class A:
|
||||
def implicit_self(self) -> Self:
|
||||
# TODO: This should be Self@implicit_self
|
||||
reveal_type(self) # revealed: Unknown
|
||||
|
||||
return self
|
||||
|
||||
def a_method(self) -> int:
|
||||
def first_arg_is_not_self(a: int) -> int:
|
||||
reveal_type(a) # revealed: int
|
||||
return a
|
||||
return first_arg_is_not_self(1)
|
||||
|
||||
@classmethod
|
||||
def a_classmethod(cls) -> Self:
|
||||
# TODO: This should be type[Self@bar]
|
||||
reveal_type(cls) # revealed: Unknown
|
||||
return cls()
|
||||
|
||||
@staticmethod
|
||||
def a_staticmethod(x: int): ...
|
||||
|
||||
a = A()
|
||||
|
||||
reveal_type(a.implicit_self()) # revealed: A
|
||||
reveal_type(a.implicit_self) # revealed: bound method A.implicit_self() -> A
|
||||
```
|
||||
|
||||
Calling an instance method explicitly verifies the first argument:
|
||||
|
||||
```py
|
||||
A.implicit_self(a)
|
||||
|
||||
# error: [invalid-argument-type] "Argument to function `implicit_self` is incorrect: Argument type `Literal[1]` does not satisfy upper bound `A` of type variable `Self`"
|
||||
A.implicit_self(1)
|
||||
```
|
||||
|
||||
Passing `self` implicitly also verifies the type:
|
||||
|
||||
```py
|
||||
from typing import Never
|
||||
|
||||
class Strange:
|
||||
def can_not_be_called(self: Never) -> None: ...
|
||||
|
||||
# error: [invalid-argument-type] "Argument to bound method `can_not_be_called` is incorrect: Expected `Never`, found `Strange`"
|
||||
Strange().can_not_be_called()
|
||||
```
|
||||
|
||||
If the method is a class or static method then first argument is not inferred as `Self`:
|
||||
|
||||
```py
|
||||
A.a_classmethod()
|
||||
A.a_classmethod(a) # error: [too-many-positional-arguments]
|
||||
A.a_staticmethod(1)
|
||||
a.a_staticmethod(1)
|
||||
A.a_staticmethod(a) # error: [invalid-argument-type]
|
||||
```
|
||||
|
||||
The first parameter of instance methods always has type `Self`, if it is not explicitly annotated.
|
||||
The name `self` is not special in any way.
|
||||
|
||||
```py
|
||||
class B:
|
||||
def name_does_not_matter(this) -> Self:
|
||||
# TODO: Should reveal Self@name_does_not_matter
|
||||
reveal_type(this) # revealed: Unknown
|
||||
|
||||
return this
|
||||
|
||||
def positional_only(self, /, x: int) -> Self:
|
||||
# TODO: Should reveal Self@positional_only
|
||||
reveal_type(self) # revealed: Unknown
|
||||
return self
|
||||
|
||||
def keyword_only(self, *, x: int) -> Self:
|
||||
# TODO: Should reveal Self@keyword_only
|
||||
reveal_type(self) # revealed: Unknown
|
||||
return self
|
||||
|
||||
@property
|
||||
def a_property(self) -> Self:
|
||||
# TODO: Should reveal Self@a_property
|
||||
reveal_type(self) # revealed: Unknown
|
||||
return self
|
||||
|
||||
reveal_type(B().name_does_not_matter()) # revealed: B
|
||||
reveal_type(B().positional_only(1)) # revealed: B
|
||||
reveal_type(B().keyword_only(x=1)) # revealed: B
|
||||
|
||||
# TODO: this should be B
|
||||
reveal_type(B().a_property) # revealed: Unknown
|
||||
```
|
||||
|
||||
This also works for generic classes:
|
||||
|
||||
```py
|
||||
from typing import Self, Generic, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class G(Generic[T]):
|
||||
def id(self) -> Self:
|
||||
# TODO: Should reveal Self@id
|
||||
reveal_type(self) # revealed: Unknown
|
||||
|
||||
return self
|
||||
|
||||
reveal_type(G[int]().id()) # revealed: G[int]
|
||||
reveal_type(G[str]().id()) # revealed: G[str]
|
||||
```
|
||||
|
||||
Free functions and nested functions do not use implicit `Self`:
|
||||
|
||||
```py
|
||||
def not_a_method(self):
|
||||
reveal_type(self) # revealed: Unknown
|
||||
|
||||
# error: [invalid-type-form]
|
||||
def does_not_return_self(self) -> Self:
|
||||
return self
|
||||
|
||||
class C:
|
||||
def outer(self) -> None:
|
||||
def inner(self):
|
||||
reveal_type(self) # revealed: Unknown
|
||||
|
||||
reveal_type(not_a_method) # revealed: def not_a_method(self) -> Unknown
|
||||
```
|
||||
|
||||
## typing_extensions
|
||||
|
||||
```toml
|
||||
|
@ -208,6 +347,47 @@ class MyMetaclass(type):
|
|||
return super().__new__(cls)
|
||||
```
|
||||
|
||||
## Explicit annotations override implicit `Self`
|
||||
|
||||
If the first parameter is explicitly annotated, that annotation takes precedence over the implicit
|
||||
`Self` type.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import final
|
||||
|
||||
@final
|
||||
class Disjoint: ...
|
||||
|
||||
class Explicit:
|
||||
# TODO: We could emit a warning if the annotated type of `self` is disjoint from `Explicit`
|
||||
def bad(self: Disjoint) -> None:
|
||||
reveal_type(self) # revealed: Disjoint
|
||||
|
||||
def forward(self: Explicit) -> None:
|
||||
reveal_type(self) # revealed: Explicit
|
||||
|
||||
# error: [invalid-argument-type] "Argument to bound method `bad` is incorrect: Expected `Disjoint`, found `Explicit`"
|
||||
Explicit().bad()
|
||||
|
||||
Explicit().forward()
|
||||
|
||||
class ExplicitGeneric[T]:
|
||||
def special(self: ExplicitGeneric[int]) -> None:
|
||||
reveal_type(self) # revealed: ExplicitGeneric[int]
|
||||
|
||||
ExplicitGeneric[int]().special()
|
||||
|
||||
# TODO: this should be an `invalid-argument-type` error
|
||||
ExplicitGeneric[str]().special()
|
||||
```
|
||||
|
||||
## Binding a method fixes `Self`
|
||||
|
||||
When a method is bound, any instances of `Self` in its signature are "fixed", since we now know the
|
||||
|
|
|
@ -69,7 +69,9 @@ reveal_type(bound_method(1)) # revealed: str
|
|||
When we call the function object itself, we need to pass the `instance` explicitly:
|
||||
|
||||
```py
|
||||
C.f(1) # error: [missing-argument]
|
||||
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `C`, found `Literal[1]`"
|
||||
# error: [missing-argument]
|
||||
C.f(1)
|
||||
|
||||
reveal_type(C.f(C(), 1)) # revealed: str
|
||||
```
|
||||
|
|
|
@ -431,6 +431,8 @@ def _(flag: bool):
|
|||
reveal_type(C7.union_of_class_data_descriptor_and_attribute) # revealed: Literal["data", 2]
|
||||
|
||||
C7.union_of_metaclass_attributes = 2 if flag else 1
|
||||
# TODO: https://github.com/astral-sh/ty/issues/1163
|
||||
# error: [invalid-assignment]
|
||||
C7.union_of_metaclass_data_descriptor_and_attribute = 2 if flag else 100
|
||||
C7.union_of_class_attributes = 2 if flag else 1
|
||||
C7.union_of_class_data_descriptor_and_attribute = 2 if flag else DataDescriptor()
|
||||
|
|
|
@ -43,8 +43,7 @@ import b
|
|||
df: a.DataFrame = b.DataFrame() # error: [invalid-assignment] "Object of type `b.DataFrame` is not assignable to `a.DataFrame`"
|
||||
|
||||
def _(dfs: list[b.DataFrame]):
|
||||
# TODO should be"Object of type `list[b.DataFrame]` is not assignable to `list[a.DataFrame]`
|
||||
# error: [invalid-assignment] "Object of type `list[DataFrame]` is not assignable to `list[DataFrame]`"
|
||||
# error: [invalid-assignment] "Object of type `list[b.DataFrame]` is not assignable to `list[a.DataFrame]`"
|
||||
dataframes: list[a.DataFrame] = dfs
|
||||
```
|
||||
|
||||
|
@ -171,6 +170,36 @@ class Container(Generic[T]):
|
|||
|
||||
## Protocols
|
||||
|
||||
### Differing members
|
||||
|
||||
`bad.py`:
|
||||
|
||||
```py
|
||||
from typing import Protocol, TypeVar
|
||||
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
|
||||
class Iterator(Protocol[T_co]):
|
||||
def __nexxt__(self) -> T_co: ...
|
||||
|
||||
def bad() -> Iterator[str]:
|
||||
raise NotImplementedError
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
from typing import Iterator
|
||||
|
||||
def f() -> Iterator[str]:
|
||||
import bad
|
||||
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `typing.Iterator[str]`, found `bad.Iterator[str]"
|
||||
return bad.bad()
|
||||
```
|
||||
|
||||
### Same members but with different types
|
||||
|
||||
```py
|
||||
from typing import Protocol
|
||||
import proto_a
|
||||
|
@ -228,3 +257,21 @@ from typing import TypedDict
|
|||
class Person(TypedDict):
|
||||
name: bytes
|
||||
```
|
||||
|
||||
## Tuple specializations
|
||||
|
||||
`module.py`:
|
||||
|
||||
```py
|
||||
class Model: ...
|
||||
```
|
||||
|
||||
```py
|
||||
class Model: ...
|
||||
|
||||
def get_models_tuple() -> tuple[Model]:
|
||||
from module import Model
|
||||
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `tuple[mdtest_snippet.Model]`, found `tuple[module.Model]`"
|
||||
return (Model(),)
|
||||
```
|
||||
|
|
|
@ -562,17 +562,17 @@ class C(Generic[T]):
|
|||
return u
|
||||
|
||||
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
||||
reveal_type(generic_context(C.method)) # revealed: None
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||
reveal_type(generic_context(C[int])) # revealed: None
|
||||
reveal_type(generic_context(C[int].method)) # revealed: None
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||
|
||||
c: C[int] = C[int]()
|
||||
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
||||
reveal_type(generic_context(c)) # revealed: None
|
||||
reveal_type(generic_context(c.method)) # revealed: None
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||
```
|
||||
|
||||
## Specializations propagate
|
||||
|
|
|
@ -464,6 +464,7 @@ def f(x: str):
|
|||
from typing import TypeVar, overload
|
||||
|
||||
T = TypeVar("T")
|
||||
S = TypeVar("S")
|
||||
|
||||
def outer(t: T) -> None:
|
||||
def inner(t: T) -> None: ...
|
||||
|
@ -479,6 +480,13 @@ def overloaded_outer(t: T | None = None) -> None:
|
|||
|
||||
if t is not None:
|
||||
inner(t)
|
||||
|
||||
def outer(t: T) -> None:
|
||||
def inner(inner_t: T, s: S) -> tuple[T, S]:
|
||||
return inner_t, s
|
||||
reveal_type(inner(t, 1)) # revealed: tuple[T@outer, Literal[1]]
|
||||
|
||||
inner("wrong", 1) # error: [invalid-argument-type]
|
||||
```
|
||||
|
||||
## Unpacking a TypeVar
|
||||
|
|
|
@ -504,17 +504,17 @@ class C[T]:
|
|||
def cannot_shadow_class_typevar[T](self, t: T): ...
|
||||
|
||||
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
||||
reveal_type(generic_context(C.method)) # revealed: None
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||
reveal_type(generic_context(C[int])) # revealed: None
|
||||
reveal_type(generic_context(C[int].method)) # revealed: None
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||
|
||||
c: C[int] = C[int]()
|
||||
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
||||
reveal_type(generic_context(c)) # revealed: None
|
||||
reveal_type(generic_context(c.method)) # revealed: None
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
|
||||
```
|
||||
|
||||
## Specializations propagate
|
||||
|
|
|
@ -474,6 +474,13 @@ def overloaded_outer[T](t: T | None = None) -> None:
|
|||
|
||||
if t is not None:
|
||||
inner(t)
|
||||
|
||||
def outer[T](t: T) -> None:
|
||||
def inner[S](inner_t: T, s: S) -> tuple[T, S]:
|
||||
return inner_t, s
|
||||
reveal_type(inner(t, 1)) # revealed: tuple[T@outer, Literal[1]]
|
||||
|
||||
inner("wrong", 1) # error: [invalid-argument-type]
|
||||
```
|
||||
|
||||
## Unpacking a TypeVar
|
||||
|
@ -534,6 +541,5 @@ class C:
|
|||
def _(x: int):
|
||||
reveal_type(C().explicit_self(x)) # revealed: tuple[C, int]
|
||||
|
||||
# TODO: this should be `tuple[C, int]` as well, once we support implicit `self`
|
||||
reveal_type(C().implicit_self(x)) # revealed: tuple[Unknown, int]
|
||||
reveal_type(C().implicit_self(x)) # revealed: tuple[C, int]
|
||||
```
|
||||
|
|
|
@ -117,6 +117,7 @@ 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
|
||||
|
||||
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Argument type `Literal[1]` does not satisfy upper bound `C[T@C]` of type variable `Self`"
|
||||
C[int].f(1) # error: [missing-argument]
|
||||
reveal_type(C[int].f(C[int](), 1)) # revealed: str
|
||||
|
||||
|
@ -154,7 +155,7 @@ from ty_extensions import generic_context
|
|||
legacy.m("string", None) # error: [invalid-argument-type]
|
||||
reveal_type(legacy.m) # revealed: bound method Legacy[int].m[S](x: int, y: S@m) -> S@m
|
||||
reveal_type(generic_context(Legacy)) # revealed: tuple[T@Legacy]
|
||||
reveal_type(generic_context(legacy.m)) # revealed: tuple[S@m]
|
||||
reveal_type(generic_context(legacy.m)) # revealed: tuple[Self@m, S@m]
|
||||
```
|
||||
|
||||
With PEP 695 syntax, it is clearer that the method uses a separate typevar:
|
||||
|
|
|
@ -278,8 +278,7 @@ reveal_type(Person._make(("Alice", 42))) # revealed: Unknown
|
|||
person = Person("Alice", 42)
|
||||
|
||||
reveal_type(person._asdict()) # revealed: dict[str, Any]
|
||||
# TODO: should be `Person` once we support implicit type of `self`
|
||||
reveal_type(person._replace(name="Bob")) # revealed: Unknown
|
||||
reveal_type(person._replace(name="Bob")) # revealed: Person
|
||||
```
|
||||
|
||||
When accessing them on child classes of generic `NamedTuple`s, the return type is specialized
|
||||
|
@ -296,8 +295,7 @@ class Box(NamedTuple, Generic[T]):
|
|||
class IntBox(Box[int]):
|
||||
pass
|
||||
|
||||
# TODO: should be `IntBox` once we support the implicit type of `self`
|
||||
reveal_type(IntBox(1)._replace(content=42)) # revealed: Unknown
|
||||
reveal_type(IntBox(1)._replace(content=42)) # revealed: IntBox
|
||||
```
|
||||
|
||||
## `collections.namedtuple`
|
||||
|
|
|
@ -324,8 +324,7 @@ a covariant generic, this is equivalent to using the upper bound of the type par
|
|||
from typing import Self
|
||||
|
||||
class Covariant[T]:
|
||||
# TODO: remove the explicit `Self` annotation, once we support the implicit type of `self`
|
||||
def get(self: Self) -> T:
|
||||
def get(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
def _(x: object):
|
||||
|
@ -338,8 +337,7 @@ Similarly, contravariant type parameters use their lower bound of `Never`:
|
|||
|
||||
```py
|
||||
class Contravariant[T]:
|
||||
# TODO: remove the explicit `Self` annotation, once we support the implicit type of `self`
|
||||
def push(self: Self, x: T) -> None: ...
|
||||
def push(self, x: T) -> None: ...
|
||||
|
||||
def _(x: object):
|
||||
if isinstance(x, Contravariant):
|
||||
|
@ -354,10 +352,8 @@ the type system, so we represent it with the internal `Top[]` special form.
|
|||
|
||||
```py
|
||||
class Invariant[T]:
|
||||
# TODO: remove the explicit `Self` annotation, once we support the implicit type of `self`
|
||||
def push(self: Self, x: T) -> None: ...
|
||||
# TODO: remove the explicit `Self` annotation, once we support the implicit type of `self`
|
||||
def get(self: Self) -> T:
|
||||
def push(self, x: T) -> None: ...
|
||||
def get(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
def _(x: object):
|
||||
|
|
|
@ -173,6 +173,11 @@ def _(d: Any):
|
|||
|
||||
## Narrowing
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
from typing_extensions import TypeGuard, TypeIs
|
||||
|
@ -295,6 +300,38 @@ def _(a: Foo):
|
|||
reveal_type(a) # revealed: Foo & Bar
|
||||
```
|
||||
|
||||
For generics, we transform the argument passed into `TypeIs[]` from `X` to `Top[X]`. This helps
|
||||
especially when using various functions from typeshed that are annotated as returning
|
||||
`TypeIs[SomeCovariantGeneric[Any]]` to avoid false positives in other type checkers. For ty's
|
||||
purposes, it would usually lead to more intuitive results if `object` was used as the specialization
|
||||
for a covariant generic inside the `TypeIs` special form, but this is mitigated by our implicit
|
||||
transformation from `TypeIs[SomeCovariantGeneric[Any]]` to `TypeIs[Top[SomeCovariantGeneric[Any]]]`
|
||||
(which just simplifies to `TypeIs[SomeCovariantGeneric[object]]`).
|
||||
|
||||
```py
|
||||
class Unrelated: ...
|
||||
|
||||
class Covariant[T]:
|
||||
def get(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
def is_instance_of_covariant(arg: object) -> TypeIs[Covariant[Any]]:
|
||||
return isinstance(arg, Covariant)
|
||||
|
||||
def needs_instance_of_unrelated(arg: Unrelated):
|
||||
pass
|
||||
|
||||
def _(x: Unrelated | Covariant[int]):
|
||||
if is_instance_of_covariant(x):
|
||||
raise RuntimeError("oh no")
|
||||
|
||||
reveal_type(x) # revealed: Unrelated & ~Covariant[object]
|
||||
|
||||
# We would emit a false-positive diagnostic here if we didn't implicitly transform
|
||||
# `TypeIs[Covariant[Any]]` to `TypeIs[Covariant[object]]`
|
||||
needs_instance_of_unrelated(x)
|
||||
```
|
||||
|
||||
## `TypeGuard` special cases
|
||||
|
||||
```py
|
||||
|
|
|
@ -325,7 +325,7 @@ type A = list[Union["A", str]]
|
|||
def f(x: A):
|
||||
reveal_type(x) # revealed: list[A | str]
|
||||
for item in x:
|
||||
reveal_type(item) # revealed: list[A | str] | str
|
||||
reveal_type(item) # revealed: list[Any | str] | str
|
||||
```
|
||||
|
||||
#### With new-style union
|
||||
|
@ -336,7 +336,7 @@ type A = list["A" | str]
|
|||
def f(x: A):
|
||||
reveal_type(x) # revealed: list[A | str]
|
||||
for item in x:
|
||||
reveal_type(item) # revealed: list[A | str] | str
|
||||
reveal_type(item) # revealed: list[Any | str] | str
|
||||
```
|
||||
|
||||
#### With Optional
|
||||
|
@ -349,7 +349,7 @@ type A = list[Optional[Union["A", str]]]
|
|||
def f(x: A):
|
||||
reveal_type(x) # revealed: list[A | str | None]
|
||||
for item in x:
|
||||
reveal_type(item) # revealed: list[A | str | None] | str | None
|
||||
reveal_type(item) # revealed: list[Any | str | None] | str | None
|
||||
```
|
||||
|
||||
### Tuple comparison
|
||||
|
|
|
@ -893,8 +893,10 @@ class LotsOfBindings(Protocol):
|
|||
match object():
|
||||
case l: # error: [ambiguous-protocol-member]
|
||||
...
|
||||
# error: [ambiguous-protocol-member] "Consider adding an annotation, e.g. `m: int | str = ...`"
|
||||
m = 1 if 1.2 > 3.4 else "a"
|
||||
|
||||
# revealed: frozenset[Literal["Nested", "NestedProtocol", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]]
|
||||
# revealed: frozenset[Literal["Nested", "NestedProtocol", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"]]
|
||||
reveal_type(get_protocol_members(LotsOfBindings))
|
||||
|
||||
class Foo(Protocol):
|
||||
|
@ -1977,12 +1979,12 @@ from typing_extensions import TypeVar, Self, Protocol
|
|||
from ty_extensions import is_equivalent_to, static_assert, is_assignable_to, is_subtype_of
|
||||
|
||||
class NewStyleClassScoped[T](Protocol):
|
||||
def method(self: Self, input: T) -> None: ...
|
||||
def method(self, input: T) -> None: ...
|
||||
|
||||
S = TypeVar("S")
|
||||
|
||||
class LegacyClassScoped(Protocol[S]):
|
||||
def method(self: Self, input: S) -> None: ...
|
||||
def method(self, input: S) -> None: ...
|
||||
|
||||
# TODO: these should pass
|
||||
static_assert(is_equivalent_to(NewStyleClassScoped, LegacyClassScoped)) # error: [static-assert-error]
|
||||
|
|
|
@ -339,7 +339,7 @@ class A: ...
|
|||
|
||||
def f(x: A):
|
||||
# TODO: no error
|
||||
# error: [invalid-assignment] "Object of type `A | A` is not assignable to `A`"
|
||||
# error: [invalid-assignment] "Object of type `mdtest_snippet.A | mdtest_snippet.A` is not assignable to `mdtest_snippet.A`"
|
||||
x = A()
|
||||
```
|
||||
|
||||
|
|
|
@ -133,6 +133,11 @@ class Single(Enum):
|
|||
VALUE = 1
|
||||
|
||||
static_assert(is_equivalent_to(P | Q | Single, Literal[Single.VALUE] | Q | P))
|
||||
|
||||
static_assert(is_equivalent_to(Any, Any | Intersection[Any, str]))
|
||||
static_assert(is_equivalent_to(Any, Intersection[str, Any] | Any))
|
||||
static_assert(is_equivalent_to(Any, Any | Intersection[Any, Not[None]]))
|
||||
static_assert(is_equivalent_to(Any, Intersection[Not[None], Any] | Any))
|
||||
```
|
||||
|
||||
## Tuples
|
||||
|
|
|
@ -1948,8 +1948,6 @@ static_assert(is_subtype_of(TypeOf[A.g], Callable[[int], int]))
|
|||
static_assert(not is_subtype_of(TypeOf[a.f], Callable[[float], int]))
|
||||
static_assert(not is_subtype_of(TypeOf[A.g], Callable[[], int]))
|
||||
|
||||
# TODO: This assertion should be true
|
||||
# error: [static-assert-error] "Static assertion error: argument of type `ty_extensions.ConstraintSet[never]` is statically known to be falsy"
|
||||
static_assert(is_subtype_of(TypeOf[A.f], Callable[[A, int], int]))
|
||||
```
|
||||
|
||||
|
|
|
@ -657,16 +657,14 @@ alice: Employee = {"name": "Alice", "employee_id": 1}
|
|||
eve: Employee = {"name": "Eve"}
|
||||
|
||||
def combine(p: Person, e: Employee):
|
||||
# TODO: Should be `Person` once we support the implicit type of self
|
||||
reveal_type(p.copy()) # revealed: Unknown
|
||||
# TODO: Should be `Employee` once we support the implicit type of self
|
||||
reveal_type(e.copy()) # revealed: Unknown
|
||||
reveal_type(p.copy()) # revealed: Person
|
||||
reveal_type(e.copy()) # revealed: Employee
|
||||
|
||||
reveal_type(p | p) # revealed: Person
|
||||
reveal_type(e | e) # revealed: Employee
|
||||
|
||||
# TODO: Should be `Person` once we support the implicit type of self and subtyping for TypedDicts
|
||||
reveal_type(p | e) # revealed: Employee
|
||||
# TODO: Should be `Person` once we support subtyping for TypedDicts
|
||||
reveal_type(p | e) # revealed: Person | Employee
|
||||
```
|
||||
|
||||
When inheriting from a `TypedDict` with a different `total` setting, inherited fields maintain their
|
||||
|
|
|
@ -254,8 +254,7 @@ async def long_running_task():
|
|||
|
||||
async def main():
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
# TODO: should be `TaskGroup`
|
||||
reveal_type(tg) # revealed: Unknown
|
||||
reveal_type(tg) # revealed: TaskGroup
|
||||
|
||||
tg.create_task(long_running_task())
|
||||
```
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue