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 @@ -170,7 +170,12 @@ from __future__ import annotations
def f(v: int | "Foo"): # fine
reveal_type(v) # revealed: int | Foo

class Foo: ...
class Foo:
def __init__(self):
self.x: "int" | "str" = 42

d = {}
d[0]: "int" | "str" = 42

# error: [unsupported-operator]
X = list["int" | None]
Expand Down
42 changes: 42 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/annotations/union.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,45 @@ X = int | str
def f(y: X):
reveal_type(y) # revealed: int | str
```

## Diagnostics for PEP-604 unions used on Python less than 3.10

<!-- snapshot-diagnostics -->

PEP-604 unions generally don't work on Python \<=3.9:

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

`a.py`:

```py
x: int | str # error: [unsupported-operator]

class Foo:
def __init__(self):
self.x: int | str = 42 # error: [unsupported-operator]

d = {}
d[0]: int | str = 42 # error: [unsupported-operator]
```

But these runtime errors can be avoided if you add `from __future__ import annotations` to the top
of your file:

`b.py`:

```py
from __future__ import annotations

x: int | str

class Foo:
def __init__(self):
self.x: int | str = 42

d = {}
d[0]: int | str = 42
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
source: crates/ty_test/src/lib.rs
expression: snapshot
---

---
mdtest name: union.md - Union - Diagnostics for PEP-604 unions used on Python less than 3.10
mdtest path: crates/ty_python_semantic/resources/mdtest/annotations/union.md
---

# Python source files

## a.py

```
1 | x: int | str # error: [unsupported-operator]
2 |
3 | class Foo:
4 | def __init__(self):
5 | self.x: int | str = 42 # error: [unsupported-operator]
6 |
7 | d = {}
8 | d[0]: int | str = 42 # error: [unsupported-operator]
```

## b.py

```
1 | from __future__ import annotations
2 |
3 | x: int | str
4 |
5 | class Foo:
6 | def __init__(self):
7 | self.x: int | str = 42
8 |
9 | d = {}
10 | d[0]: int | str = 42
```

# Diagnostics

```
error[unsupported-operator]: Unsupported `|` operation
--> src/a.py:1:4
|
1 | x: int | str # error: [unsupported-operator]
| ---^^^---
| | |
| | Has type `<class 'str'>`
| Has type `<class 'int'>`
2 |
3 | class Foo:
|
info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted
info: Python 3.9 was assumed when inferring types because it was specified on the command line
info: rule `unsupported-operator` is enabled by default

```

```
error[unsupported-operator]: Unsupported `|` operation
--> src/a.py:5:17
|
3 | class Foo:
4 | def __init__(self):
5 | self.x: int | str = 42 # error: [unsupported-operator]
| ---^^^---
| | |
| | Has type `<class 'str'>`
| Has type `<class 'int'>`
6 |
7 | d = {}
|
info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted
info: Python 3.9 was assumed when inferring types because it was specified on the command line
info: rule `unsupported-operator` is enabled by default

```

```
error[unsupported-operator]: Unsupported `|` operation
--> src/a.py:8:7
|
7 | d = {}
8 | d[0]: int | str = 42 # error: [unsupported-operator]
| ---^^^---
| | |
| | Has type `<class 'str'>`
| Has type `<class 'int'>`
|
info: PEP 604 `|` unions are only available on Python 3.10+ unless they are quoted
info: Python 3.9 was assumed when inferring types because it was specified on the command line
info: rule `unsupported-operator` is enabled by default

```
6 changes: 4 additions & 2 deletions crates/ty_python_semantic/src/types/infer/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3780,8 +3780,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
target,
simple: _,
} = assignment;
let annotated =
self.infer_annotation_expression(annotation, DeferredExpressionState::None);
let annotated = self.infer_annotation_expression(
annotation,
DeferredExpressionState::from(self.defer_annotations()),
);

if !annotated.qualifiers.is_empty() {
for qualifier in [TypeQualifiers::CLASS_VAR, TypeQualifiers::INIT_VAR] {
Expand Down
Loading