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
13 changes: 11 additions & 2 deletions src/attrs_strict/_type_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,10 @@ 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
if not hasattr(expected_type, "__args__"):
return # No annotations specified on type, matches all sets or lists

(element_type,) = expected_type.__args__

for element in container:
try:
Expand All @@ -282,7 +285,10 @@ 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
if not hasattr(expected_type, "__args__"):
return # No annotations specified on type, matches all dicts

key_type, value_type = expected_type.__args__

for key in container:
try:
Expand All @@ -298,6 +304,9 @@ def _handle_tuple(
container: tuple[typing.Any],
expected_type: type[tuple[typing.Any]],
) -> None:
if not hasattr(expected_type, "__args__"):
return # No annotations specified on type, matches all tuples

tuple_types = expected_type.__args__ # type: ignore
if len(tuple_types) == 2 and tuple_types[1] == Ellipsis:
element_type = tuple_types[0]
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
Loading