This package contains a set of simple functions to facilitate creation of chainable validator methods for developing data validation schemata.
Although it provides a few validation function, it's main purpose is extensibility.
The PyPI package name is chainable-validators
. You can install the package
using pip
or easy_install
:
pip install chainable-validators easy_install chainable-validators
The basis of all validation are chainable validators (validation functions) and
the validation chains which are created using make_chain()
calls.
The chainable validators are found in the validators.validators
module. For
convenience, they can be imported directly from the validators
package:
>>> from validators import required, istype, gte
The chainable validators can be used stand-alone or as part of a chain. For standalone usage, pass the value to the validator.
>>> required(None) Traceback (most recent call last): ... ValueError: required value missing
Some validators are parametric. They are first invoked with a parameter, and the return value is then used as a chainable validator.
>>> isint = istype(int) >>> isint('foo') Traceback (most recent call last): ... ValueError: not int
To build a chain of desired validators, we first compile a list of chainable validators:
>>> from validators import make_chain >>> fns = [required, istype(int), gte(2)] >>> chain = make_chain(fns) >>> chain(None) Traceback (most recent call last): ... ValueError: required value missing >>> chain('1') Traceback (most recent call last): ... ValueError: not int >>> chain(1) Traceback (most recent call last): ... ValueError: value too small >>> chain(3) 3
The validators in the chain are invoked in order until the last one is called,
or ValueError
or validators.ReturnEarly
is raised. When no exceptions
are raised, the return value of the last chainable validator is returned. In
case ReturnEarly
is raised, it is not propagated to the chain's caller, but
instead the original value is returned.
The following is a list of built-in vaidators.
Validators can be parametric or simple. Simple validators can be used directly. Parametric validators take an argument and return a chainable validator functions.
In the list below, if you encounter a validator that looks like foo(bar)
,
it's a parametric validator.
optional(default=None)
- interrupts validation chain if value is None or a user-supplied default valuerequired
- rejects Nonenonemtpy
- rejects empty sequences (string, list, dict)boolean
- reject non-boolean values (other than True, False, 1, and 0)istype(t)
- rejects values that are not of typet
isin(collection)
- rejects values that are not incollection
(collection is a sequence such as string, list, or dict)gte(num)
- rejects values that are not greater than or equal tonum
lte(num)
- rejects values that are not less than or equal tonum
match(regex)
- rejects values that do not match theregex
object (regex
object is a validre.RegExp
instance or object with amatch()
method)url
- rejects values that are not URLstimestamp(fmt)
- rejects values that cannot be converted todatetime
usingdatetime.strptime()
and given format string
There are a few helper functions that help you modify the behavior or one or more functions in the chain.
OR(*fns)
- rejects value if and only if all of the functions passed to it fail to validate, otherwise passesNOT(fn)
- reverses the behavior offn
For example:
>>> my_chain = [foo, OR(bar, baz), NOT(fam)]
This chain will validate using foo
first, then either bar
or baz
(whichever passes), then fam
, reversing the results of fam
(if it
raises, then the validation will succeed and vice versa).
Another helper function, that is not mentioned in the previous section, is
spec_validator()
factory function. It takes a spec, which is a dict mapping
key/attribute names to chains and returns a validator function that validates
objects.
Let's take a look at an example, and then explain things as we go:
>>> import re >>> from validator import * >>> spec = { ... 'foo': [required, istype(int)], ... 'bar': [optional, match(re.compile(r'te.*')], ... 'baz': [optional, boolean] ... }
Each key in spec represents the key we expect to find in the object. The key could be a dictionary key, list/tuple index, or an object attribute. It could also be an arbitrary value based on which the value will be extracted.
The way keys map to values is defined by a key function which can be passed
using the key
argument. This function must accept a spec key name and
return a function that returns the value given an object. The default key
function is operator.itemgetter
. For example, if we have an object that as
attributes we want to validate, we could create the validator like so:
>>> import operator >>> attr_validator = spec_validator(spec, key=operator.attrgetter)
Each key maps to an iterable which represents the validator chain. Chains are applied to values matching the key.
The spec_validator()
function returns a validator function.
>>> validator = spec_validator(spec)
When passed the object to be validated, the validator function returns a dict
which maps keys to any ValueError
exceptions raised by the individual
chains. If data is valid, the dict is empty.
>>> data = {'foo': 1, 'bar': 'test', 'baz': None} >>> validator(data) {} >>> data['foo'] = None >>> validator(data) {'foo': ValueError('required value is missing')}
Thanks to this behavior, you can test whether object is valid, by testing if the returned dict is empty.
It is possible to write your own validators. To write a simple chainable
validator, use the validators.chain.chainable
decorator.
>>> from validators import chainable >>> @chainable ... def my_validator(s): ... if not s.startswith('foo'): ... raise ValueError('does not start with foo') ... return s ... >>> my_validator('foobar') 'foobar' >>> my_validator('barfoo') Traceback (most recent call last): ... ValueError: does not start with foo
To write a parametric validator, define the chainable validator in a closure:
>>> def my_parametric(start): ... @chainable ... def validator(s): ... if not s.startswith(start): ... raise ValueError('does not sart with {}'.format(start)) ... return s ... return validator ... >>> validator = my_parametric('baz') >>> validator('bazfoo') 'bazfoo' >>> validator('foo') Traceback (most recent call last): ... ValueError: does not sart with baz
Now you can use these validators in chains like other validators.
Please report any bugs or feature requests to the issue tracker.