Skip to content

deep_iterable and deep_mapping inconsistencies #1206

@AdrianSosic

Description

@AdrianSosic

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: 0

It 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions