-1

I'm attempting to add custom data types to Cerberus. The UUID class works as expected (it's a standard library class) but I'm not able to validate with the UUID type using Cerberus.

Secondarily I was not able to register multiple types in an an __init__ function but that probably should be it's own question.

Here's my custom validator that should register the given types.

import cerberus

class UUID:
    name = "UUID"

    def __init__(self, potential_uuid: str):
        self.uuid = uuid.UUID(potential_uuid)

    def __str__(self):
        return str(self.uuid)

class Validator(cerberus.Validator):
    def _register_types(self) -> cerberus.Validator.types_mapping:
        types_mapping = Validator.types_mapping.copy()
        for schema_type in datatypes.ALL_TYPES:
            cerberus_type = cerberus.TypeDefinition(
                schema_type.name,
                (schema_type,),
                ())
            types_mapping[schema_type.name] = cerberus_type
        return types_mapping

    cerberus_type = cerberus.TypeDefinition(
        "UUID",
        (datatypes.UUID,),
        ())
    types_mapping = cerberus.Validator.types_mapping.copy()
    types_mapping["UUID"] = cerberus_type

    #def __init__(self, *args, **kwargs ):
    #    types_mapping = self._register_types()
    #    super().__init__(*args, **kwargs)

And here's my unit tests for this code.

@pytest.mark.unit
def test_valid_uuid():
    test_input = "35d6d5a0-6f37-4794-a493-2712eda41c1a"
    actual = UUID(test_input)
    assert str(actual) == "35d6d5a0-6f37-4794-a493-2712eda41c1a"

@pytest.mark.unit
def test_invalid_uuid():
    test_input = "Not a Valid UUID"
    with pytest.raises(ValueError):
        actual = UUID(test_input) 

@pytest.mark.unit
def test_uuid_type_registration():
    test_schema = {"test_name": {"type": "UUID"}}
    validator = Validator(test_schema)
    test_record = {"test_name": "35d6d5a0-6f37-4794-a493-2712eda41c1a"}
    result = validator.validate(test_record)
    print(validator._errors)
    assert result == True

If we just give the UUID class a valid UUID it succeeds but if we attempt to validate it through Cerberus we get a BAD_TYPE validation error.

pytest tests/test_datatypes/test_datatypes.py
============================================================================================================================= test session starts ==============================================================================================================================
platform linux -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
benchmark: 3.2.2 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/vdev, inifile: setup.cfg
plugins: cov-2.6.1, benchmark-3.2.2
collected 4 items

tests/test_datatypes/test_datatypes.py ...F                                                                                                                                                                                                                              [100%]

=================================================================================================================================== FAILURES ===================================================================================================================================
_________________________________________________________________________________________________________________________ test_uuid_type_registration __________________________________________________________________________________________________________________________

    @pytest.mark.unit
    def test_uuid_type_registration():
        test_schema = {"test_name": {"type": "UUID"}}
        validator = Validator(test_schema)
        test_record = {"test_name": "35d6d5a0-6f37-4794-a493-2712eda41c1a"}
        result = validator.validate(test_record)
        print(validator._errors)
>       assert result == True
E       assert False == True

tests/test_datatypes/test_datatypes.py:30: AssertionError
----------------------------------------------------------------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------------------------------------------------------------
[ValidationError @ 0x7fa477e10278 ( document_path=('test_name',),schema_path=('test_name', 'type'),code=0x24,constraint="UUID",value="35d6d5a0-6f37-4794-a493-2712eda41c1a",info=() )]
=============================================================================================================================== warnings summary ===============================================================================================================================
/usr/local/lib/python3.7/site-packages/cerberus/validator.py:14
/usr/local/lib/python3.7/site-packages/cerberus/validator.py:14
/usr/local/lib/python3.7/site-packages/cerberus/validator.py:14
/usr/local/lib/python3.7/site-packages/cerberus/validator.py:14
  /usr/local/lib/python3.7/site-packages/cerberus/validator.py:14: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
    from collections import Hashable, Iterable, Mapping, Sequence

/usr/local/lib/python3.7/site-packages/cerberus/errors.py:6
  /usr/local/lib/python3.7/site-packages/cerberus/errors.py:6: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
    from collections import defaultdict, namedtuple, MutableMapping

/usr/local/lib/python3.7/site-packages/cerberus/schema.py:3
  /usr/local/lib/python3.7/site-packages/cerberus/schema.py:3: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
    from collections import (Callable, Hashable, Iterable, Mapping,

-- Docs: https://docs.pytest.org/en/latest/warnings.html
================================================================================================================ 1 failed, 3 passed, 6 warnings in 0.33 seconds ===============================================================================================================

EDIT 1

Simplified example code

import cerberus
import uuid


class Validator(cerberus.Validator):
    types_mapping = {
        **cerberus.Validator.types_mapping,
        'UUID': cerberus.TypeDefinition('UUID', (uuid.UUID,), ())
    }

Same failure

    @pytest.mark.unit
    def test_uuid_type_registration():
        test_schema = {"test_name": {"type": "UUID"}}
        validator = es_client.Validator(test_schema)
        test_record = {"test_name": "35d6d5a0-6f37-4794-a493-2712eda41c1a"}
        result = validator.validate(test_record)
        print(validator._errors)
>       assert result == True
E       assert False == True

tests/test_datatypes/test_datatypes.py:30: AssertionError
------------------------------------------------------------ Captured stdout call -------------------------------------------------------------
[ValidationError @ 0x7fd9cdeed0b8 ( document_path=('test_name',),schema_path=('test_name', 'type'),code=0x24,constraint="UUID",value="35d6d5a0-6f37-4794-a493-2712eda41c1a",info=() )]
AlexLordThorsen
  • 8,057
  • 5
  • 48
  • 103

1 Answers1

1

Could you clarify what the _register_types method is meant to do and when it is called?

This works, maybe it helps you find your error:

def test_issue_475():
    class UUID:
        def __init__(self, data):
            self.data = data

    class MyValidator(Validator):
        types_mapping = {
            **Validator.types_mapping,
            'UUID': TypeDefinition('UUID', (UUID,), ())
        }

    assert_success(
        {'field': UUID(0)},
        {'field': {'type': 'UUID'}},
        validator=MyValidator()
    )

Note that you mention the sdtlib's UUID class while you implement another one with the same name in your example.

funky-future
  • 3,716
  • 1
  • 30
  • 43
  • I should have just taken `_register_types` out. It's what I want to do later on to add a list of types but it just confuses this question. – AlexLordThorsen Mar 22 '19 at 17:05
  • The Pytest failure is not on validator creation, it's on the `validate` call. – AlexLordThorsen Mar 22 '19 at 17:05
  • As why why did I make an extended UUID class? Just adding the name for registration. Probably could have done the same with a mapping object. – AlexLordThorsen Mar 22 '19 at 17:08
  • @AlexLordThorsen as i wrote, your question is unclear whether you target the stdlib's UUID or your own UUID class. my example uses the latter. – funky-future Mar 26 '19 at 13:33
  • funky-future, I switch my code to mirror yours and I'm still getting then same failure. Unsure at this time why there's divergence since it looks like mine should pass. – AlexLordThorsen Mar 26 '19 at 18:22