mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[ty] More tests for TypedDict (#20205)
## Summary A small set of additional tests for `TypedDict` that I wrote while going through the spec. Note that this certainly doesn't make the test suite exhaustive (see remaining open points in the updated list here: https://github.com/astral-sh/ty/issues/154).
This commit is contained in:
parent
b49aa35074
commit
1aaa0847ab
1 changed files with 50 additions and 2 deletions
|
@ -128,6 +128,9 @@ Person({"name": "Alice"})
|
||||||
accepts_person({"name": "Alice"})
|
accepts_person({"name": "Alice"})
|
||||||
# TODO: this should be an error, similar to the above
|
# TODO: this should be an error, similar to the above
|
||||||
house.owner = {"name": "Alice"}
|
house.owner = {"name": "Alice"}
|
||||||
|
a_person: Person
|
||||||
|
# TODO: this should be an error, similar to the above
|
||||||
|
a_person = {"name": "Alice"}
|
||||||
```
|
```
|
||||||
|
|
||||||
All of these have an invalid type for the `name` field:
|
All of these have an invalid type for the `name` field:
|
||||||
|
@ -144,6 +147,9 @@ Person({"name": None, "age": 30})
|
||||||
accepts_person({"name": None, "age": 30})
|
accepts_person({"name": None, "age": 30})
|
||||||
# TODO: this should be an error, similar to the above
|
# TODO: this should be an error, similar to the above
|
||||||
house.owner = {"name": None, "age": 30}
|
house.owner = {"name": None, "age": 30}
|
||||||
|
a_person: Person
|
||||||
|
# TODO: this should be an error, similar to the above
|
||||||
|
a_person = {"name": None, "age": 30}
|
||||||
```
|
```
|
||||||
|
|
||||||
All of these have an extra field that is not defined in the `TypedDict`:
|
All of these have an extra field that is not defined in the `TypedDict`:
|
||||||
|
@ -160,6 +166,9 @@ Person({"name": "Alice", "age": 30, "extra": True})
|
||||||
accepts_person({"name": "Alice", "age": 30, "extra": True})
|
accepts_person({"name": "Alice", "age": 30, "extra": True})
|
||||||
# TODO: this should be an error
|
# TODO: this should be an error
|
||||||
house.owner = {"name": "Alice", "age": 30, "extra": True}
|
house.owner = {"name": "Alice", "age": 30, "extra": True}
|
||||||
|
# TODO: this should be an error
|
||||||
|
a_person: Person
|
||||||
|
a_person = {"name": "Alice", "age": 30, "extra": True}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Type ignore compatibility issues
|
## Type ignore compatibility issues
|
||||||
|
@ -242,8 +251,9 @@ invalid_extra = OptionalPerson(name="George", extra=True)
|
||||||
|
|
||||||
## `Required` and `NotRequired`
|
## `Required` and `NotRequired`
|
||||||
|
|
||||||
You can have fine-grained control over field requirements using `Required` and `NotRequired`
|
You can have fine-grained control over keys using `Required` and `NotRequired` qualifiers. These
|
||||||
qualifiers, which override the class-level `total=` setting:
|
qualifiers override the class-level `total` setting, which sets the default (`total=True` means that
|
||||||
|
all keys are required by default, `total=False` means that all keys are non-required by default):
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing_extensions import TypedDict, Required, NotRequired
|
from typing_extensions import TypedDict, Required, NotRequired
|
||||||
|
@ -444,6 +454,12 @@ class Person(TypedDict, total=False):
|
||||||
id: ReadOnly[Required[int]]
|
id: ReadOnly[Required[int]]
|
||||||
name: str
|
name: str
|
||||||
age: int | None
|
age: int | None
|
||||||
|
|
||||||
|
alice: Person = {"id": 1, "name": "Alice", "age": 30}
|
||||||
|
alice["age"] = 31 # okay
|
||||||
|
|
||||||
|
# TODO: this should be an error
|
||||||
|
alice["id"] = 2
|
||||||
```
|
```
|
||||||
|
|
||||||
## Methods on `TypedDict`
|
## Methods on `TypedDict`
|
||||||
|
@ -764,6 +780,38 @@ from typing import TypedDict
|
||||||
x: TypedDict = {"name": "Alice"}
|
x: TypedDict = {"name": "Alice"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `dict`-subclass inhabitants
|
||||||
|
|
||||||
|
Values that inhabit a `TypedDict` type must be instances of `dict` itself, not a subclass:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
class MyDict(dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Person(TypedDict):
|
||||||
|
name: str
|
||||||
|
age: int | None
|
||||||
|
|
||||||
|
# TODO: this should be an error
|
||||||
|
x: Person = MyDict({"name": "Alice", "age": 30})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cannot be used in `isinstance` tests
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
class Person(TypedDict):
|
||||||
|
name: str
|
||||||
|
age: int | None
|
||||||
|
|
||||||
|
def _(obj: object) -> bool:
|
||||||
|
# TODO: this should be an error
|
||||||
|
return isinstance(obj, Person)
|
||||||
|
```
|
||||||
|
|
||||||
## Diagnostics
|
## Diagnostics
|
||||||
|
|
||||||
<!-- snapshot-diagnostics -->
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue