Skip to content

'Is Valid' is a simple lightweight python library to create validation predicates.

License

Notifications You must be signed in to change notification settings

daanvdk/is_valid

Repository files navigation


Is Valid

pypi_version python_versions build_status code_coverage

'Is Valid' is a simple lightweight python library for validation predicates. Predicates are functions that given a certain input return either True or False according to some rules that the input either follows or does not follow.

Example

Suppose we have the following data about some books.

>>> books = [
...     {
...         'title': 'A Game of Thrones',
...         'author': 'George R. R. Martin',
...         'pubdate': datetime.date(1996, 8, 1),
...         'ISBN': '0553103547'
...     },
...     {
...         'title': 'Harry Potter and the Philosopher\'s Stone',
...         'author': 'J. K. Rowling',
...         'pubdate': '1997-6-26',
...         'ISBN': '0747532699'
...     },
...     {
...         'title': 'The Fellowship of the Ring',
...         'author': 'J. R. R. Tolkien',
...         'pubdate': datetime.date(1954, 7, 29),
...         'ISBN': '0-618-57494-8'
...     }
... ]

We want to validate this data according to a rather strict specification. To do this we are going to write a predicate that validates our data.

>>> from is_valid import is_list_of, is_dict_where, is_str, is_date, is_match
>>> is_list_of_books = is_list_of(
...     is_dict_where(
...         title=is_str,
...         author=is_str,
...         pubdate=is_date,
...         ISBN=is_match(r'^\d{9}(\d|X)$')
...     )
... )

You can see that using only a few simple predicates you can easily create a predicate that evaluates complex structures of data. Now lets analyze our data!

>>> is_list_of_books(books)
False

Okay, so now we know that our data isn't valid according to the specification. But what if we now want to fix this. In this case just True or False doesn't cut it, you might want to know why your data is valid or not. For this purpose all predicates include an explain method. If you call this method the predicate will return a special Explanation-object as result. All Explanation objects have a valid, code, and message attribute.

>>> explanation = is_list_of_books.explain(books)
>>> explanation.valid
False
>>> explanation.message
'not all elements are valid according to the predicate'

So in our case the explanation message is still not very useful. Luckily Explanation-objects often have a details attribute as well that contains more specific information. In the case of predicates generated by is_list_of this is a mapping from indexes in the list that weren't valid to an Explanation-object explaining why they weren't valid. So lets use this to get some more insight into our data.

>>> for i, subexplanation in explanation.details.items():
...     print(i, subexplanation.message)
1 data is not a dict where all the given predicates hold
2 data is not a dict where all the given predicates hold

Still not specific enough, however as you might have guessed already, this subexplanation also has a details attribute. For predicates generated by is_dict_where this is a mapping of keys to an Explanation-object explaining why the value associated with that key was not valid (if the dictionary has the right keys). So lets have a look.

>>> for i, subexplanation in explanation.details.items():
...     for key, subsubexplanation in subexplanation.details.items():
...         print(i, key, subsubexplanation.message)
1 pubdate data is not a date
2 ISBN data does not match /^\d{9}(\d|X)$/

So now we've found the issues! Let's try fixing them and evaluating again.

>>> books[1]['pubdate'] = datetime.date(1997, 6, 26)
>>> books[2]['ISBN'] = '0618574948'
>>> is_list_of_books(books)
True

So you might think this way of finding all the errors was a bit cumbersome. This is because the API shown above is optimized to handling the result programmatically. If we want human readable errors we can just get the summary.

>>> explanation.summary()
Data is not valid.
[1:'pubdate'] '1997-6-26' is not a date
[2:'ISBN'] '0-618-57494-8' does not match /^\d{9}(\d|X)$/

Which would've made it instantly clear to us what to do.

Installation

'Is Valid?' is on PyPI, you can install it with:

pip install is_valid

The module is written in pure python without any dependencies and is only a few hundred LOC.

About

'Is Valid' is a simple lightweight python library to create validation predicates.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages