- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 407
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.