← Back to index

dataclasses_transform_converter.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 8
FP: 8
FN: 1
Optional: 0 / 0
1"""
2Tests the dataclass_transform mechanism supports the "converter" parameter
3in a field specifier class.
4"""
5
6# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#converters
7
8from typing import (
9 Any,
10 Callable,
11 TypeVar,
12 dataclass_transform,
13 overload,
14)
16T = TypeVar("T")
17S = TypeVar("S")
20def model_field(
21 *,
22 converter: Callable[[S], T],
23 default: S | None = None,
24 default_factory: Callable[[], S] | None = None,
25) -> T:
26 raise NotImplementedError
29@dataclass_transform(field_specifiers=(model_field,))
30class ModelBase:
31 ...
34# > The converter must be a callable that must accept a single positional
35# > argument (but may accept other optional arguments, which are ignored for
36# > typing purposes).
39def bad_converter1() -> int:
40 return 0
43def bad_converter2(*, x: int) -> int:
44 return 0
47class DC1(ModelBase):
48 field1: int = model_field(converter=bad_converter1) # E
[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Unknown, /) -> Unknown`, found `def bad_converter1() -> int`
49 field2: int = model_field(converter=bad_converter2) # E
[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Unknown, /) -> Unknown`, found `def bad_converter2(*, x: int) -> int`
52# > The type of the first positional parameter provides the type of the
53# > synthesized __init__ parameter for the field.
55# > The return type of the callable must be assignable to the field’s
56# > declared type.
59def converter_simple(s: str) -> int:
60 return int(s)
63def converter_with_param_before_args(s: str, *args: int, **kwargs: int) -> int:
64 return int(s)
67def converter_with_args(*args: str) -> int:
68 return int(args[0])
71@overload
72def overloaded_converter(s: str) -> int:
73 ...
76@overload
77def overloaded_converter(s: list[str]) -> int:
78 ...
81def overloaded_converter(s: str | list[str], *args: str) -> int | str:
82 return 0
85class ConverterClass:
86 @overload
87 def __init__(self, val: str) -> None:
88 ...
90 @overload
91 def __init__(self, val: bytes) -> None:
92 ...
94 def __init__(self, val: str | bytes) -> None:
95 pass
98class DC2(ModelBase):
99 field0: int = model_field(converter=converter_simple)
100 field1: int = model_field(converter=converter_with_param_before_args)
101 field2: int = model_field(converter=converter_with_args)
102 field3: ConverterClass = model_field(converter=ConverterClass)
Unexpected error [invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | bytes, /) -> ConverterClass`, found `<class 'ConverterClass'>`
103 field4: int = model_field(converter=overloaded_converter)
Unexpected error [invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | list[str], /) -> int`, found `Overload[(s: str) -> int, (s: list[str]) -> int]`
104 field5: dict[str, str] = model_field(converter=dict, default=())
Unexpected error [invalid-assignment] Object of type `dict[str, str] | dict[bytes, bytes]` is not assignable to `dict[str, str]` [invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Iterable[list[str]] | Iterable[list[bytes]], /) -> dict[str, str] | dict[bytes, bytes]`, found `<class 'dict'>`
107DC2(1, "f1", "f2", b"f3", []) # E
[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f1"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f2"]` [invalid-argument-type] Argument is incorrect: Expected `ConverterClass`, found `Literal[b"f3"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `list[Unknown]`
108DC2("f0", "f1", "f2", 1, []) # E
[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f0"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f1"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f2"]` [invalid-argument-type] Argument is incorrect: Expected `ConverterClass`, found `Literal[1]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `list[Unknown]`
109DC2("f0", "f1", "f2", "f3", 3j) # E
[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f0"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f1"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f2"]` [invalid-argument-type] Argument is incorrect: Expected `ConverterClass`, found `Literal["f3"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `complex`
112dc1 = DC2("f0", "f1", "f2", b"f6", [])
Unexpected error [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f0"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f1"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f2"]` [invalid-argument-type] Argument is incorrect: Expected `ConverterClass`, found `Literal[b"f6"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `list[Unknown]`
114dc1.field0 = "f1"
Unexpected error [invalid-assignment] Object of type `Literal["f1"]` is not assignable to attribute `field0` of type `int`
115dc1.field3 = "f6"
Unexpected error [invalid-assignment] Object of type `Literal["f6"]` is not assignable to attribute `field3` of type `ConverterClass`
116dc1.field3 = b"f6"
Unexpected error [invalid-assignment] Object of type `Literal[b"f6"]` is not assignable to attribute `field3` of type `ConverterClass`
118dc1.field0 = 1 # E
Expected a ty diagnostic for this line
119dc1.field3 = 1 # E
[invalid-assignment] Object of type `Literal[1]` is not assignable to attribute `field3` of type `ConverterClass`
121dc2 = DC2("f0", "f1", "f2", "f6", "1", (("a", "1"), ("b", "2")))
Unexpected error [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f0"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f1"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f2"]` [invalid-argument-type] Argument is incorrect: Expected `ConverterClass`, found `Literal["f6"]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["1"]` [invalid-argument-type] Argument is incorrect: Expected `dict[str, str]`, found `tuple[tuple[Literal["a"], Literal["1"]], tuple[Literal["b"], Literal["2"]]]`
124# > If default or default_factory are provided, the type of the default value
125# > should be assignable to the first positional parameter of the converter.
128class DC3(ModelBase):
129 field0: int = model_field(converter=converter_simple, default="")
130 field1: int = model_field(converter=converter_simple, default=1) # E
[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | Literal[1], /) -> int`, found `def converter_simple(s: str) -> int`
132 field2: int = model_field(converter=converter_simple, default_factory=str)
133 field3: int = model_field(converter=converter_simple, default_factory=int) # E
[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | int, /) -> int`, found `def converter_simple(s: str) -> int`