diff --git a/crates/ty_python_semantic/resources/mdtest/dataclasses/dataclass_transform.md b/crates/ty_python_semantic/resources/mdtest/dataclasses/dataclass_transform.md index 079f55b43dcb2..39e87d6553cdd 100644 --- a/crates/ty_python_semantic/resources/mdtest/dataclasses/dataclass_transform.md +++ b/crates/ty_python_semantic/resources/mdtest/dataclasses/dataclass_transform.md @@ -218,60 +218,99 @@ reveal_type(TestWithBase(1) < TestWithBase(2)) # revealed: bool ### `kw_only_default` -When provided, sets the default value for the `kw_only` parameter of `field()`. +When provided, sets the default value for the `kw_only` parameter of dataclass fields: ```py from typing import dataclass_transform -from dataclasses import field @dataclass_transform(kw_only_default=True) def create_model(*, kw_only: bool = True): ... @create_model() -class A: +class Model1: name: str -a = A(name="Harry") +reveal_type(Model1.__init__) # revealed: (self: Model1, *, name: str) -> None + +Model1(name="Harry") # error: [missing-argument] # error: [too-many-positional-arguments] -a = A("Harry") +Model1("Harry") ``` This can be overridden by setting `kw_only=False` when applying the decorator: ```py @create_model(kw_only=False) -class CustomerModel: - id: int +class Model1KwOnlyFalse: name: str -c = CustomerModel(1, "Harry") +reveal_type(Model1KwOnlyFalse.__init__) # revealed: (self: Model1KwOnlyFalse, name: str) -> None + +Model1KwOnlyFalse(name="Harry") +Model1KwOnlyFalse("Harry") ``` This also works for metaclass-based transformers: ```py @dataclass_transform(kw_only_default=True) -class ModelMeta(type): ... +class ModelMeta(type): + def __new__( + cls, + name, + bases, + namespace, + *, + kw_only: bool = True, + ): ... class ModelBase(metaclass=ModelMeta): ... -class TestMeta(ModelBase): +class Model2(ModelBase): name: str -reveal_type(TestMeta.__init__) # revealed: (self: TestMeta, *, name: str) -> None +reveal_type(Model2.__init__) # revealed: (self: Model2, *, name: str) -> None + +Model2(name="Harry") +# error: [missing-argument] +# error: [too-many-positional-arguments] +Model2("Harry") + +class Model2KwOnlyFalse(ModelBase, kw_only=False): + name: str + +reveal_type(Model2KwOnlyFalse.__init__) # revealed: (self: Model2KwOnlyFalse, name: str) -> None + +Model2KwOnlyFalse(name="Harry") +Model2KwOnlyFalse("Harry") ``` And for base-class-based transformers: ```py @dataclass_transform(kw_only_default=True) -class ModelBase: ... +class ModelBase: + def __init_subclass__(cls, kw_only: bool = False) -> None: + pass + +class Model3(ModelBase): + name: str + +reveal_type(Model3.__init__) # revealed: (self: Model3, *, name: str) -> None -class TestBase(ModelBase): +Model3(name="Harry") +# error: [missing-argument] +# error: [too-many-positional-arguments] +Model3("Harry") + +class Model3KwOnlyFalse(ModelBase, kw_only=False): name: str -reveal_type(TestBase.__init__) # revealed: (self: TestBase, *, name: str) -> None +reveal_type(Model3KwOnlyFalse.__init__) # revealed: (self: Model3KwOnlyFalse, name: str) -> None + +Model3KwOnlyFalse(name="Harry") +Model3KwOnlyFalse("Harry") ``` ### `frozen_default` diff --git a/crates/ty_python_semantic/src/types/class/static_literal.rs b/crates/ty_python_semantic/src/types/class/static_literal.rs index 7bf4b625f4927..6ed3b8753c208 100644 --- a/crates/ty_python_semantic/src/types/class/static_literal.rs +++ b/crates/ty_python_semantic/src/types/class/static_literal.rs @@ -43,8 +43,8 @@ use crate::{ diagnostic::INVALID_DATACLASS_OVERRIDE, enums::{enum_metadata, is_enum_class_by_inheritance, try_unwrap_nonmember_value}, function::{ - DataclassTransformerFlags, DataclassTransformerParams, KnownFunction, - is_implicit_classmethod, is_implicit_staticmethod, + DataclassTransformerParams, KnownFunction, is_implicit_classmethod, + is_implicit_staticmethod, }, generics::Specialization, infer::infer_unpack_types, @@ -2291,18 +2291,7 @@ impl<'db> StaticClassLiteral<'db> { .. } = field.kind { - let class_kw_only_default = self - .dataclass_params(db) - .is_some_and(|params| params.flags(db).contains(DataclassFlags::KW_ONLY)) - // TODO this next part should not be necessary, if we were properly - // initializing `dataclass_params` from the dataclass-transform params, for - // metaclass and base-class-based dataclass-transformers. - || matches!( - field_policy, - CodeGeneratorKind::DataclassLike(Some(transformer_params)) - if transformer_params.flags(db).contains(DataclassTransformerFlags::KW_ONLY_DEFAULT) - ); - *kw = Some(class_kw_only_default); + *kw = Some(self.has_dataclass_param(db, field_policy, DataclassFlags::KW_ONLY)); } attributes.insert(symbol.name().clone(), field);