mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 07:04:53 +00:00
[ty] NamedTuple 'fallback' attributes (#18127)
## Summary Add various attributes to `NamedTuple` classes/instances that are available at runtime. closes https://github.com/astral-sh/ty/issues/417 ## Test Plan New Markdown tests
This commit is contained in:
parent
8644c9da43
commit
e67b35743a
3 changed files with 53 additions and 5 deletions
|
@ -139,6 +139,33 @@ class Property[T](NamedTuple):
|
|||
reveal_type(Property("height", 3.4)) # revealed: Property[Unknown]
|
||||
```
|
||||
|
||||
## Attributes on `NamedTuple`
|
||||
|
||||
The following attributes are available on `NamedTuple` classes / instances:
|
||||
|
||||
```py
|
||||
from typing import NamedTuple
|
||||
|
||||
class Person(NamedTuple):
|
||||
name: str
|
||||
age: int | None = None
|
||||
|
||||
reveal_type(Person._field_defaults) # revealed: dict[str, Any]
|
||||
reveal_type(Person._fields) # revealed: tuple[str, ...]
|
||||
reveal_type(Person._make) # revealed: bound method <class 'Person'>._make(iterable: Iterable[Any]) -> Self
|
||||
reveal_type(Person._asdict) # revealed: def _asdict(self) -> dict[str, Any]
|
||||
reveal_type(Person._replace) # revealed: def _replace(self, **kwargs: Any) -> Self
|
||||
|
||||
# TODO: should be `Person` once we support `Self`
|
||||
reveal_type(Person._make(("Alice", 42))) # revealed: Unknown
|
||||
|
||||
person = Person("Alice", 42)
|
||||
|
||||
reveal_type(person._asdict()) # revealed: dict[str, Any]
|
||||
# TODO: should be `Person` once we support `Self`
|
||||
reveal_type(person._replace(name="Bob")) # revealed: Unknown
|
||||
```
|
||||
|
||||
## `collections.namedtuple`
|
||||
|
||||
```py
|
||||
|
|
|
@ -118,6 +118,8 @@ pub enum KnownModule {
|
|||
Dataclasses,
|
||||
Collections,
|
||||
Inspect,
|
||||
#[strum(serialize = "_typeshed._type_checker_internals")]
|
||||
TypeCheckerInternals,
|
||||
TyExtensions,
|
||||
}
|
||||
|
||||
|
@ -135,6 +137,7 @@ impl KnownModule {
|
|||
Self::Dataclasses => "dataclasses",
|
||||
Self::Collections => "collections",
|
||||
Self::Inspect => "inspect",
|
||||
Self::TypeCheckerInternals => "_typeshed._type_checker_internals",
|
||||
Self::TyExtensions => "ty_extensions",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1289,6 +1289,14 @@ impl<'db> ClassLiteral<'db> {
|
|||
|
||||
Some(Type::Callable(CallableType::single(db, signature)))
|
||||
}
|
||||
(CodeGeneratorKind::NamedTuple, name) if name != "__init__" => {
|
||||
KnownClass::NamedTupleFallback
|
||||
.to_class_literal(db)
|
||||
.into_class_literal()?
|
||||
.own_class_member(db, None, name)
|
||||
.symbol
|
||||
.ignore_possibly_unbound()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -1985,6 +1993,8 @@ pub enum KnownClass {
|
|||
NotImplementedType,
|
||||
// dataclasses
|
||||
Field,
|
||||
// _typeshed._type_checker_internals
|
||||
NamedTupleFallback,
|
||||
}
|
||||
|
||||
impl<'db> KnownClass {
|
||||
|
@ -2067,7 +2077,8 @@ impl<'db> KnownClass {
|
|||
// (see https://docs.python.org/3/library/constants.html#NotImplemented)
|
||||
| Self::NotImplementedType
|
||||
| Self::Classmethod
|
||||
| Self::Field => Truthiness::Ambiguous,
|
||||
| Self::Field
|
||||
| Self::NamedTupleFallback => Truthiness::Ambiguous,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2141,7 +2152,8 @@ impl<'db> KnownClass {
|
|||
| Self::EllipsisType
|
||||
| Self::NotImplementedType
|
||||
| Self::UnionType
|
||||
| Self::Field => false,
|
||||
| Self::Field
|
||||
| Self::NamedTupleFallback => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2217,6 +2229,7 @@ impl<'db> KnownClass {
|
|||
}
|
||||
Self::NotImplementedType => "_NotImplementedType",
|
||||
Self::Field => "Field",
|
||||
Self::NamedTupleFallback => "NamedTupleFallback",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2446,6 +2459,7 @@ impl<'db> KnownClass {
|
|||
| Self::Deque
|
||||
| Self::OrderedDict => KnownModule::Collections,
|
||||
Self::Field => KnownModule::Dataclasses,
|
||||
Self::NamedTupleFallback => KnownModule::TypeCheckerInternals,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2508,7 +2522,8 @@ impl<'db> KnownClass {
|
|||
| Self::Super
|
||||
| Self::NamedTuple
|
||||
| Self::NewType
|
||||
| Self::Field => false,
|
||||
| Self::Field
|
||||
| Self::NamedTupleFallback => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2573,7 +2588,8 @@ impl<'db> KnownClass {
|
|||
| Self::UnionType
|
||||
| Self::NamedTuple
|
||||
| Self::NewType
|
||||
| Self::Field => false,
|
||||
| Self::Field
|
||||
| Self::NamedTupleFallback => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2646,6 +2662,7 @@ impl<'db> KnownClass {
|
|||
}
|
||||
"_NotImplementedType" => Self::NotImplementedType,
|
||||
"Field" => Self::Field,
|
||||
"NamedTupleFallback" => Self::NamedTupleFallback,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
@ -2700,7 +2717,8 @@ impl<'db> KnownClass {
|
|||
| Self::GeneratorType
|
||||
| Self::AsyncGeneratorType
|
||||
| Self::WrapperDescriptorType
|
||||
| Self::Field => module == self.canonical_module(db),
|
||||
| Self::Field
|
||||
| Self::NamedTupleFallback => module == self.canonical_module(db),
|
||||
Self::NoneType => matches!(module, KnownModule::Typeshed | KnownModule::Types),
|
||||
Self::SpecialForm
|
||||
| Self::TypeVar
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue