Skip to main content

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


Project details


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)

Uploaded Source

Built Distribution

required-0.3.3-py2.py3-none-any.whl (10.9 kB view details)

Uploaded Python 2 Python 3

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

Hashes for required-0.3.3.tar.gz
Algorithm Hash digest
SHA256 685b0f969ef3a7444f7760f6a64d19c2a0a2bfcadd18158455084727037bb651
MD5 6f89c0e69f440985e5bf3838036150cd
BLAKE2b-256 bed3dd9350b5aee1f190ac7f9f8c12c3018d5e6853faa872e4d40c3c4843ed87

See more details on using hashes here.

File details

Details for the file required-0.3.3-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for required-0.3.3-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 82d201748e7dd2cff3c27d2bcf450d8e7b7996b43e76ec7201baaa9deb1943fe
MD5 79351d07c0a711c40bbc9a35dc6dd2b6
BLAKE2b-256 9921c7e0047138d785d0892d61f982194344da0043d0b09b1ba9d9d31f8793ff

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page