-
-
Notifications
You must be signed in to change notification settings - Fork 409
Description
Hi 👋 In the last days, I noticed some inconsistent behavior of deep_iterable and deep_mapping. Some of the problems have already been mentioned in other issues (links provided below) but for others it seems there is no open issue yet. Since they are partly related, I thought it would be good to have a single overview of the necessary open fixes:
Lists/tuples of validators
Some of the inner validators accept lists while others still require the explicit use of and_. For consistency, I think all of them should ideally be able to handle lists. For the member_validator, the change has been in implemented in #925, but iterable_validator still struggles with lists. For example, the following piece of code works, but replacing any_ with a list will make it fail:
@define
class A:
x: List[str] = field(
validator=deep_iterable(
member_validator=[instance_of(str), min_len(1)],
iterable_validator=and_(instance_of(list), min_len(1)),
),
)For deep_mapping, lists/tuples not supported for any of its three arguments.
Typing support
When a list of validators is passed, mypy complains about the type, as already pointed out in #1197. However, when a tuple is used instead, mypy is happy.
Wrong error message
When a validation error in one of the inner validators of deep_iterable or deep_mapping occurs, an exception is thrown but the contained message is wrong. For example, using the above code and calling A(["abc", ""]), you get the following error:
ValueError: Length of 'x' must be => 1: 0It rightfully complains about the length of the second item in the list, but note that the message refers to attribute name x, which is incorrect, since it's not x that is too short but one of its items.
Optional validators
For deep_iterable, only the iterable_validator is optional, which makes sense because if no member_validator was provided, one should simply use a regular validator in the first place. However, for deep_mapping, both value_validator and key_validator are required. This makes its use unnecessarily complicated in certain situations. For example, validating only dictionary keys requires a rather cumbersome workaround and also results in unnecessary calls of the value_validator:
@define
class B:
x: Dict[str, Any] = field(
validator=deep_mapping(
key_validator=and_(instance_of(str), min_len(1)),
value_validator=lambda *x: None,
)
)Instead, I guess the user-friendly way would be to check that at least one of them is provided, but not necessarily both.