mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 13:30:49 -05:00
[ty] Synthesize an empty __slots__ for named tuples (#22573)
## Summary Closes https://github.com/astral-sh/ty/issues/2490.
This commit is contained in:
@@ -845,6 +845,7 @@ class Person(NamedTuple):
|
||||
|
||||
reveal_type(Person._field_defaults) # revealed: dict[str, Any]
|
||||
reveal_type(Person._fields) # revealed: tuple[Literal["name"], Literal["age"]]
|
||||
reveal_type(Person.__slots__) # revealed: tuple[()]
|
||||
reveal_type(Person._make) # revealed: bound method <class 'Person'>._make(iterable: Iterable[Any]) -> Person
|
||||
reveal_type(Person._asdict) # revealed: def _asdict(self) -> dict[str, Any]
|
||||
reveal_type(Person._replace) # revealed: (self: Self, *, name: str = ..., age: int | None = ...) -> Self
|
||||
@@ -887,6 +888,8 @@ Person = namedtuple("Person", ["id", "name", "age"], defaults=[None])
|
||||
|
||||
alice = Person(1, "Alice", 42)
|
||||
bob = Person(2, "Bob")
|
||||
|
||||
reveal_type(Person.__slots__) # revealed: tuple[()]
|
||||
```
|
||||
|
||||
## `collections.namedtuple` with tuple variable field names
|
||||
|
||||
@@ -3244,7 +3244,10 @@ impl<'db> StaticClassLiteral<'db> {
|
||||
)
|
||||
})
|
||||
}
|
||||
(CodeGeneratorKind::NamedTuple, "__new__" | "_replace" | "__replace__" | "_fields") => {
|
||||
(
|
||||
CodeGeneratorKind::NamedTuple,
|
||||
"__new__" | "_replace" | "__replace__" | "_fields" | "__slots__",
|
||||
) => {
|
||||
let fields = self.fields(db, specialization, field_policy);
|
||||
let fields_iter = fields.iter().map(|(name, field)| {
|
||||
let default_ty = match &field.kind {
|
||||
@@ -5212,6 +5215,10 @@ fn synthesize_namedtuple_class_member<'db>(
|
||||
fields.map(|(field_name, _, _)| Type::string_literal(db, &field_name));
|
||||
Some(Type::heterogeneous_tuple(db, field_types))
|
||||
}
|
||||
"__slots__" => {
|
||||
// __slots__: tuple[()] - always empty for namedtuples
|
||||
Some(Type::empty_tuple(db))
|
||||
}
|
||||
"_replace" | "__replace__" => {
|
||||
if name == "__replace__" && Program::get(db).python_version(db) < PythonVersion::PY313 {
|
||||
return None;
|
||||
@@ -5536,7 +5543,10 @@ impl<'db> DynamicNamedTupleLiteral<'db> {
|
||||
// For fallback members from NamedTupleFallback, apply type mapping to handle
|
||||
// `Self` types. The explicitly synthesized members (__new__, _fields, _replace,
|
||||
// __replace__) don't need this mapping.
|
||||
if matches!(name, "__new__" | "_fields" | "_replace" | "__replace__") {
|
||||
if matches!(
|
||||
name,
|
||||
"__new__" | "_fields" | "_replace" | "__replace__" | "__slots__"
|
||||
) {
|
||||
result
|
||||
} else {
|
||||
result.map(|ty| {
|
||||
|
||||
Reference in New Issue
Block a user