0

I have a variable declaration as follows

my_var = typing.List[typing.Tuple[int, int]]

and I want to write a validator as follows

schema_validator = "my_var": {
    "type": "list",
    "empty": False,
    "items": [
        {"type": "tuple"},
        {"items": [
            {"type": "int"}, {"type": "int"}
        ]}
    ]
}

In Cerberus documentation it does not specify a validator example for tuples.

How to accomplish this?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
not 0x12
  • 19,360
  • 22
  • 67
  • 133

2 Answers2

1

While this isn't the cleanest solution, it will certainly do what you want.

from cerberus import Validator, TypeDefinition

class MyValidator(Validator):
    def __init__(self, *args, **kwargs):
        # Add the tuple type
        tuple_type = TypeDefinition("tuple", (tuple,), ())
        Validator.types_mapping["tuple"] = tuple_type
        # Call the Validator constructor
        super(MyValidator, self).__init__(*args, **kwargs)

    def _validate_is_int_two_tuple(self, is_int_two_tuple, field, value):
        ''' Test that the value is a 2-tuple of ints

        The rule's arguments are validated against this schema:
        {'type': 'boolean'}
        '''
        if is_int_two_tuple:
            # Check the type
            if type(value) != tuple:
                self._error(field, "Must be of type 'tuple'")
            # Check the length
            if len(value) != 2:
                self._error(field, "Tuple must have two elements")
            # Check the element types
            if type(value[0]) != int or type(value[1]) != int:
                self._error(field, "Both tuple values must be of type 'int'")

data = {"mylist": [(1,1), (2,2), (3,3)]}

schema = {
    "mylist": {
        "type": "list",
        "schema": {
            "type": "tuple",
            "is_int_two_tuple": True
        }
    }
}

v = MyValidator(schema)
print("Validated: {}".format(v.validate(data)))
print("Validation errors: {}".format(v.errors))
print("Normalized result: {}".format(v.normalized(data)))

So as bro-grammer pointed out, the custom data types will get you validation of the types, but that's it. From the schema that you provided, it looks like you also want to validate other features like the length of the tuple and the types of the elements in the tuple. Doing that requires more than just a simple TypeDefinition for tuples.

Extending Validator to include a rule for this specific use-case isn't ideal, but it will do what you want. The more comprehensive solution would be to create a TupleValidator subclass that has rules for validating length, element-types, order, etc. of tuples.

Flargebla
  • 76
  • 3
1

Given your typevar typing.List[typing.Tuple[int, int]], you expect an arbritrary length list of two-value tuples where each value is an integer.

class MyValidator(Validator):
    # add a type definition to a validator subclass
    types_mapping = Validator.types_mapping.copy()
    types_mapping['tuple'] = TypeDefinition((tuple,), ())


schema = {
    'type': 'list',
    'empty': False,
    'schema': {  # the number of items is undefined
        'type': 'tuple',
        'items': 2 * ({'type': 'int'},)
    }
}

validator = MyValidator(schema)

It's important to understand the difference of the items and the schema rule.

Mind that the default list type actually maps to the more abstract Sequence type and you might want to add another, stricter type for that.

funky-future
  • 3,716
  • 1
  • 30
  • 43