Skip to content

Commit 7538ca4

Browse files
committed
Rename, more tests
1 parent 2b7a42a commit 7538ca4

File tree

4 files changed

+97
-10
lines changed

4 files changed

+97
-10
lines changed

src/cattrs/v/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
)
1010
from ._fluent import V, customize
1111
from ._validators import (
12-
all_elements_must,
1312
between,
13+
for_all,
1414
greater_than,
1515
ignoring_none,
1616
is_unique,
1717
len_between,
1818
)
1919

2020
__all__ = [
21-
"all_elements_must",
2221
"between",
2322
"customize",
23+
"for_all",
2424
"format_exception",
2525
"greater_than",
2626
"ignoring_none",

src/cattrs/v/_validators.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ def skip_none(val: T | None, _validators=validators) -> None:
9797
return factory
9898

9999

100-
def all_elements_must(
100+
def for_all(
101101
validator: Callable[[T], None | bool], *validators: Callable[[T], None | bool]
102-
) -> ValidatorFactory[T]:
102+
) -> ValidatorFactory[Iterable[T]]:
103103
"""A helper validator included with cattrs.
104104
105105
Run all the given validators against all members of the

tests/v/test_typing_validators.yml

+54-1
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,57 @@
8888
c = Converter()
8989
9090
v.customize(c, A, v.V(fields(A).a).ensure(v.greater_than(5))) # E: Argument 1 to "ensure" of "V" has incompatible type "Callable[[int], None]"; expected "Callable[[int | None], bool | None] | Callable[[bool], Callable[[int | None], None]]" [arg-type]
91-
v.customize(c, A, v.V(fields(A).a).ensure(v.ignoring_none(v.len_between(0, 5)))) # E: Argument 1 to "ignoring_none" has incompatible type "Callable[[Sized], None]"; expected "Callable[[int], None]" [arg-type]
91+
v.customize(c, A, v.V(fields(A).a).ensure(v.ignoring_none(v.len_between(0, 5)))) # E: Argument 1 to "ignoring_none" has incompatible type "Callable[[Sized], None]"; expected "Callable[[int], None]" [arg-type]
92+
93+
- case: for_all
94+
main: |
95+
from attrs import define, fields
96+
from cattrs import v, Converter
97+
98+
@define
99+
class A:
100+
a: list[int]
101+
102+
c = Converter()
103+
104+
v.customize(c, A, v.V(fields(A).a).ensure(v.for_all(v.greater_than(5))))
105+
106+
- case: for_all_dict
107+
main: |
108+
from attrs import define, fields
109+
from cattrs import v, Converter
110+
111+
@define
112+
class A:
113+
a: dict[str, int]
114+
115+
c = Converter()
116+
117+
v.customize(c, A, v.V(fields(A).a).ensure(v.for_all(v.len_between(0, 5))))
118+
119+
- case: for_all_error
120+
main: |
121+
from attrs import define, fields
122+
from cattrs import v, Converter
123+
from cattrs.v import for_all as fa
124+
125+
@define
126+
class A:
127+
a: list[int]
128+
129+
c = Converter()
130+
131+
v.customize(c, A, v.V(fields(A).a).ensure(fa(fa(v.greater_than(5))))) # E: Argument 1 to "for_all" has incompatible type "Callable[[bool], Callable[[Iterable[int]], None]]"; expected "Callable[[int], bool | None]" [arg-type]
132+
133+
- case: for_all_dict_error
134+
main: |
135+
from attrs import define, fields
136+
from cattrs import v, Converter
137+
138+
@define
139+
class A:
140+
a: dict[str, int]
141+
142+
c = Converter()
143+
144+
v.customize(c, A, v.V(fields(A).a).ensure(v.for_all(v.greater_than(5)))) # E: Argument 1 to "greater_than" has incompatible type "int"; expected "str" [arg-type]

tests/v/test_validators.py

+39-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Optional
1+
from typing import Dict, List, Optional, Tuple
22

33
from attrs import define
44
from attrs import fields as f
@@ -8,9 +8,9 @@
88
from cattrs.errors import ClassValidationError
99
from cattrs.v import (
1010
V,
11-
all_elements_must,
1211
between,
1312
customize,
13+
for_all,
1414
greater_than,
1515
ignoring_none,
1616
is_unique,
@@ -29,6 +29,11 @@ class WithList:
2929
a: List[int]
3030

3131

32+
@define
33+
class WithDict:
34+
a: Dict[str, int]
35+
36+
3237
@define
3338
class WithOptional:
3439
a: Optional[int]
@@ -171,13 +176,13 @@ def test_ignoring_none(converter: BaseConverter):
171176
assert repr(exc_info.value) == "ValueError('10 not between 0 and 5')"
172177

173178

174-
def test_all_elements_must(converter: BaseConverter):
175-
"""`all_elements_must` works."""
179+
def test_for_all_lists(converter: BaseConverter):
180+
"""`for_all` works on lists."""
176181

177182
hook = customize(
178183
converter,
179184
WithList,
180-
V(f(WithList).a).ensure(all_elements_must(greater_than(5), between(5, 10))),
185+
V(f(WithList).a).ensure(for_all(greater_than(5), between(5, 10))),
181186
)
182187

183188
assert hook({"a": []}, None) == WithList([])
@@ -193,3 +198,32 @@ def test_all_elements_must(converter: BaseConverter):
193198
"invalid value (2 not greater than 5) @ $.a[1]",
194199
"invalid value (2 not between 5 and 10) @ $.a[1]",
195200
]
201+
else:
202+
with raises(ValueError) as exc_info:
203+
hook({"a": [1, 2]}, None)
204+
205+
assert repr(exc_info.value) == "ValueError('1 not greater than 5')"
206+
207+
208+
def test_for_all_dicts(converter: BaseConverter):
209+
"""`for_all` works on dicts."""
210+
211+
hook = customize(
212+
converter, WithDict, V(f(WithDict).a).ensure(for_all(len_between(0, 2)))
213+
)
214+
215+
assert hook({"a": {}}, None) == WithDict({})
216+
assert hook({"a": {"a": 1, "b": 2}}, None) == WithDict({"a": 1, "b": 2})
217+
218+
if converter.detailed_validation:
219+
with raises(ClassValidationError) as exc_info:
220+
hook({"a": {"aaa": 1}}, None)
221+
222+
assert transform_error(exc_info.value) == [
223+
"invalid value (length (3) not between 0 and 2) @ $.a[0]"
224+
]
225+
else:
226+
with raises(ValueError) as exc_info:
227+
hook({"a": {"aaa": 1}}, None)
228+
229+
assert repr(exc_info.value) == "ValueError('length (3) not between 0 and 2')"

0 commit comments

Comments
 (0)