-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[red-knot] Move
UnionBuilder
tests to Markdown (#15374)
## Summary This moves almost all of our existing `UnionBuilder` tests to a Markdown-based test suite. I see how this could be a more controversial change, since these tests where written specifically for `UnionBuilder`, and by creating the union types using Python type expressions, we add an additional layer on top (parsing and inference of these expressions) that moves these tests away from clean unit tests more in the direction of integration tests. Also, there are probably a few implementation details of `UnionBuilder` hidden in the test assertions (e.g. order of union elements after simplifications). That said, I think we would like to see all those properties that are being tested here from *any* implementation of union types. And the Markdown tests come with the usual advantages: - More consice - Better readability - No re-compiliation when working on tests - Easier to add additional explanations and structure to the test suite This changeset adds a few additional tests, but keeps the logic of the existing tests except for a few minor modifications for consistency. --------- Co-authored-by: Alex Waygood <[email protected]> Co-authored-by: T-256 <[email protected]>
- Loading branch information
1 parent
b0905c4
commit b33cf5b
Showing
2 changed files
with
131 additions
and
97 deletions.
There are no files selected for viewing
125 changes: 125 additions & 0 deletions
125
crates/red_knot_python_semantic/resources/mdtest/union_types.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# Union types | ||
|
||
This test suite covers certain basic properties and simplification strategies for union types. | ||
|
||
## Basic unions | ||
|
||
```py | ||
from typing import Literal | ||
|
||
def _(u1: int | str, u2: Literal[0] | Literal[1]) -> None: | ||
reveal_type(u1) # revealed: int | str | ||
reveal_type(u2) # revealed: Literal[0, 1] | ||
``` | ||
|
||
## Duplicate elements are collapsed | ||
|
||
```py | ||
def _(u1: int | int | str, u2: int | str | int) -> None: | ||
reveal_type(u1) # revealed: int | str | ||
reveal_type(u2) # revealed: int | str | ||
``` | ||
|
||
## `Never` is removed | ||
|
||
`Never` is an empty set, a type with no inhabitants. Its presence in a union is always redundant, | ||
and so we eagerly simplify it away. `NoReturn` is equivalent to `Never`. | ||
|
||
```py | ||
from typing_extensions import Never, NoReturn | ||
|
||
def never(u1: int | Never, u2: int | Never | str) -> None: | ||
reveal_type(u1) # revealed: int | ||
reveal_type(u2) # revealed: int | str | ||
|
||
def noreturn(u1: int | NoReturn, u2: int | NoReturn | str) -> None: | ||
reveal_type(u1) # revealed: int | ||
reveal_type(u2) # revealed: int | str | ||
``` | ||
|
||
## Flattening of nested unions | ||
|
||
```py | ||
from typing import Literal | ||
|
||
def _( | ||
u1: (int | str) | bytes, | ||
u2: int | (str | bytes), | ||
u3: int | (str | (bytes | complex)), | ||
) -> None: | ||
reveal_type(u1) # revealed: int | str | bytes | ||
reveal_type(u2) # revealed: int | str | bytes | ||
reveal_type(u3) # revealed: int | str | bytes | complex | ||
``` | ||
|
||
## Simplification using subtyping | ||
|
||
The type `S | T` can be simplified to `T` if `S` is a subtype of `T`: | ||
|
||
```py | ||
from typing_extensions import Literal, LiteralString | ||
|
||
def _( | ||
u1: str | LiteralString, u2: LiteralString | str, u3: Literal["a"] | str | LiteralString, u4: str | bytes | LiteralString | ||
) -> None: | ||
reveal_type(u1) # revealed: str | ||
reveal_type(u2) # revealed: str | ||
reveal_type(u3) # revealed: str | ||
reveal_type(u4) # revealed: str | bytes | ||
``` | ||
|
||
## Boolean literals | ||
|
||
The union `Literal[True] | Literal[False]` is exactly equivalent to `bool`: | ||
|
||
```py | ||
from typing import Literal | ||
|
||
def _( | ||
u1: Literal[True, False], | ||
u2: bool | Literal[True], | ||
u3: Literal[True] | bool, | ||
u4: Literal[True] | Literal[True, 17], | ||
u5: Literal[True, False, True, 17], | ||
) -> None: | ||
reveal_type(u1) # revealed: bool | ||
reveal_type(u2) # revealed: bool | ||
reveal_type(u3) # revealed: bool | ||
reveal_type(u4) # revealed: Literal[True, 17] | ||
reveal_type(u5) # revealed: bool | Literal[17] | ||
``` | ||
|
||
## Do not erase `Unknown` | ||
|
||
```py | ||
from knot_extensions import Unknown | ||
|
||
def _(u1: Unknown | str, u2: str | Unknown) -> None: | ||
reveal_type(u1) # revealed: Unknown | str | ||
reveal_type(u2) # revealed: str | Unknown | ||
``` | ||
|
||
## Collapse multiple `Unknown`s | ||
|
||
Since `Unknown` is a gradual type, it is not a subtype of anything, but multiple `Unknown`s in a | ||
union are still redundant: | ||
|
||
```py | ||
from knot_extensions import Unknown | ||
|
||
def _(u1: Unknown | Unknown | str, u2: Unknown | str | Unknown, u3: str | Unknown | Unknown) -> None: | ||
reveal_type(u1) # revealed: Unknown | str | ||
reveal_type(u2) # revealed: Unknown | str | ||
reveal_type(u3) # revealed: str | Unknown | ||
``` | ||
|
||
## Subsume multiple elements | ||
|
||
Simplifications still apply when `Unknown` is present. | ||
|
||
```py | ||
from knot_extensions import Unknown | ||
|
||
def _(u1: str | Unknown | int | object): | ||
reveal_type(u1) # revealed: Unknown | object | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters