Coverage for tests\unit\validate_type\test_validate_type.py: 78%
206 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-01-17 01:00 -0700
« prev ^ index » next coverage.py v7.6.1, created at 2025-01-17 01:00 -0700
1from __future__ import annotations
3import typing
4from typing import Any, Optional, Union
6import pytest
8from muutils.validate_type import IncorrectTypeException, validate_type
11# Tests for basic types and common use cases
12@pytest.mark.parametrize(
13 "value, expected_type, expected_result",
14 [
15 (42, int, True),
16 (3.14, float, True),
17 (5, int, True),
18 (5.0, int, False),
19 ("hello", str, True),
20 (True, bool, True),
21 (None, type(None), True),
22 (None, int, False),
23 ([1, 2, 3], typing.List, True),
24 ([1, 2, 3], typing.List, True),
25 ({"a": 1, "b": 2}, typing.Dict, True),
26 ({"a": 1, "b": 2}, typing.Dict, True),
27 ({1, 2, 3}, typing.Set, True),
28 ({1, 2, 3}, typing.Set, True),
29 ((1, 2, 3), typing.Tuple, True),
30 ((1, 2, 3), typing.Tuple, True),
31 (b"bytes", bytes, True),
32 (b"bytes", str, False),
33 ("3.14", float, False),
34 ("hello", Any, True),
35 (5, Any, True),
36 (3.14, Any, True),
37 # ints
38 (int(0), int, True),
39 (int(1), int, True),
40 (int(-1), int, True),
41 # bools
42 (True, bool, True),
43 (False, bool, True),
44 ],
45)
46def test_validate_type_basic(value, expected_type, expected_result):
47 try:
48 assert validate_type(value, expected_type) == expected_result
49 except Exception as e:
50 raise Exception(
51 f"{value = }, {expected_type = }, {expected_result = }, {e}"
52 ) from e
55@pytest.mark.parametrize(
56 "value",
57 [
58 42,
59 "hello",
60 3.14,
61 True,
62 None,
63 [1, 2, 3],
64 {"a": 1, "b": 2},
65 {1, 2, 3},
66 (1, 2, 3),
67 b"bytes",
68 "3.14",
69 ],
70)
71def test_validate_type_any(value):
72 try:
73 assert validate_type(value, Any)
74 except Exception as e:
75 raise Exception(f"{value = }, expected `Any`, {e}") from e
78@pytest.mark.parametrize(
79 "value, expected_type, expected_result",
80 [
81 (42, Union[int, str], True),
82 ("hello", Union[int, str], True),
83 (3.14, Union[int, float], True),
84 (True, Union[int, str], True),
85 (None, Union[int, type(None)], True),
86 (None, Union[int, str], False),
87 (5, Union[int, str], True),
88 (5.0, Union[int, str], False),
89 ("hello", Union[int, str], True),
90 (5, typing.Union[int, str], True),
91 ("hello", typing.Union[int, str], True),
92 (5.0, typing.Union[int, str], False),
93 (5, Union[int, str], True),
94 ("hello", Union[int, str], True),
95 (5.0, Union[int, str], False),
96 ],
97)
98def test_validate_type_union(value, expected_type, expected_result):
99 try:
100 assert validate_type(value, expected_type) == expected_result
101 except Exception as e:
102 raise Exception(
103 f"{value = }, {expected_type = }, {expected_result = }, {e}"
104 ) from e
107@pytest.mark.parametrize(
108 "value, expected_type, expected_result",
109 [
110 (42, Optional[int], True),
111 ("hello", Optional[int], False),
112 (3.14, Optional[int], False),
113 ([1], Optional[typing.List[int]], True),
114 (None, Optional[int], True),
115 (None, Optional[str], True),
116 (None, Optional[int], True),
117 (None, Optional[None], True),
118 (None, Optional[typing.List[typing.Dict[str, int]]], True),
119 ],
120)
121def test_validate_type_optional(value, expected_type, expected_result):
122 try:
123 assert validate_type(value, expected_type) == expected_result
124 except Exception as e:
125 raise Exception(
126 f"{value = }, {expected_type = }, {expected_result = }, {e}"
127 ) from e
130@pytest.mark.parametrize(
131 "value, expected_type, expected_result",
132 [
133 (42, typing.List[int], False),
134 ([1, 2, 3], typing.List[int], True),
135 ([1, 2, 3], typing.List[str], False),
136 (["a", "b", "c"], typing.List[str], True),
137 ([1, "a", 3], typing.List[int], False),
138 (42, typing.List[int], False),
139 ([1, 2, 3], typing.List[int], True),
140 ([1, "2", 3], typing.List[int], False),
141 ],
142)
143def test_validate_type_list(value, expected_type, expected_result):
144 try:
145 assert validate_type(value, expected_type) == expected_result
146 except Exception as e:
147 raise Exception(
148 f"{value = }, {expected_type = }, {expected_result = }, {e}"
149 ) from e
152@pytest.mark.parametrize(
153 "value, expected_type, expected_result",
154 [
155 (42, typing.Dict[str, int], False),
156 ({"a": 1, "b": 2}, typing.Dict[str, int], True),
157 ({"a": 1, "b": 2}, typing.Dict[int, str], False),
158 (42, typing.Dict[str, int], False),
159 ({"a": 1, "b": 2}, typing.Dict[str, int], True),
160 ({"a": 1, "b": 2}, typing.Dict[int, str], False),
161 ({1: "a", 2: "b"}, typing.Dict[int, str], True),
162 ({1: "a", 2: "b"}, typing.Dict[str, int], False),
163 ({"a": 1, "b": "c"}, typing.Dict[str, int], False),
164 ([("a", 1), ("b", 2)], typing.Dict[str, int], False),
165 ({"key": "value"}, typing.Dict[str, str], True),
166 ({"key": 2}, typing.Dict[str, str], False),
167 ({"key": 2}, typing.Dict[str, int], True),
168 ({"key": 2.0}, typing.Dict[str, int], False),
169 ({"a": 1, "b": 2}, typing.Dict[str, int], True),
170 ({"a": 1, "b": "2"}, typing.Dict[str, int], False),
171 ],
172)
173def test_validate_type_dict(value, expected_type, expected_result):
174 try:
175 assert validate_type(value, expected_type) == expected_result
176 except Exception as e:
177 raise Exception(
178 f"{value = }, {expected_type = }, {expected_result = }, {e}"
179 ) from e
182@pytest.mark.parametrize(
183 "value, expected_type, expected_result",
184 [
185 (42, typing.Set[int], False),
186 ({1, 2, 3}, typing.Set[int], True),
187 (42, typing.Set[int], False),
188 ({1, 2, 3}, typing.Set[int], True),
189 ({1, 2, 3}, typing.Set[str], False),
190 ({"a", "b", "c"}, typing.Set[str], True),
191 ({1, "a", 3}, typing.Set[int], False),
192 (42, typing.Set[int], False),
193 ({1, 2, 3}, typing.Set[int], True),
194 ({1, "2", 3}, typing.Set[int], False),
195 ([1, 2, 3], typing.Set[int], False),
196 ("hello", typing.Set[str], False),
197 ],
198)
199def test_validate_type_set(value, expected_type, expected_result):
200 try:
201 assert validate_type(value, expected_type) == expected_result
202 except Exception as e:
203 raise Exception(
204 f"{value = }, {expected_type = }, {expected_result = }, {e}"
205 ) from e
208@pytest.mark.parametrize(
209 "value, expected_type, expected_result",
210 [
211 (42, typing.Tuple[int, str], False),
212 ((1, "a"), typing.Tuple[int, str], True),
213 (42, typing.Tuple[int, str], False),
214 ((1, "a"), typing.Tuple[int, str], True),
215 ((1, 2), typing.Tuple[int, str], False),
216 ((1, 2), typing.Tuple[int, int], True),
217 ((1, 2, 3), typing.Tuple[int, int], False),
218 ((1, "a", 3.14), typing.Tuple[int, str, float], True),
219 (("a", "b", "c"), typing.Tuple[str, str, str], True),
220 ((1, "a", 3.14), typing.Tuple[int, str], False),
221 ((1, "a", 3.14), typing.Tuple[int, str, float], True),
222 ([1, "a", 3.14], typing.Tuple[int, str, float], False),
223 (
224 (1, "a", 3.14, "b", True, None, (1, 2, 3)),
225 # no idea why this throws type error, only locally, and only for the generated modern types
226 typing.Tuple[ # type: ignore[misc]
227 int, str, float, str, bool, type(None), typing.Tuple[int, int, int]
228 ],
229 True,
230 ),
231 ],
232)
233def test_validate_type_tuple(value, expected_type, expected_result):
234 try:
235 assert validate_type(value, expected_type) == expected_result
236 except Exception as e:
237 raise Exception(
238 f"{value = }, {expected_type = }, {expected_result = }, {e}"
239 ) from e
242@pytest.mark.parametrize(
243 "value, expected_type",
244 [
245 (43, typing.Callable),
246 (lambda x: x, typing.Callable),
247 (42, typing.Callable[[], None]),
248 (42, typing.Callable[[int, str], typing.List]),
249 ],
250)
251def test_validate_type_unsupported_type_hint(value, expected_type):
252 with pytest.raises(NotImplementedError):
253 validate_type(value, expected_type)
254 print(f"Failed to except: {value = }, {expected_type = }")
257@pytest.mark.parametrize(
258 "value, expected_type, expected_result",
259 [
260 ([1, 2, 3], typing.List[int], True),
261 (["a", "b", "c"], typing.List[str], True),
262 ([1, "a", 3], typing.List[int], False),
263 ([1, 2, [3, 4]], typing.List[Union[int, typing.List[int]]], True),
264 ([(1, 2), (3, 4)], typing.List[typing.Tuple[int, int]], True),
265 ([(1, 2), (3, "4")], typing.List[typing.Tuple[int, int]], False),
266 ({1: [1, 2], 2: [3, 4]}, typing.Dict[int, typing.List[int]], True),
267 ({1: [1, 2], 2: [3, "4"]}, typing.Dict[int, typing.List[int]], False),
268 ],
269)
270def test_validate_type_collections(value, expected_type, expected_result):
271 try:
272 assert validate_type(value, expected_type) == expected_result
273 except Exception as e:
274 raise Exception(
275 f"{value = }, {expected_type = }, {expected_result = }, {e}"
276 ) from e
279@pytest.mark.parametrize(
280 "value, expected_type, expected_result",
281 [
282 # empty lists
283 ([], typing.List[int], True),
284 ([], typing.List[typing.Dict], True),
285 (
286 [],
287 typing.List[typing.Tuple[typing.Dict[typing.Tuple, str], str, None]],
288 True,
289 ),
290 # empty dicts
291 ({}, typing.Dict[str, int], True),
292 ({}, typing.Dict[str, typing.Dict], True),
293 ({}, typing.Dict[str, typing.Dict[str, int]], True),
294 ({}, typing.Dict[str, typing.Dict[str, int]], True),
295 # empty sets
296 (set(), typing.Set[int], True),
297 (set(), typing.Set[typing.Dict], True),
298 (
299 set(),
300 typing.Set[typing.Tuple[typing.Dict[typing.Tuple, str], str, None]],
301 True,
302 ),
303 # empty tuple
304 (tuple(), typing.Tuple, True),
305 # empty string
306 ("", str, True),
307 # empty bytes
308 (b"", bytes, True),
309 # None
310 (None, type(None), True),
311 # bools are ints, ints are not floats
312 (True, int, True),
313 (False, int, True),
314 (True, float, False),
315 (False, float, False),
316 (1, int, True),
317 (0, int, True),
318 (1, float, False),
319 (0, float, False),
320 (0, bool, False),
321 (1, bool, False),
322 # weird floats
323 (float("nan"), float, True),
324 (float("inf"), float, True),
325 (float("-inf"), float, True),
326 (float(0), float, True),
327 # list/tuple
328 ([1], typing.Tuple[int, int], False),
329 ((1, 2), typing.List[int], False),
330 ],
331)
332def test_validate_type_edge_cases(value, expected_type, expected_result):
333 try:
334 assert validate_type(value, expected_type) == expected_result
335 except Exception as e:
336 raise Exception(
337 f"{value = }, {expected_type = }, {expected_result = }, {e}"
338 ) from e
341@pytest.mark.parametrize(
342 "value, expected_type, expected_result",
343 [
344 (42, typing.List[int], False),
345 ([1, 2, 3], int, False),
346 (3.14, typing.Tuple[float], False),
347 (3.14, typing.Tuple[float, float], False),
348 (3.14, typing.Tuple[bool, str], False),
349 (False, typing.Tuple[bool, str], False),
350 (False, typing.Tuple[bool], False),
351 ((False,), typing.Tuple[bool], True),
352 (("abc",), typing.Tuple[str], True),
353 ("test-dict", typing.Dict[str, int], False),
354 ("test-dict", typing.Dict, False),
355 ],
356)
357def test_validate_type_wrong_type(value, expected_type, expected_result):
358 try:
359 assert validate_type(value, expected_type) == expected_result
360 except Exception as e:
361 raise Exception(
362 f"{value = }, {expected_type = }, {expected_result = }, {e}"
363 ) from e
366def test_validate_type_complex():
367 assert validate_type([1, 2, [3, 4]], typing.List[Union[int, typing.List[int]]])
368 assert validate_type(
369 {"a": 1, "b": {"c": 2}}, typing.Dict[str, Union[int, typing.Dict[str, int]]]
370 )
371 assert validate_type({1, (2, 3)}, typing.Set[Union[int, typing.Tuple[int, int]]])
372 assert validate_type((1, ("a", "b")), typing.Tuple[int, typing.Tuple[str, str]])
373 assert validate_type([{"key": "value"}], typing.List[typing.Dict[str, str]])
374 assert validate_type([{"key": 2}], typing.List[typing.Dict[str, str]]) is False
375 assert validate_type([[1, 2], [3, 4]], typing.List[typing.List[int]])
376 assert validate_type([[1, 2], [3, "4"]], typing.List[typing.List[int]]) is False
377 assert validate_type([(1, 2), (3, 4)], typing.List[typing.Tuple[int, int]])
378 assert (
379 validate_type([(1, 2), (3, "4")], typing.List[typing.Tuple[int, int]]) is False
380 )
381 assert validate_type({1: "one", 2: "two"}, typing.Dict[int, str])
382 assert validate_type({1: "one", 2: 2}, typing.Dict[int, str]) is False
383 assert validate_type([(1, "one"), (2, "two")], typing.List[typing.Tuple[int, str]])
384 assert (
385 validate_type([(1, "one"), (2, 2)], typing.List[typing.Tuple[int, str]])
386 is False
387 )
388 assert validate_type({1: [1, 2], 2: [3, 4]}, typing.Dict[int, typing.List[int]])
389 assert (
390 validate_type({1: [1, 2], 2: [3, "4"]}, typing.Dict[int, typing.List[int]])
391 is False
392 )
393 assert validate_type([(1, "a"), (2, "b")], typing.List[typing.Tuple[int, str]])
394 assert (
395 validate_type([(1, "a"), (2, 2)], typing.List[typing.Tuple[int, str]]) is False
396 )
399@pytest.mark.parametrize(
400 "value, expected_type, expected_result",
401 [
402 ([[[[1]]]], typing.List[typing.List[typing.List[typing.List[int]]]], True),
403 ([[[[1]]]], typing.List[typing.List[typing.List[typing.List[str]]]], False),
404 (
405 {"a": {"b": {"c": 1}}},
406 typing.Dict[str, typing.Dict[str, typing.Dict[str, int]]],
407 True,
408 ),
409 (
410 {"a": {"b": {"c": 1}}},
411 typing.Dict[str, typing.Dict[str, typing.Dict[str, str]]],
412 False,
413 ),
414 ({1, 2, 3}, typing.Set[int], True),
415 ({1, 2, 3}, typing.Set[str], False),
416 (
417 ((1, 2), (3, 4)),
418 typing.Tuple[typing.Tuple[int, int], typing.Tuple[int, int]],
419 True,
420 ),
421 (
422 ((1, 2), (3, 4)),
423 typing.Tuple[typing.Tuple[int, int], typing.Tuple[int, str]],
424 False,
425 ),
426 ],
427)
428def test_validate_type_nested(value, expected_type, expected_result):
429 try:
430 assert validate_type(value, expected_type) == expected_result
431 except Exception as e:
432 raise Exception(
433 f"{value = }, {expected_type = }, {expected_result = }, {e}"
434 ) from e
437def test_validate_type_inheritance():
438 class Parent:
439 def __init__(self, a: int, b: str):
440 self.a: int = a
441 self.b: str = b
443 class Child(Parent):
444 def __init__(self, a: int, b: str):
445 self.a: int = 2 * a
446 self.b: str = b
448 assert validate_type(Parent(1, "a"), Parent)
449 validate_type(Child(1, "a"), Parent, do_except=True)
450 assert validate_type(Child(1, "a"), Child)
451 assert not validate_type(Parent(1, "a"), Child)
453 with pytest.raises(IncorrectTypeException):
454 validate_type(Parent(1, "a"), Child, do_except=True)
457def test_validate_type_class():
458 class Parent:
459 def __init__(self, a: int, b: str):
460 self.a: int = a
461 self.b: str = b
463 class Child(Parent):
464 def __init__(self, a: int, b: str):
465 self.a: int = 2 * a
466 self.b: str = b
468 assert validate_type(Parent, type)
469 assert validate_type(Child, type)
470 assert validate_type(Parent, typing.Type[Parent], do_except=True)
471 assert validate_type(Child, typing.Type[Child])
472 assert not validate_type(Parent, typing.Type[Child])
474 assert validate_type(Child, typing.Union[typing.Type[Child], typing.Type[Parent]])
475 assert validate_type(Child, typing.Union[typing.Type[Child], int])
478@pytest.mark.skip(reason="Not implemented")
479def test_validate_type_class_union():
480 class Parent:
481 def __init__(self, a: int, b: str):
482 self.a: int = a
483 self.b: str = b
485 class Child(Parent):
486 def __init__(self, a: int, b: str):
487 self.a: int = 2 * a
488 self.b: str = b
490 class Other:
491 def __init__(self, x: int, y: str):
492 self.x: int = x
493 self.y: str = y
495 assert validate_type(Child, typing.Type[typing.Union[Child, Parent]])
496 assert validate_type(Child, typing.Type[typing.Union[Child, Other]])
497 assert validate_type(Parent, typing.Type[typing.Union[Child, Other]])
498 assert validate_type(Parent, typing.Type[typing.Union[Parent, Other]])
501def test_validate_type_aliases():
502 AliasInt = int
503 AliasStr = str
504 AliasListInt = typing.List[int]
505 AliasListStr = typing.List[str]
506 AliasDictIntStr = typing.Dict[int, str]
507 AliasDictStrInt = typing.Dict[str, int]
508 AliasTupleIntStr = typing.Tuple[int, str]
509 AliasTupleStrInt = typing.Tuple[str, int]
510 AliasSetInt = typing.Set[int]
511 AliasSetStr = typing.Set[str]
512 AliasUnionIntStr = typing.Union[int, str]
513 AliasUnionStrInt = typing.Union[str, int]
514 AliasOptionalInt = typing.Optional[int]
515 AliasOptionalStr = typing.Optional[str]
516 AliasOptionalListInt = typing.Optional[typing.List[int]]
517 AliasDictStrListInt = typing.Dict[str, typing.List[int]]
519 assert validate_type(42, AliasInt)
520 assert not validate_type("42", AliasInt)
521 assert validate_type(42, AliasInt)
522 assert not validate_type("42", AliasInt)
523 assert validate_type("hello", AliasStr)
524 assert not validate_type(42, AliasStr)
525 assert validate_type([1, 2, 3], AliasListInt)
526 assert not validate_type([1, "2", 3], AliasListInt)
527 assert validate_type(["hello", "world"], AliasListStr)
528 assert not validate_type(["hello", 42], AliasListStr)
529 assert validate_type({1: "a", 2: "b"}, AliasDictIntStr)
530 assert not validate_type({1: 2, 3: 4}, AliasDictIntStr)
531 assert validate_type({"one": 1, "two": 2}, AliasDictStrInt)
532 assert not validate_type({1: "one", 2: "two"}, AliasDictStrInt)
533 assert validate_type((1, "a"), AliasTupleIntStr)
534 assert not validate_type(("a", 1), AliasTupleIntStr)
535 assert validate_type(("a", 1), AliasTupleStrInt)
536 assert not validate_type((1, "a"), AliasTupleStrInt)
537 assert validate_type({1, 2, 3}, AliasSetInt)
538 assert not validate_type({1, "two", 3}, AliasSetInt)
539 assert validate_type({"one", "two"}, AliasSetStr)
540 assert not validate_type({"one", 2}, AliasSetStr)
541 assert validate_type(42, AliasUnionIntStr)
542 assert validate_type("hello", AliasUnionIntStr)
543 assert not validate_type(3.14, AliasUnionIntStr)
544 assert validate_type("hello", AliasUnionStrInt)
545 assert validate_type(42, AliasUnionStrInt)
546 assert not validate_type(3.14, AliasUnionStrInt)
547 assert validate_type(42, AliasOptionalInt)
548 assert validate_type(None, AliasOptionalInt)
549 assert not validate_type("42", AliasOptionalInt)
550 assert validate_type("hello", AliasOptionalStr)
551 assert validate_type(None, AliasOptionalStr)
552 assert not validate_type(42, AliasOptionalStr)
553 assert validate_type([1, 2, 3], AliasOptionalListInt)
554 assert validate_type(None, AliasOptionalListInt)
555 assert not validate_type(["1", "2", "3"], AliasOptionalListInt)
556 assert validate_type({"key": [1, 2, 3]}, AliasDictStrListInt)
557 assert not validate_type({"key": [1, "2", 3]}, AliasDictStrListInt)