Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support types without args #108

Merged
merged 9 commits into from
Dec 13, 2023
24 changes: 19 additions & 5 deletions src/attrs_strict/_type_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
from types import UnionType

if sys.version_info >= (3, 8): # pragma: >=3.8 cover
from typing import Literal
from typing import Literal, get_args
else: # pragma: <3.8 cover
from typing_extensions import Literal
from typing_extensions import Literal, get_args

import attr

Expand Down Expand Up @@ -265,7 +265,12 @@ def _handle_set_or_list(
container: set[typing.Any] | list[typing.Any],
expected_type: type[set[typing.Any]] | type[list[typing.Any]],
) -> None:
(element_type,) = expected_type.__args__ # type: ignore
args = get_args(expected_type)

if not args:
return # No annotations specified on type, matches all sets or lists

(element_type,) = args

for element in container:
try:
Expand All @@ -282,7 +287,12 @@ def _handle_dict(
expected_type: type[typing.Mapping[typing.Any, typing.Any]]
| type[typing.MutableMapping[typing.Any, typing.Any]],
) -> None:
key_type, value_type = expected_type.__args__ # type: ignore
args = get_args(expected_type)

if not args:
return # No annotations specified on type, matches all dicts

key_type, value_type = args

for key in container:
try:
Expand All @@ -298,7 +308,11 @@ def _handle_tuple(
container: tuple[typing.Any],
expected_type: type[tuple[typing.Any]],
) -> None:
tuple_types = expected_type.__args__ # type: ignore
tuple_types = get_args(expected_type)

if not tuple_types:
return # No annotations specified on type, matches all tuples

if len(tuple_types) == 2 and tuple_types[1] == Ellipsis:
element_type = tuple_types[0]
tuple_types = (element_type,) * len(container)
Expand Down
5 changes: 5 additions & 0 deletions tests/test_callable__py3.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ def int_default_returns_int(a=123): # noqa: U100,N805
_TestResources.int_default_returns_int,
typing.Callable[[int], int],
),
(
"callable_with_no_args",
_TestResources.plain_unannotated_callable,
typing.Callable,
),
],
)
def test_callable_not_raises_with_valid_annotations(
Expand Down
19 changes: 19 additions & 0 deletions tests/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ def test_defaultdict_with_correct_type_no_raise():
validator(None, attr, elem)


@pytest.mark.parametrize(
("data", "type"),
[
({}, dict),
({}, Dict),
({"a": 1}, dict),
({"a": 1}, Dict),
],
)
def test_dict_without_args_any_does_not_raise(data, type):
validator = type_validator()

attr = MagicMock()
attr.name = "zoo"
attr.type = type

validator(None, attr, data)


def test_dict_with_any_does_not_raise():
elem = {"foo": 123, "b": "abc"}

Expand Down
2 changes: 2 additions & 0 deletions tests/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ def test_list_of_values_raise_value_error(values, type_, error_message):
([{1: [1, 2, 3], 2: [3, 4, 5]}], List[Dict[int, List[int]]]),
([1, 2, 3, 4], List[Any]),
([1, 2, {"foo": "bar"}], List[Any]),
([1, "2", 3.5], list),
([1, "2", 3.5], List),
],
)
def test_list_of_valid_values_no_raise(values, type_):
Expand Down
17 changes: 17 additions & 0 deletions tests/test_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,23 @@ def test_variable_length_tuple_empty():
validator(None, attr, element)


@pytest.mark.parametrize(
("data", "type"),
[
((1, 2, 3), tuple),
((1, 2, 3), Tuple),
],
)
def test_tuple_without_args(data, type):
attr = MagicMock()
attr.name = "foo"
attr.type = type

validator = type_validator()

validator(None, attr, data)


def test_variable_length_tuple_raises():
element = (1, 2, 3, "4")

Expand Down