@@ -210,6 +210,125 @@ Foo(5)
210210[typing fixtures/typing-full.pyi]
211211[builtins fixtures/dataclasses.pyi]
212212
213+ [case testDataclassTransformFieldSpecifierRejectMalformed]
214+ # flags: --python-version 3.11
215+ from typing import dataclass_transform, Any, Callable, Final, Type
216+
217+ def some_type() -> Type: ...
218+ def some_function() -> Callable[[], None]: ...
219+
220+ def field(*args, **kwargs): ...
221+ def fields_tuple() -> tuple[type | Callable[..., Any], ...]: return (field,)
222+ CONSTANT: Final = (field,)
223+
224+ @dataclass_transform(field_specifiers=(some_type(),)) # E: "field_specifiers" must only contain identifiers
225+ def bad_dataclass1() -> None: ...
226+ @dataclass_transform(field_specifiers=(some_function(),)) # E: "field_specifiers" must only contain identifiers
227+ def bad_dataclass2() -> None: ...
228+ @dataclass_transform(field_specifiers=CONSTANT) # E: "field_specifiers" argument must be a tuple literal
229+ def bad_dataclass3() -> None: ...
230+ @dataclass_transform(field_specifiers=fields_tuple()) # E: "field_specifiers" argument must be a tuple literal
231+ def bad_dataclass4() -> None: ...
232+
233+ [typing fixtures/typing-full.pyi]
234+ [builtins fixtures/dataclasses.pyi]
235+
236+ [case testDataclassTransformFieldSpecifierParams]
237+ # flags: --python-version 3.11
238+ from typing import dataclass_transform, Any, Callable, Type, Final
239+
240+ def field(
241+ *,
242+ init: bool = True,
243+ kw_only: bool = False,
244+ alias: str | None = None,
245+ default: Any | None = None,
246+ default_factory: Callable[[], Any] | None = None,
247+ factory: Callable[[], Any] | None = None,
248+ ): ...
249+ @dataclass_transform(field_specifiers=(field,))
250+ def my_dataclass(cls: Type) -> Type:
251+ return cls
252+
253+ B: Final = 'b_'
254+ @my_dataclass
255+ class Foo:
256+ a: int = field(alias='a_')
257+ b: int = field(alias=B)
258+ # cannot be passed as a positional
259+ kwonly: int = field(kw_only=True, default=0)
260+ # Safe to omit from constructor, error to pass
261+ noinit: int = field(init=False, default=1)
262+ # It should be safe to call the constructor without passing any of these
263+ unused1: int = field(default=0)
264+ unused2: int = field(factory=lambda: 0)
265+ unused3: int = field(default_factory=lambda: 0)
266+
267+ Foo(a=5, b_=1) # E: Unexpected keyword argument "a" for "Foo"
268+ Foo(a_=1, b_=1, noinit=1) # E: Unexpected keyword argument "noinit" for "Foo"
269+ Foo(1, 2, 3) # E: Too many positional arguments for "Foo"
270+ foo = Foo(1, 2, kwonly=3)
271+ reveal_type(foo.noinit) # N: Revealed type is "builtins.int"
272+ reveal_type(foo.unused1) # N: Revealed type is "builtins.int"
273+ Foo(a_=5, b_=1, unused1=2, unused2=3, unused3=4)
274+
275+ def some_str() -> str: ...
276+ def some_bool() -> bool: ...
277+ @my_dataclass
278+ class Bad:
279+ bad1: int = field(alias=some_str()) # E: "alias" argument to dataclass field must be a string literal
280+ bad2: int = field(kw_only=some_bool()) # E: "kw_only" argument must be a boolean literal
281+
282+ # this metadata should only exist for dataclasses.dataclass classes
283+ Foo.__dataclass_fields__ # E: "Type[Foo]" has no attribute "__dataclass_fields__"
284+
285+ [typing fixtures/typing-full.pyi]
286+ [builtins fixtures/dataclasses.pyi]
287+
288+ [case testDataclassTransformFieldSpecifierExtraArgs]
289+ # flags: --python-version 3.11
290+ from typing import dataclass_transform
291+
292+ def field(extra1, *, kw_only=False, extra2=0): ...
293+ @dataclass_transform(field_specifiers=(field,))
294+ def my_dataclass(cls):
295+ return cls
296+
297+ @my_dataclass
298+ class Good:
299+ a: int = field(5)
300+ b: int = field(5, extra2=1)
301+ c: int = field(5, kw_only=True)
302+
303+ @my_dataclass
304+ class Bad:
305+ a: int = field(kw_only=True) # E: Missing positional argument "extra1" in call to "field"
306+
307+ [typing fixtures/typing-full.pyi]
308+ [builtins fixtures/dataclasses.pyi]
309+
310+ [case testDataclassTransformMultipleFieldSpecifiers]
311+ # flags: --python-version 3.11
312+ from typing import dataclass_transform
313+
314+ def field1(*, default: int) -> int: ...
315+ def field2(*, default: str) -> str: ...
316+
317+ @dataclass_transform(field_specifiers=(field1, field2))
318+ def my_dataclass(cls): return cls
319+
320+ @my_dataclass
321+ class Foo:
322+ a: int = field1(default=0)
323+ b: str = field2(default='hello')
324+
325+ reveal_type(Foo) # N: Revealed type is "def (a: builtins.int =, b: builtins.str =) -> __main__.Foo"
326+ Foo()
327+ Foo(a=1, b='bye')
328+
329+ [typing fixtures/typing-full.pyi]
330+ [builtins fixtures/dataclasses.pyi]
331+
213332[case testDataclassTransformOverloadsDecoratorOnOverload]
214333# flags: --python-version 3.11
215334from typing import dataclass_transform, overload, Any, Callable, Type, Literal
0 commit comments