mirror of https://github.com/astral-sh/ruff
[ty] Suppress false positives when `dataclasses.dataclass(...)(cls)` is called imperatively (#21729)
Fixes https://github.com/astral-sh/ty/issues/1705
This commit is contained in:
parent
f68080b55e
commit
c5b8d551df
|
|
@ -1462,3 +1462,57 @@ def test_c():
|
|||
c = C(1)
|
||||
c.__lt__ = Mock()
|
||||
```
|
||||
|
||||
## Imperatively calling `dataclasses.dataclass`
|
||||
|
||||
While we do not currently recognize the special behaviour of `dataclasses.dataclass` if it is called
|
||||
imperatively, we recognize that it can be called imperatively and do not emit any false-positive
|
||||
diagnostics on such calls:
|
||||
|
||||
```py
|
||||
from dataclasses import dataclass
|
||||
from typing_extensions import TypeVar, dataclass_transform
|
||||
|
||||
U = TypeVar("U")
|
||||
|
||||
@dataclass_transform(kw_only_default=True)
|
||||
def sequence(cls: type[U]) -> type[U]:
|
||||
d = dataclass(
|
||||
repr=False,
|
||||
eq=False,
|
||||
match_args=False,
|
||||
kw_only=True,
|
||||
)(cls)
|
||||
reveal_type(d) # revealed: type[U@sequence] & Any
|
||||
return d
|
||||
|
||||
@dataclass_transform(kw_only_default=True)
|
||||
def sequence2(cls: type) -> type:
|
||||
d = dataclass(
|
||||
repr=False,
|
||||
eq=False,
|
||||
match_args=False,
|
||||
kw_only=True,
|
||||
)(cls)
|
||||
reveal_type(d) # revealed: type & Any
|
||||
return d
|
||||
|
||||
@dataclass_transform(kw_only_default=True)
|
||||
def sequence3(cls: type[U]) -> type[U]:
|
||||
# TODO: should reveal `type[U@sequence3]`
|
||||
return reveal_type(dataclass(cls)) # revealed: Unknown
|
||||
|
||||
@dataclass_transform(kw_only_default=True)
|
||||
def sequence4(cls: type) -> type:
|
||||
# TODO: should reveal `type`
|
||||
return reveal_type(dataclass(cls)) # revealed: Unknown
|
||||
|
||||
class Foo: ...
|
||||
|
||||
ordered_foo = dataclass(order=True)(Foo)
|
||||
reveal_type(ordered_foo) # revealed: type[Foo] & Any
|
||||
# TODO: should be `Foo & Any`
|
||||
reveal_type(ordered_foo()) # revealed: @Todo(Type::Intersection.call)
|
||||
# TODO: should be `Any`
|
||||
reveal_type(ordered_foo() < ordered_foo()) # revealed: @Todo(Type::Intersection.call)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6226,11 +6226,25 @@ impl<'db> Type<'db> {
|
|||
),
|
||||
|
||||
Type::Intersection(_) => {
|
||||
Binding::single(self, Signature::todo("Type::Intersection.call()")).into()
|
||||
Binding::single(self, Signature::todo("Type::Intersection.call")).into()
|
||||
}
|
||||
|
||||
// TODO: this is actually callable
|
||||
Type::DataclassDecorator(_) => CallableBinding::not_callable(self).into(),
|
||||
Type::DataclassDecorator(_) => {
|
||||
let typevar = BoundTypeVarInstance::synthetic(db, "T", TypeVarVariance::Invariant);
|
||||
let typevar_meta = SubclassOfType::from(db, typevar);
|
||||
let context = GenericContext::from_typevar_instances(db, [typevar]);
|
||||
let parameters = [Parameter::positional_only(Some(Name::new_static("cls")))
|
||||
.with_annotated_type(typevar_meta)];
|
||||
// Intersect with `Any` for the return type to reflect the fact that the `dataclass()`
|
||||
// decorator adds methods to the class
|
||||
let returns = IntersectionType::from_elements(db, [typevar_meta, Type::any()]);
|
||||
let signature = Signature::new_generic(
|
||||
Some(context),
|
||||
Parameters::new(db, parameters),
|
||||
Some(returns),
|
||||
);
|
||||
Binding::single(self, signature).into()
|
||||
}
|
||||
|
||||
// TODO: some `SpecialForm`s are callable (e.g. TypedDicts)
|
||||
Type::SpecialForm(_) => CallableBinding::not_callable(self).into(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue