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
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,13 @@ simplify to `Never`, even in the presence of other types:

```py
from ty_extensions import Intersection, Not
from typing import Any
from typing import Any, Generic, TypeVar

T_co = TypeVar("T_co", covariant=True)

class P: ...
class Q: ...
class R(Generic[T_co]): ...

def _(
i1: Intersection[P, Not[P]],
Expand All @@ -317,13 +320,17 @@ def _(
i4: Intersection[Not[P], Q, P],
i5: Intersection[P, Any, Not[P]],
i6: Intersection[Not[P], Any, P],
i7: Intersection[R[P], Not[R[P]]],
i8: Intersection[R[P], Not[R[Q]]],
) -> None:
reveal_type(i1) # revealed: Never
reveal_type(i2) # revealed: Never
reveal_type(i3) # revealed: Never
reveal_type(i4) # revealed: Never
reveal_type(i5) # revealed: Never
reveal_type(i6) # revealed: Never
reveal_type(i7) # revealed: Never
reveal_type(i8) # revealed: R[P] & ~R[Q]
```

### Union of a type and its negation
Expand All @@ -332,20 +339,28 @@ Similarly, if we have both `P` and `~P` in a _union_, we can simplify that to `o

```py
from ty_extensions import Intersection, Not
from typing import Generic, TypeVar

T_co = TypeVar("T_co", covariant=True)

class P: ...
class Q: ...
class R(Generic[T_co]): ...

def _(
i1: P | Not[P],
i2: Not[P] | P,
i3: P | Q | Not[P],
i4: Not[P] | Q | P,
i5: R[P] | Not[R[P]],
i6: R[P] | Not[R[Q]],
) -> None:
reveal_type(i1) # revealed: object
reveal_type(i2) # revealed: object
reveal_type(i3) # revealed: object
reveal_type(i4) # revealed: object
reveal_type(i5) # revealed: object
reveal_type(i6) # revealed: R[P] | ~R[Q]
```

### Negation is an involution
Expand Down
3 changes: 1 addition & 2 deletions crates/ty_python_semantic/resources/mdtest/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -902,8 +902,7 @@ from ty_extensions import is_subtype_of, is_assignable_to, static_assert, TypeOf
class HasX(Protocol):
x: int

# TODO: this should pass
static_assert(is_subtype_of(TypeOf[module], HasX)) # error: [static-assert-error]
static_assert(is_subtype_of(TypeOf[module], HasX))
static_assert(is_assignable_to(TypeOf[module], HasX))

class ExplicitProtocolSubtype(HasX, Protocol):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,34 @@ class AnyMeta(metaclass=Any): ...
static_assert(is_assignable_to(type[AnyMeta], type))
static_assert(is_assignable_to(type[AnyMeta], type[object]))
static_assert(is_assignable_to(type[AnyMeta], type[Any]))

from typing import TypeVar, Generic, Any

T_co = TypeVar("T_co", covariant=True)

class Foo(Generic[T_co]): ...
class Bar(Foo[T_co], Generic[T_co]): ...

static_assert(is_assignable_to(TypeOf[Bar[int]], type[Foo[int]]))
static_assert(is_assignable_to(TypeOf[Bar[bool]], type[Foo[int]]))
static_assert(is_assignable_to(TypeOf[Bar], type[Foo[int]]))
static_assert(is_assignable_to(TypeOf[Bar[Any]], type[Foo[int]]))
static_assert(is_assignable_to(TypeOf[Bar], type[Foo]))
static_assert(is_assignable_to(TypeOf[Bar[Any]], type[Foo[Any]]))
static_assert(is_assignable_to(TypeOf[Bar[Any]], type[Foo[int]]))

# TODO: these should pass (all subscripts inside `type[]` type expressions are currently TODO types)
static_assert(not is_assignable_to(TypeOf[Bar[int]], type[Foo[bool]])) # error: [static-assert-error]
static_assert(not is_assignable_to(TypeOf[Foo[bool]], type[Bar[int]])) # error: [static-assert-error]
```

## `type[]` is not assignable to types disjoint from `builtins.type`

```py
from typing import Any
from ty_extensions import is_assignable_to, static_assert

static_assert(not is_assignable_to(type[Any], None))
```

## Class-literals that inherit from `Any`
Expand Down Expand Up @@ -717,6 +745,53 @@ def f(x: int, y: str) -> None: ...
c1: Callable[[int], None] = partial(f, y="a")
```

### Generic classes with `__call__`

```toml
[environment]
python-version = "3.12"
```

```py
from typing_extensions import Callable, Any, Generic, TypeVar, ParamSpec
from ty_extensions import static_assert, is_assignable_to

T = TypeVar("T")
P = ParamSpec("P")

class Foo[T]:
def __call__(self): ...

class FooLegacy(Generic[T]):
def __call__(self): ...

class Bar[T, **P]:
def __call__(self): ...

# TODO: should not error
class BarLegacy(Generic[T, P]): # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"
def __call__(self): ...

static_assert(is_assignable_to(Foo, Callable[..., Any]))
static_assert(is_assignable_to(FooLegacy, Callable[..., Any]))
static_assert(is_assignable_to(Bar, Callable[..., Any]))
static_assert(is_assignable_to(BarLegacy, Callable[..., Any]))

class Spam[T]: ...
class SpamLegacy(Generic[T]): ...
class Eggs[T, **P]: ...

# TODO: should not error
class EggsLegacy(Generic[T, P]): ... # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"

static_assert(not is_assignable_to(Spam, Callable[..., Any]))
static_assert(not is_assignable_to(SpamLegacy, Callable[..., Any]))
static_assert(not is_assignable_to(Eggs, Callable[..., Any]))

# TODO: should pass
static_assert(not is_assignable_to(EggsLegacy, Callable[..., Any])) # error: [static-assert-error]
```

### Classes with `__call__` as attribute

An instance type is assignable to a compatible callable type if the instance type's class has a
Expand Down
Loading
Loading