David Gourde
David Gourde

Reputation: 3914

Getting a better Voluptuous Schema error message for key type validation?

I have a Schema that looks like this (an example):

Schema({
    Any(str, unicode): [{
        Required('first_name'): [Any(str, unicode)],
        Required('age'): Any('int32', 'double'),
        Required('something'): Any(int, long, float, str, unicode)
    }]
})

If I pass an int (let's say 42) as the key (Any(str, unicode)), I get:

<class 'voluptuous.error.Invalid'> : extra keys not allowed @ data[42]. Got ["my_value"]

This does not represent my error very well, as it does not even inform us that the problem is actually the type of the key. The error for a value type is perfect, like so: expected str for dictionary value @ data[0]["my_key"]. Got 42

Is there a way to get a clearer message for key type validation using a voluptuous' Schema, like so?

expected str for dictionary key type. Got 42

PS: Or could it be that my Schema is incorrect? My goal is to have a dictionary where the keys are strings or unicode (this is an example) and the values are lists of dictionaries with specific keys that have certain value types.

Update

I tried to put the key validation in another Schema, to get the right error message, like so:

KEY_SCHEMA = Schema(Any(str, unicode))

def validate_key(my_key):
    KEY_SCHEMA (my_key) # Here the correct error/message is raised

Schema({
    validate_key: [{
        Required('first_name'): [Any(str, unicode)],
        Required('age'): Any('int32', 'double'),
        Required('something'): Any(int, long, float, str, unicode)
    }]
})

The error message I get from the KEY_SCHEMA is "ok" and I could do a try/except raise to output an even nicer error message, but it's getting catched by the main Schema and the same error as before is returned again.

Upvotes: 3

Views: 2167

Answers (1)

nulse
nulse

Reputation: 873

I ran into the exact same problem. This is not about your schema, but about the way voluptuous works.

Because you define a Schema for your dictionary keys and not static values, it allows the dictionary to contain multiple keys that match that schema. The extra parameter of your parent schema is to False (the default), which means any extra element (any key that doesn't match your inner schema) won't be accepted (which is great, otherwise you couldn't ensure all keys are validated against your inner schema). Here is the problem, the extra=False condition takes precedence over the non validation of the inner schema. The integer key is then considered as an extra key because it doesn't match the schema, before being considered as an Invalid. Validating multiple keys with same schema isn't as effective and explicit as values validation in voluptuous.

That being said, I ended up with a (dirty) solution. I only wanted to output a message saying the key schema does not match on a given key, before it is printed as an extra parameter. I did the same as you did by defining a function for the key validation. This function validates itself the key and prints an error message if it doesn't. Raising an Invalid will trigger the extra parameter error message, so I exit my program before (it fits my needs, but this is not necessary true for anyone else).

In your case it could be something like that:

KEY_SCHEMA = Schema(Any(str, unicode))

def validate_key(my_key):
    if type(my_key) is not str and type(my_key) is not unicode:
        logger.error("{} is not a str or unicode. Aborting.") # if you use the logger library
        sys.exit(1) # need to import sys
    return KEY_SCHEMA (my_key)

Schema({
    validate_key: [{
        Required('first_name'): [Any(str, unicode)],
        Required('age'): Any('int32', 'double'),
        Required('something'): Any(int, long, float, str, unicode)
    }]
})

Upvotes: 2

Related Questions