[ty] Add documentation for ty_extensions.Top and ty_extensions.Bottom (#22245)

Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
Alex Waygood
2025-12-29 19:43:17 +00:00
committed by GitHub
parent 3d8ae2e476
commit 9dadf2724c
2 changed files with 139 additions and 4 deletions

View File

@@ -6924,6 +6924,86 @@ mod tests {
");
}
#[test]
fn hover_narrowed_type_with_top_materialization() {
let mut test = inlay_hint_test(
r#"
def f(xyxy: object):
if isinstance(xyxy, list):
x = xyxy
"#,
);
assert_snapshot!(test.inlay_hints(), @r#"
def f(xyxy: object):
if isinstance(xyxy, list):
x[: Top[list[Unknown]]] = xyxy
---------------------------------------------
info[inlay-hint-location]: Inlay Hint Target
--> stdlib/ty_extensions.pyi:24:1
|
22 | CallableTypeOf: _SpecialForm
23 |
24 | Top: _SpecialForm
| ^^^
25 | """
26 | `Top[T]` represents the "top materialization" of `T`.
|
info: Source
--> main2.py:4:13
|
2 | def f(xyxy: object):
3 | if isinstance(xyxy, list):
4 | x[: Top[list[Unknown]]] = xyxy
| ^^^
|
info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:2829:7
|
2828 | @disjoint_base
2829 | class list(MutableSequence[_T]):
| ^^^^
2830 | """Built-in mutable sequence.
|
info: Source
--> main2.py:4:17
|
2 | def f(xyxy: object):
3 | if isinstance(xyxy, list):
4 | x[: Top[list[Unknown]]] = xyxy
| ^^^^
|
info[inlay-hint-location]: Inlay Hint Target
--> stdlib/ty_extensions.pyi:14:1
|
13 | # Types
14 | Unknown = object()
| ^^^^^^^
15 | AlwaysTruthy = object()
16 | AlwaysFalsy = object()
|
info: Source
--> main2.py:4:22
|
2 | def f(xyxy: object):
3 | if isinstance(xyxy, list):
4 | x[: Top[list[Unknown]]] = xyxy
| ^^^^^^^
|
---------------------------------------------
info[inlay-hint-edit]: File after edits
info: Source
def f(xyxy: object):
if isinstance(xyxy, list):
x: Top[list[Unknown]] = xyxy
"#);
}
struct InlayHintLocationDiagnostic {
source: FileRange,
target: FileRange,

View File

@@ -20,12 +20,67 @@ Not: _SpecialForm
Intersection: _SpecialForm
TypeOf: _SpecialForm
CallableTypeOf: _SpecialForm
# Top[T] evaluates to the top materialization of T, a type that is a supertype
# of every materialization of T.
Top: _SpecialForm
# Bottom[T] evaluates to the bottom materialization of T, a type that is a subtype
# of every materialization of T.
"""
`Top[T]` represents the "top materialization" of `T`.
For any type `T`, the top [materialization] of `T` is a type that is
a supertype of all materializations of `T`.
For a [fully static] type `T`, `Top[T]` is always exactly the same type
as `T` itself. For example, the top materialization of `Sequence[int]`
is simply `Sequence[int]`.
For a [gradual type] `T` that contains [`Any`][Any] or `Unknown` inside
it, however, `Top[T]` will not be equivalent to `T`. `Top[Sequence[Any]]`
evaluates to `Sequence[object]`: since `Sequence` is covariant, no
possible materialization of `Any` exists such that a fully static
materialization of `Sequence[Any]` would not be a subtype of
`Sequence[object]`.
`Top[T]` cannot be simplified further for invariant gradual types.
`Top[list[Any]]` cannot be simplified to any other type: because `list`
is invariant, `list[object]` is not a supertype of `list[int]`. The
top materialization of `list[Any]` is simply `Top[list[Any]]`: the
infinite union of `list[T]` for every possible fully static type `T`.
[materialization]: https://typing.python.org/en/latest/spec/concepts.html#materialization
[fully static]: https://typing.python.org/en/latest/spec/concepts.html#fully-static-types
[gradual type]: https://typing.python.org/en/latest/spec/concepts.html#gradual-types
[Any]: https://typing.python.org/en/latest/spec/special-types.html#any
"""
Bottom: _SpecialForm
"""
`Bottom[T]` represents the "bottom materialization" of `T`.
For any type `T`, the bottom [materialization] of `T` is a type that is
a subtype of all materializations of `T`.
For a [fully static] type `T`, `Bottom[T]` is always exactly the same type
as `T` itself. For example, the bottom materialization of `Sequence[int]`
is simply `Sequence[int]`.
For a [gradual type] `T` that contains [`Any`][Any] or `Unknown` inside it,
however, `Bottom[T]` will not be equivalent to `T`. `Bottom[Sequence[Any]]`
evaluates to `Sequence[Never]`: since `Sequence` is covariant, no
possible materialization of `Any` exists such that a fully static
materialization of `Sequence[Any]` would not be a supertype of
`Sequence[Never]`. (`Sequence[Never]` is not the same type as the
uninhabited type `Never`: for example, it is inhabited by the empty tuple,
`()`.)
For many invariant gradual types `T`, `Bottom[T]` is equivalent to
[`Never`][Never], although ty will not necessarily apply this simplification
eagerly.
[materialization]: https://typing.python.org/en/latest/spec/concepts.html#materialization
[fully static]: https://typing.python.org/en/latest/spec/concepts.html#fully-static-types
[gradual type]: https://typing.python.org/en/latest/spec/concepts.html#gradual-types
[Never]: https://typing.python.org/en/latest/spec/special-types.html#never
[Any]: https://typing.python.org/en/latest/spec/special-types.html#any
"""
# ty treats annotations of `float` to mean `float | int`, and annotations of `complex`
# to mean `complex | float | int`. This is to support a typing-system special case [1].