Onctuous is a fluid and pleasing to use validation tool you will love to use. Originally based on Voluptuous code by Alec Thomas <alec@swapoff.org>, we first fixed long outstanding issues like Python builtins collision and added support for default values.
You can use Onctuous to validate list, scalar (regular variables) or dict. For this purpose, you will need to define a so-called Schema and call the Schema with the input to validate. In case of success, it will return the validated input, possibly filtered or edited according to your rules
$ pip install onctuous
from onctuous import Schema
validate_is_int = Schema(int)
# Validate 42 (this will run fine)
validated = validate_is_int(42)
# Validate "toto" (this will raise ``InvalidList`` containing a list of errors)
validated = validate_is_int("toto")
Using the same idea, you can validate a list of int
from onctuous import Schema
validate_is_int_list = Schema([int])
# This will run fine
validated = validate_is_int_list([42, 2, 7])
# This will raise ``InvalidList`` containing a list of errors
validated = validate_is_int_list([2, 7, "toto"])
But we can also use on of the bundled validators and check the URL looks to be valid for example and even supply a custom error message!
from onctuous import Schema, Url
validate_is_urls = Schema([Url(msg="Ooops, this is *not* a valid URL")])
# This will run fine
validated = validate_is_urls(["www.example.com", "ftp://user:pass@ftp.example.com:42/toto?weird/path"])
# This will raise ``InvalidList`` containing a list of errors
validated = validate_is_urls([2, 7, "toto"])
Again, this is the same concept with some more niceties. For example, here is a basic user schema:
from onctuous import Schema, Url
validate_user = Schema({
'firstname': unicode,
'lastname': unicode,
'age': int,
'website': Url(msg="Ooops, this is *not* a valid URL"),
})
# use it...
But wait, I don’t want megative ages, do I ?
from onctuous import Schema, Url, InRange, All
validate_user = Schema({
'firstname': unicode,
'lastname': unicode,
'age': All(int, InRange(min=0, msg="Uh, ages can not be negative...")),
'website': Url(msg="Ooops, this is *not* a valid URL"),
})
# use it...
Have you noticed how this uses All to specify that both int and range conditions must ne met ?
What if I want to make the “Website” field optional ? Let me introduce Markers
from onctuous import Schema, Url, InRange, All, Optional
validate_user = Schema({
'firstname': unicode,
'lastname': unicode,
'age': All(int, InRange(min=0, msg="Uh, ages can not be negative...")),
Optional('website'): Url(msg="Ooops, this is *not* a valid URL"),
})
# use it...
You could also have used the ‘Required’ Marker with a default value. This is very usefull if you do not want to spend your whole time writing if key in data....
from onctuous import Schema, Url, InRange, All, Required
validate_user = Schema({
'firstname': unicode,
'lastname': unicode,
'age': All(int, InRange(min=0, msg="Uh, ages can not be negative...")),
Required('website', "#"): Url(msg="Ooops, this is *not* a valid URL"),
})
# use it...
It is worth noting that that the provided default value does not need to pass validations. You can use it as a “Marker” further in you application.
You can nest shemas. You actually did it in the previous example where scalars are nested into a dict or a list. But you can arbitrarily nest lists into dict and the other way around, as you need.
For example, let’s say you are writing a blog post which obviously has an author and maybe some tags whose len are between 3 and 20 chars included.
from onctuous import Schema, All, Required, Length, InRange
# Same schema as user above. I just removed the Schema instanciation but
# could have kept it. It's just more natural
user = {
'firstname': unicode,
'lastname': unicode,
'age': All(int, InRange(min=0, msg="Uh, ages can not be negative...")),
Required('website', "#"): Url(msg="Ooops, this is *not* a valid URL"),
}
validate_post = Schema({
'title': unicode,
'body': unicode,
'author': user, # look how you can split a schema into re-usable chunks!
Optional('tags'): [All(unicode, Length(min=3, max=20))],
Required('website', "#"): Url(msg="Ooops, this is *not* a valid URL"),
})
# use it...
That’s all for nesting.
You could also use the Extra special key to allow extra fields to be present while still being valid.
When instanciating the schema, there are also a global required and extra parameters that can optionally be set. They both default to False
There are tons of bundled validators, see the full API documentation for a full list.