mirror of
https://github.com/astral-sh/ruff
synced 2026-01-22 05:51:03 -05:00
## Summary We synthesize a (potentially large) set of `__setitem__` overloads for every item in a `TypedDict`. Previously, validation of subscript assignments on `TypedDict`s relied on actually calling `__setitem__` with the provided key and value types, which implied that we needed to do the full overload call evaluation for this large set of overloads. This PR improves the performance of subscript assignment checks on `TypedDict`s by validating the assignment directly instead of calling `__setitem__`. This PR also adds better handling for assignments to subscripts on union and intersection types (but does not attempt to make it perfect). It achieves this by distributing the check over unions and intersections, instead of calling `__setitem__` on the union/intersection directly. We already do something similar when validating *attribute* assignments. ## Ecosystem impact * A lot of diagnostics change their rule type, and/or split into multiple diagnostics. The new version is more verbose, but easier to understand, in my opinion * Almost all of the invalid-key diagnostics come from pydantic, and they should all go away (including many more) when we implement https://github.com/astral-sh/ty/issues/1479 * Everything else looks correct to me. There may be some new diagnostics due to the fact that we now check intersections. ## Test Plan New Markdown tests.
2.2 KiB
2.2 KiB
Subscript assignment diagnostics
Invalid value type
config: dict[str, int] = {}
config["retries"] = "three" # error: [invalid-assignment]
Invalid key type
config: dict[str, int] = {}
config[0] = 3 # error: [invalid-assignment]
Invalid value type for TypedDict
from typing import TypedDict
class Config(TypedDict):
retries: int
def _(config: Config) -> None:
config["retries"] = "three" # error: [invalid-assignment]
Invalid key type for TypedDict
from typing import TypedDict
class Config(TypedDict):
retries: int
def _(config: Config) -> None:
config[0] = 3 # error: [invalid-key]
Misspelled key for TypedDict
from typing import TypedDict
class Config(TypedDict):
retries: int
def _(config: Config) -> None:
config["Retries"] = 30.0 # error: [invalid-key]
No __setitem__ method
class ReadOnlyDict:
def __getitem__(self, key: str) -> int:
return 42
config = ReadOnlyDict()
config["retries"] = 3 # error: [invalid-assignment]
Possibly missing __setitem__ method
def _(config: dict[str, int] | None) -> None:
config["retries"] = 3 # error: [invalid-assignment]
Unknown key for one element of a union
from typing import TypedDict
class Person(TypedDict):
name: str
class Animal(TypedDict):
name: str
legs: int
def _(being: Person | Animal) -> None:
being["legs"] = 4 # error: [invalid-key]
Unknown key for all elemens of a union
from typing import TypedDict
class Person(TypedDict):
name: str
class Animal(TypedDict):
name: str
legs: int
def _(being: Person | Animal) -> None:
# error: [invalid-key]
# error: [invalid-key]
being["surname"] = "unknown"
Wrong value type for one element of a union
def _(config: dict[str, int] | dict[str, str]) -> None:
config["retries"] = 3 # error: [invalid-assignment]
Wrong value type for all elements of a union
def _(config: dict[str, int] | dict[str, str]) -> None:
# error: [invalid-assignment]
# error: [invalid-assignment]
config["retries"] = 3.0