A easy dependency validator
Project description
required: Easy multi-field validation
=====================================
|PyPI| |Build Status| |Coverage Status|
Required is a simple libaray which allows you to validate dependencies
across multiple fields. The goal is to make writing things like Forms
and Seralizers much easier by providing a declariative way to encode
your complex validation logic.
Most Forms and Serializers limit you to doing validation on a single
field, and then have one single ``clean`` method where you can do
muti-field validation logic. The problem with this is that if you have a
large number of optional fields which depend on each other, your
validation code can quickly become unreadable, unmaintainable and
non-resuable.
The aim of Required is to do the following:
- To have a declaritave way to encode validation logic
- Allow you to maintain extreamly complex multi field valiation logic
- Allow you to reuse your validation logic easily
- Be flexible with what you want to validate
If this all sounds good. Read On!
Installation
------------
Install using ``pip``
::
pip install required
Quickstart
----------
Lets start with a quick example.
You want to validate some business rules on some optional input
paramaters (for example to a API endpoint or function). They are
``start_date`` and ``end_date``.
The business rules:
- ``start_date``
- Only valid with ``end_date``
- Must be after 2017
- Must be before 2018
- ``end_date`` - filter events which start before this date
- Only valid with ``start_date``
- Must be before 2018
- Must be after ``start_date``
Theses rules can be written with ``required`` as follows:
.. code:: python
import datetime
from required import Requires, R
# start_date requirements
start_requires_end = Requires("start_date", "end_date")
start_after_2017 = Requires("start_date", R("start_date") > datetime.date(2017, 1, 1))
start_before_2018 = Requires("start_date", R("start_date") < datetime.date(2018, 1, 1))
# end_date requirements
end_requires_start = Requires("end_date", "start_date")
end_before_2018 = Requires("end_date", R("end_date") < datetime.date(2018, 1, 1))
end_after_start = Requires("end_date", R("end_date") > R("start_date"))
The above introduces the two important concepts of required; the
``Requires`` and ``R`` objects.
The ``Requires`` object is used to define pair-wise dependencies. It has
two non-optional arguments, the first one is the target (key) of the
constraint, and the second argument is the constraint itself.
``Requires("start_date", "end_date")`` means "start\_date requires
end\_date to be present".
The ``R`` object acts as a placeholder for a future value. If you
require a future value of ``end_date`` to be more than ``start_date``,
you would write it as ``R("end_date") > R("start_date)``. Any such
expression can be used as the constraint for the ``Requires`` object.
The last step is simply summing all the ``Requires`` together in order
to combine the rules:
.. code:: python
# combine all the rules
all_rules = (
start_requires_end +
start_after_2017 +
start_before_2018 +
end_requires_start +
end_before_2018 +
end_after_start
)
Once you have combined all the rules, you can simply call validate on
the ``all_rules`` object with a dict of your data you want to validate.
.. code:: python
data = {
"start_date": datetime.date(2017, 10, 10),
"end_date": datetime.date(2017, 10, 9),
}
all_rules.validate(data)
# RequirementError: end_date requires end_date to be greater than start_date
The above not only tells you that the data was invalid, but which rule
it broke. The following correct data passes validation:
.. code:: python
data = {
"start_date": datetime.date(2017, 10, 10),
"end_date": datetime.date(2017, 10, 11),
}
all_rules.validate(data)
Cookbook
--------
The following shows some recipes for forming validation rules with the
``R`` object.
.. code:: python
# Arithmetic on the `R` object follows normal maths rules.
R("x", R("x") + 1 < 1)
R("x", R("x") - R("y") == 1)
# A value `x` needs to be in an array
R("x", R("x").in_(array))
# The length of x must be 10
R("x", R("x").length() == 10)
# The length of x and y must be the same
R("x", R("x").length() == R("y").length())
# when x is present y must not be present
# from required import empty
R("x", R("y") == empty)
# x must be equal to the return value of a function
# this is useful if what you are checking is against
# is non-pure eg. current time
f = lambda x: 1
Requires("x", R("x") == Func(f, R("x")))
# the above can be used to ensure that a value is not in the past
R("start_date", R("start_date") > Func(datetime.now))
# Partial dependencies can be also specified with R objects
# x requires y when x is equal to 1
Requires(R("x") == 1, "y")
Contributing
------------
If you want to contribute you are most welcome! This project is
distributed under the `MIT <https://choosealicense.com/licenses/mit/>`__
licence. It is tested using `tox <https://pypi.python.org/pypi/tox>`__
against Python 2.7 and 3.4+
.. |PyPI| image:: https://img.shields.io/pypi/v/required.svg
:target:
.. |Build Status| image:: https://travis-ci.org/shezadkhan137/required.svg?branch=master
:target: https://travis-ci.org/shezadkhan137/required
.. |Coverage Status| image:: https://coveralls.io/repos/github/shezadkhan137/required/badge.svg?branch=master
:target: https://coveralls.io/github/shezadkhan137/required?branch=master
=====================================
|PyPI| |Build Status| |Coverage Status|
Required is a simple libaray which allows you to validate dependencies
across multiple fields. The goal is to make writing things like Forms
and Seralizers much easier by providing a declariative way to encode
your complex validation logic.
Most Forms and Serializers limit you to doing validation on a single
field, and then have one single ``clean`` method where you can do
muti-field validation logic. The problem with this is that if you have a
large number of optional fields which depend on each other, your
validation code can quickly become unreadable, unmaintainable and
non-resuable.
The aim of Required is to do the following:
- To have a declaritave way to encode validation logic
- Allow you to maintain extreamly complex multi field valiation logic
- Allow you to reuse your validation logic easily
- Be flexible with what you want to validate
If this all sounds good. Read On!
Installation
------------
Install using ``pip``
::
pip install required
Quickstart
----------
Lets start with a quick example.
You want to validate some business rules on some optional input
paramaters (for example to a API endpoint or function). They are
``start_date`` and ``end_date``.
The business rules:
- ``start_date``
- Only valid with ``end_date``
- Must be after 2017
- Must be before 2018
- ``end_date`` - filter events which start before this date
- Only valid with ``start_date``
- Must be before 2018
- Must be after ``start_date``
Theses rules can be written with ``required`` as follows:
.. code:: python
import datetime
from required import Requires, R
# start_date requirements
start_requires_end = Requires("start_date", "end_date")
start_after_2017 = Requires("start_date", R("start_date") > datetime.date(2017, 1, 1))
start_before_2018 = Requires("start_date", R("start_date") < datetime.date(2018, 1, 1))
# end_date requirements
end_requires_start = Requires("end_date", "start_date")
end_before_2018 = Requires("end_date", R("end_date") < datetime.date(2018, 1, 1))
end_after_start = Requires("end_date", R("end_date") > R("start_date"))
The above introduces the two important concepts of required; the
``Requires`` and ``R`` objects.
The ``Requires`` object is used to define pair-wise dependencies. It has
two non-optional arguments, the first one is the target (key) of the
constraint, and the second argument is the constraint itself.
``Requires("start_date", "end_date")`` means "start\_date requires
end\_date to be present".
The ``R`` object acts as a placeholder for a future value. If you
require a future value of ``end_date`` to be more than ``start_date``,
you would write it as ``R("end_date") > R("start_date)``. Any such
expression can be used as the constraint for the ``Requires`` object.
The last step is simply summing all the ``Requires`` together in order
to combine the rules:
.. code:: python
# combine all the rules
all_rules = (
start_requires_end +
start_after_2017 +
start_before_2018 +
end_requires_start +
end_before_2018 +
end_after_start
)
Once you have combined all the rules, you can simply call validate on
the ``all_rules`` object with a dict of your data you want to validate.
.. code:: python
data = {
"start_date": datetime.date(2017, 10, 10),
"end_date": datetime.date(2017, 10, 9),
}
all_rules.validate(data)
# RequirementError: end_date requires end_date to be greater than start_date
The above not only tells you that the data was invalid, but which rule
it broke. The following correct data passes validation:
.. code:: python
data = {
"start_date": datetime.date(2017, 10, 10),
"end_date": datetime.date(2017, 10, 11),
}
all_rules.validate(data)
Cookbook
--------
The following shows some recipes for forming validation rules with the
``R`` object.
.. code:: python
# Arithmetic on the `R` object follows normal maths rules.
R("x", R("x") + 1 < 1)
R("x", R("x") - R("y") == 1)
# A value `x` needs to be in an array
R("x", R("x").in_(array))
# The length of x must be 10
R("x", R("x").length() == 10)
# The length of x and y must be the same
R("x", R("x").length() == R("y").length())
# when x is present y must not be present
# from required import empty
R("x", R("y") == empty)
# x must be equal to the return value of a function
# this is useful if what you are checking is against
# is non-pure eg. current time
f = lambda x: 1
Requires("x", R("x") == Func(f, R("x")))
# the above can be used to ensure that a value is not in the past
R("start_date", R("start_date") > Func(datetime.now))
# Partial dependencies can be also specified with R objects
# x requires y when x is equal to 1
Requires(R("x") == 1, "y")
Contributing
------------
If you want to contribute you are most welcome! This project is
distributed under the `MIT <https://choosealicense.com/licenses/mit/>`__
licence. It is tested using `tox <https://pypi.python.org/pypi/tox>`__
against Python 2.7 and 3.4+
.. |PyPI| image:: https://img.shields.io/pypi/v/required.svg
:target:
.. |Build Status| image:: https://travis-ci.org/shezadkhan137/required.svg?branch=master
:target: https://travis-ci.org/shezadkhan137/required
.. |Coverage Status| image:: https://coveralls.io/repos/github/shezadkhan137/required/badge.svg?branch=master
:target: https://coveralls.io/github/shezadkhan137/required?branch=master
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
required-0.3.3.tar.gz
(10.8 kB
view details)
Built Distribution
File details
Details for the file required-0.3.3.tar.gz
.
File metadata
- Download URL: required-0.3.3.tar.gz
- Upload date:
- Size: 10.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 685b0f969ef3a7444f7760f6a64d19c2a0a2bfcadd18158455084727037bb651 |
|
MD5 | 6f89c0e69f440985e5bf3838036150cd |
|
BLAKE2b-256 | bed3dd9350b5aee1f190ac7f9f8c12c3018d5e6853faa872e4d40c3c4843ed87 |
File details
Details for the file required-0.3.3-py2.py3-none-any.whl
.
File metadata
- Download URL: required-0.3.3-py2.py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 82d201748e7dd2cff3c27d2bcf450d8e7b7996b43e76ec7201baaa9deb1943fe |
|
MD5 | 79351d07c0a711c40bbc9a35dc6dd2b6 |
|
BLAKE2b-256 | 9921c7e0047138d785d0892d61f982194344da0043d0b09b1ba9d9d31f8793ff |