Skip to content

Commit ff19629

Browse files
authored
Understand typing.Optional in annotations (#14397)
1 parent cd80c9d commit ff19629

File tree

4 files changed

+61
-4
lines changed

4 files changed

+61
-4
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Optional
2+
3+
## Annotation
4+
5+
`typing.Optional` is equivalent to using the type with a None in a Union.
6+
7+
```py
8+
from typing import Optional
9+
10+
a: Optional[int]
11+
a1: Optional[bool]
12+
a2: Optional[Optional[bool]]
13+
a3: Optional[None]
14+
15+
def f():
16+
# revealed: int | None
17+
reveal_type(a)
18+
# revealed: bool | None
19+
reveal_type(a1)
20+
# revealed: bool | None
21+
reveal_type(a2)
22+
# revealed: None
23+
reveal_type(a3)
24+
```
25+
26+
## Assignment
27+
28+
```py
29+
from typing import Optional
30+
31+
a: Optional[int] = 1
32+
a = None
33+
# error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int | None`"
34+
a = ""
35+
```
36+
37+
## Typing Extensions
38+
39+
```py
40+
from typing_extensions import Optional
41+
42+
a: Optional[int]
43+
44+
def f():
45+
# revealed: int | None
46+
reveal_type(a)
47+
```

crates/red_knot_python_semantic/src/types.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,8 @@ impl<'db> KnownClass {
18071807
pub enum KnownInstanceType<'db> {
18081808
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
18091809
Literal,
1810+
/// The symbol `typing.Optional` (which can also be found as `typing_extensions.Literal`)
1811+
Optional,
18101812
/// A single instance of `typing.TypeVar`
18111813
TypeVar(TypeVarInstance<'db>),
18121814
// TODO: fill this enum out with more special forms, etc.
@@ -1816,22 +1818,23 @@ impl<'db> KnownInstanceType<'db> {
18161818
pub const fn as_str(self) -> &'static str {
18171819
match self {
18181820
KnownInstanceType::Literal => "Literal",
1821+
KnownInstanceType::Optional => "Optional",
18191822
KnownInstanceType::TypeVar(_) => "TypeVar",
18201823
}
18211824
}
18221825

18231826
/// Evaluate the known instance in boolean context
18241827
pub const fn bool(self) -> Truthiness {
18251828
match self {
1826-
Self::Literal => Truthiness::AlwaysTrue,
1827-
Self::TypeVar(_) => Truthiness::AlwaysTrue,
1829+
Self::Literal | Self::Optional | Self::TypeVar(_) => Truthiness::AlwaysTrue,
18281830
}
18291831
}
18301832

18311833
/// Return the repr of the symbol at runtime
18321834
pub fn repr(self, db: &'db dyn Db) -> &'db str {
18331835
match self {
18341836
Self::Literal => "typing.Literal",
1837+
Self::Optional => "typing.Optional",
18351838
Self::TypeVar(typevar) => typevar.name(db),
18361839
}
18371840
}
@@ -1840,6 +1843,7 @@ impl<'db> KnownInstanceType<'db> {
18401843
pub const fn class(self) -> KnownClass {
18411844
match self {
18421845
Self::Literal => KnownClass::SpecialForm,
1846+
Self::Optional => KnownClass::SpecialForm,
18431847
Self::TypeVar(_) => KnownClass::TypeVar,
18441848
}
18451849
}
@@ -1859,6 +1863,7 @@ impl<'db> KnownInstanceType<'db> {
18591863
}
18601864
match (module.name().as_str(), instance_name) {
18611865
("typing" | "typing_extensions", "Literal") => Some(Self::Literal),
1866+
("typing" | "typing_extensions", "Optional") => Some(Self::Optional),
18621867
_ => None,
18631868
}
18641869
}

crates/red_knot_python_semantic/src/types/infer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4524,6 +4524,10 @@ impl<'db> TypeInferenceBuilder<'db> {
45244524
Type::Unknown
45254525
}
45264526
},
4527+
KnownInstanceType::Optional => {
4528+
let param_type = self.infer_type_expression(parameters);
4529+
UnionType::from_elements(self.db, [param_type, Type::none(self.db)])
4530+
}
45274531
KnownInstanceType::TypeVar(_) => Type::Todo,
45284532
}
45294533
}

crates/red_knot_python_semantic/src/types/mro.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,9 @@ impl<'db> ClassBase<'db> {
371371
| Type::ModuleLiteral(_)
372372
| Type::SubclassOf(_) => None,
373373
Type::KnownInstance(known_instance) => match known_instance {
374-
KnownInstanceType::Literal => None,
375-
KnownInstanceType::TypeVar(_) => None,
374+
KnownInstanceType::TypeVar(_)
375+
| KnownInstanceType::Literal
376+
| KnownInstanceType::Optional => None,
376377
},
377378
}
378379
}

0 commit comments

Comments
 (0)