mirror of https://github.com/astral-sh/ruff
[ty] Support field_specifiers for metaclass based transformers
This commit is contained in:
parent
23543194fc
commit
ea56e2415f
|
|
@ -461,7 +461,7 @@ The [`typing.dataclass_transform`] specification also allows classes (such as `d
|
|||
to be listed in `field_specifiers`, but it is currently unclear how this should work, and other type
|
||||
checkers do not seem to support this either.
|
||||
|
||||
### Basic example
|
||||
### For function-based transformers
|
||||
|
||||
```py
|
||||
from typing_extensions import dataclass_transform, Any
|
||||
|
|
@ -487,6 +487,34 @@ reveal_type(alice.name) # revealed: str
|
|||
reveal_type(alice.age) # revealed: int | None
|
||||
```
|
||||
|
||||
### For metaclass-based transformers
|
||||
|
||||
```py
|
||||
from typing_extensions import dataclass_transform, Any
|
||||
|
||||
def fancy_field(*, init: bool = True, kw_only: bool = False) -> Any: ...
|
||||
@dataclass_transform(field_specifiers=(fancy_field,))
|
||||
class FancyMeta(type):
|
||||
def __new__(cls, name, bases, namespace):
|
||||
...
|
||||
return super().__new__(cls, name, bases, namespace)
|
||||
|
||||
class FancyBase(metaclass=FancyMeta): ...
|
||||
|
||||
class Person(FancyBase):
|
||||
id: int = fancy_field(init=False)
|
||||
name: str = fancy_field()
|
||||
age: int | None = fancy_field(kw_only=True)
|
||||
|
||||
reveal_type(Person.__init__) # revealed: (self: Person, name: str = Unknown, *, age: int | None = Unknown) -> None
|
||||
|
||||
alice = Person("Alice", age=30)
|
||||
|
||||
reveal_type(alice.id) # revealed: int
|
||||
reveal_type(alice.name) # revealed: str
|
||||
reveal_type(alice.age) # revealed: int | None
|
||||
```
|
||||
|
||||
## Overloaded dataclass-like decorators
|
||||
|
||||
In the case of an overloaded decorator, the `dataclass_transform` decorator can be applied to the
|
||||
|
|
|
|||
|
|
@ -4532,12 +4532,21 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
let class_node = enclosing_scope.node().as_class()?;
|
||||
|
||||
let class_definition = index.expect_single_definition(class_node);
|
||||
infer_definition_types(db, class_definition)
|
||||
let class_literal = infer_definition_types(db, class_definition)
|
||||
.declaration_type(class_definition)
|
||||
.inner_type()
|
||||
.as_class_literal()?
|
||||
.as_class_literal()?;
|
||||
|
||||
class_literal
|
||||
.dataclass_params(db)
|
||||
.map(|params| SmallVec::from(params.field_specifiers(db)))
|
||||
.or_else(|| {
|
||||
class_literal
|
||||
.try_metaclass(db)
|
||||
.ok()
|
||||
.and_then(|(_, params)| params)
|
||||
.map(|params| SmallVec::from(params.field_specifiers(db)))
|
||||
})
|
||||
}
|
||||
|
||||
if let Some(specifiers) = field_specifiers(self.db(), self.index, self.scope()) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue