Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/ruff_benchmark/benches/ty_walltime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ static STATIC_FRAME: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLo
max_dep_date: "2025-08-09",
python_version: PythonVersion::PY311,
},
600,
630,
)
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1210,11 +1210,7 @@ from typing_extensions import LiteralString

def f(a: Foo, b: list[str], c: list[LiteralString], e):
reveal_type(e) # revealed: Unknown

# TODO: we should select the second overload here and reveal `str`
# (the incorrect result is due to missing logic in protocol subtyping/assignability)
reveal_type(a.join(b)) # revealed: LiteralString

reveal_type(a.join(b)) # revealed: str
reveal_type(a.join(c)) # revealed: LiteralString

# since both overloads match and they have return types that are not equivalent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ from typing import Protocol
import proto_a
import proto_b

# TODO should be error: [invalid-assignment] "Object of type `proto_b.Drawable` is not assignable to `proto_a.Drawable`"
def _(drawable_b: proto_b.Drawable):
# error: [invalid-assignment] "Object of type `proto_b.Drawable` is not assignable to `proto_a.Drawable`"
drawable: proto_a.Drawable = drawable_b
```

Expand Down
12 changes: 4 additions & 8 deletions crates/ty_python_semantic/resources/mdtest/loops/for.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,7 @@ class StrIterator:

def f(x: IntIterator | StrIterator):
for a in x:
# TODO: this should be `int | str` (https://github.com/astral-sh/ty/issues/1089)
reveal_type(a) # revealed: int
reveal_type(a) # revealed: int | str
```

Most real-world iterable types use `Iterator` as the return annotation of their `__iter__` methods:
Expand All @@ -260,14 +259,11 @@ def g(
c: Literal["foo", b"bar"],
):
for x in a:
# TODO: should be `int | str` (https://github.com/astral-sh/ty/issues/1089)
reveal_type(x) # revealed: int
reveal_type(x) # revealed: int | str
for y in b:
# TODO: should be `str | int` (https://github.com/astral-sh/ty/issues/1089)
reveal_type(y) # revealed: str
reveal_type(y) # revealed: str | int
for z in c:
# TODO: should be `LiteralString | int` (https://github.com/astral-sh/ty/issues/1089)
reveal_type(z) # revealed: LiteralString
reveal_type(z) # revealed: LiteralString | int
```

## Union type as iterable where one union element has no `__iter__` method
Expand Down
38 changes: 15 additions & 23 deletions crates/ty_python_semantic/resources/mdtest/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,11 +617,10 @@ static_assert(is_assignable_to(Foo, HasX))
static_assert(not is_subtype_of(Foo, HasXY))
static_assert(not is_assignable_to(Foo, HasXY))

# TODO: these should pass
static_assert(not is_subtype_of(HasXIntSub, HasX)) # error: [static-assert-error]
static_assert(not is_assignable_to(HasXIntSub, HasX)) # error: [static-assert-error]
static_assert(not is_subtype_of(HasX, HasXIntSub)) # error: [static-assert-error]
static_assert(not is_assignable_to(HasX, HasXIntSub)) # error: [static-assert-error]
static_assert(not is_subtype_of(HasXIntSub, HasX))
static_assert(not is_assignable_to(HasXIntSub, HasX))
static_assert(not is_subtype_of(HasX, HasXIntSub))
static_assert(not is_assignable_to(HasX, HasXIntSub))

class FooSub(Foo): ...

Expand Down Expand Up @@ -2291,10 +2290,9 @@ class MethodPUnrelated(Protocol):

static_assert(is_subtype_of(MethodPSub, MethodPSuper))

# TODO: these should pass
static_assert(not is_assignable_to(MethodPUnrelated, MethodPSuper)) # error: [static-assert-error]
static_assert(not is_assignable_to(MethodPSuper, MethodPUnrelated)) # error: [static-assert-error]
static_assert(not is_assignable_to(MethodPSuper, MethodPSub)) # error: [static-assert-error]
static_assert(not is_assignable_to(MethodPUnrelated, MethodPSuper))
static_assert(not is_assignable_to(MethodPSuper, MethodPUnrelated))
static_assert(not is_assignable_to(MethodPSuper, MethodPSub))
```

## Subtyping between protocols with method members and protocols with non-method members
Expand Down Expand Up @@ -2353,8 +2351,7 @@ And for the same reason, they are never assignable to attribute members (which a
class Attribute(Protocol):
f: Callable[[], bool]

# TODO: should pass
static_assert(not is_assignable_to(Method, Attribute)) # error: [static-assert-error]
static_assert(not is_assignable_to(Method, Attribute))
```

Protocols with attribute members, meanwhile, cannot be assigned to protocols with method members,
Expand All @@ -2363,9 +2360,8 @@ this is not true for attribute members. The same principle also applies for prot
members

```py
# TODO: this should pass
static_assert(not is_assignable_to(PropertyBool, Method)) # error: [static-assert-error]
static_assert(not is_assignable_to(Attribute, Method)) # error: [static-assert-error]
static_assert(not is_assignable_to(PropertyBool, Method))
static_assert(not is_assignable_to(Attribute, Method))
```

But an exception to this rule is if an attribute member is marked as `ClassVar`, as this guarantees
Expand All @@ -2384,9 +2380,8 @@ static_assert(is_assignable_to(ClassVarAttribute, Method))
class ClassVarAttributeBad(Protocol):
f: ClassVar[Callable[[], str]]

# TODO: these should pass:
static_assert(not is_subtype_of(ClassVarAttributeBad, Method)) # error: [static-assert-error]
static_assert(not is_assignable_to(ClassVarAttributeBad, Method)) # error: [static-assert-error]
static_assert(not is_subtype_of(ClassVarAttributeBad, Method))
static_assert(not is_assignable_to(ClassVarAttributeBad, Method))
```

## Narrowing of protocols
Expand Down Expand Up @@ -2707,9 +2702,8 @@ class RecursiveNonFullyStatic(Protocol):
parent: RecursiveNonFullyStatic
x: Any

# TODO: these should pass, once we take into account types of members
static_assert(not is_subtype_of(RecursiveFullyStatic, RecursiveNonFullyStatic)) # error: [static-assert-error]
static_assert(not is_subtype_of(RecursiveNonFullyStatic, RecursiveFullyStatic)) # error: [static-assert-error]
static_assert(not is_subtype_of(RecursiveFullyStatic, RecursiveNonFullyStatic))
static_assert(not is_subtype_of(RecursiveNonFullyStatic, RecursiveFullyStatic))

static_assert(is_assignable_to(RecursiveNonFullyStatic, RecursiveNonFullyStatic))
static_assert(is_assignable_to(RecursiveFullyStatic, RecursiveNonFullyStatic))
Expand All @@ -2727,9 +2721,7 @@ class RecursiveOptionalParent(Protocol):
static_assert(is_assignable_to(RecursiveOptionalParent, RecursiveOptionalParent))

# Due to invariance of mutable attribute members, neither is assignable to the other
#
# TODO: should pass
static_assert(not is_assignable_to(RecursiveNonFullyStatic, RecursiveOptionalParent)) # error: [static-assert-error]
static_assert(not is_assignable_to(RecursiveNonFullyStatic, RecursiveOptionalParent))
static_assert(not is_assignable_to(RecursiveOptionalParent, RecursiveNonFullyStatic))

class Other(Protocol):
Expand Down
Loading