Skip to main content

Data validation and standardization library wrapping Python dictionaries.

Project description

Do-Py

Do-Py, shorthand for DataObject Python, is a data-validation and standardization library wrapping Python dictionaries.

release build coverage dependencies

Project milestones

Beta ready Stable ready

Quick-Start

Make a basic DataObject.

We will make a class and call it MyFavoriteStuff. We will inherit the DataObject class to gain all its wonderful features. Here you can see we must define the '_restrictions' attribute.

from do_py import DataObject, R


class MyFavoriteStuff(DataObject):
    """
    A DataObject that contains all of my favorite items.
    :restriction favorite_number: The number I favor the most. Strings not allowed.
    :restriction favorite_candy: My favorite candy, this is restricted by value.
    :restriction favorite_movie: My favorite movie. This is optional because a `None` IS allowed!
    """
    # There are two kinds of restrictions, type and value.
    _restrictions = {
        # Type restrictions restrict the type a value can have: int, str, bool, or other DataObjects's
        'favorite_number': R.INT,
        # Value restrictions restrict the value to a specific value in a list.
        'favorite_candy': R('Jolly Ranchers', 'Nerds'),
        # This is a type restriction that allows `None` as a value.
        'favorite_movie': R.NULL_STR
        }


# Instantiate your new DataObject.
instance = MyFavoriteStuff({
    'favorite_number': 1985,
    'favorite_candy': 'Jolly Ranchers',
    'favorite_movie': 'Jolly Green Giant'
    })

print(instance)
# output: MyFavoriteStuff{"favorite_candy": "Jolly Ranchers", "favorite_number": 1985, "favorite_movie": "Jolly Green Giant"}

# You can access values using dot notation or like a `dict`.
print(instance.favorite_number == instance['favorite_number'])
# output: True

print(instance.favorite_number)
print(instance.favorite_candy)
print(instance.favorite_movie)
# output: 1985
# output: Jolly Ranchers
# output: Jolly Green Giant

# Editing the values can also be done very easily.
instance.favorite_number = 2013
print(instance.favorite_number)
# output: 2013

Using restrictions.

Restrictions are written using do_py.R. R allows developers to define custom value restrictions as well as type restrictions using the special shortcuts. Here are a few examples of how you can write value restrictions and type restrictions using the type short-cuts.

from do_py import DataObject, R


class TypeShorCuts(DataObject):
    """
    All of the restrictions written for this DataObject us R's type shortcuts.
    """
    _restrictions = {
        # integer
        'int': R.INT,
        'nullable_int': R.NULL_INT,
        # string
        'str': R.STR,
        'nullable_str': R.NULL_STR,
        # bool
        'bool': R.BOOL,
        # date and datetime
        'date': R.DATE,
        'nullable_date': R.NULL_DATE,
        'datetime': R.DATETIME,
        'nullable_datetime': R.NULL_DATETIME,
        # other (these are rarely used(aqw
        'set': R.SET,
        'list': R.LIST,
        }


class ValueRestrictions(DataObject):
    """
    All of the restrictions for this class are value restrictions.
    """
    _restrictions = {
        # number values
        'integers': R(1, 2, 3),
        'integers and None': R(1, 2, 3, None),
        # string values
        'strings': R('hello', 'hi', 'sup'),
        'nullable_strings': R('hello', 'hi', 'sup', None),
        }

Give the DataObject default values.

DataObjects are able to define the default value for their restrictions. If a developer is not sure if a value will be available, defaults are a very useful utility. We have updated the original example to have a default value for it's restriction favorite_candy.

In order to use the default value when instantiating a DataObject, we must instantiate it in non-strict mode.

Strict instantiation is used by default. In strict instantiation, the data passed in must contain all the keys defined in the DataObject's _restrictions.

With non-strict initialization, it is acceptable to have some keys missing per DO _restrictions. For all missing keys, the default restriction value is used. This section provides an example of using a DataObject in non-strict mode so that we can use the default values for favorite_candy.

from do_py import DataObject, R


class MyFavoriteStuff(DataObject):
    """
    :restriction favorite_number: The default value is 1.
    :restriction favorite_candy: The default value is is "Unknown".
    :restriction favorite_movie: When nullable, the default value is `None`.
    """
    _restrictions = {
        'favorite_number': R.INT.with_default(1),
        'favorite_candy': R('Jolly Ranchers', 'Nerds', 'Unknown', default='Unknown'),
        'favorite_movie': R.NULL_STR
        }


# In order to use the default value when instantiating a DataObject, we must instantiate it in non-strict mode.
# Any values that are not provided will use defaults.
instance = MyFavoriteStuff({}, strict=False)

print(instance)
# output: MyFavoriteStuff{"favorite_candy": "Unknown", "favorite_number": 1, "favorite_movie": null}

Nest a DataObject in another DataObject.

from do_py import DataObject, R


class Contact(DataObject):
    _restrictions = {
        'phone_number'
        }


class Author(DataObject):
    """
    A DataObject that contains all of my favorite items.
    :restriction id:
    :restriction favorite_candy: My favorite candy, this is restricted by value.
    :restriction favorite_movie: My favorite movie. This is optional because a `None` IS allowed!
    """
    _restrictions = {
        'id': R.INT,
        'name': R.STR,
        'contact': Contact
        }


class VideoGame(DataObject):
    """
    A DataObject that contains all of my favorite items.
    :restriction id:
    :restriction favorite_candy: My favorite candy, this is restricted by value.
    :restriction favorite_movie: My favorite movie. This is optional because a `None` IS allowed!
    """
    _restrictions = {
        'id': R.INT,
        'name': R.NULL_STR,
        'author': Author
        }


# Data objects must be instantiated at their **init** with a dictionary and
#   strict(True(default) or False)
instance = VideoGame({
    'favorite_number': 1985,
    'favorite_candy': 'Jolly Ranchers',
    'favorite_movie': 'Jolly Green Giant'
    })

print(instance)

Nest a list of DataObjects in another DataObject.

from do_py import DataObject, R
from do_py.common.managed_list import ManagedList


class Book(DataObject):
    """
    There are multiple books in the library!
    :restriction name: Name of the book.
    :restriction author: The author of the book.
    """
    _restrictions = {
        'name': R.STR,
        'author': R.STR,
        }


class Library(DataObject):
    """
    This DataObject represents a library which contains multiple books.
    :restriction city: The city the library is located in.
    :restriction books: A list of instances of the DataObject "Book".
    """
    _restrictions = {
        'city': R.STR,
        'books': ManagedList(Book)
        }

What is a DataObject?

A DataObject allows us to create Python classes that have strictly defined fields called "restrictions". Restrictions are defined for a DataObject using the _restriction attribute. See the Quick-start section.

There are two kinds of restrictions, type and value:

  • Value restrictions restrict the value to a specific value in a list.
  • Type restrictions restrict the type a value can have: int, str, bool, or other DataObjects.

Advanced Uses

Advanced DataObject validations.

Certain use-cases require more complex validations or restrictions that cannot be supported without code execution. The parent class Validator allows us to execute code at instantiation and any time a key is updated. A child of Validator is required to define a _validate instance method.

from do_py import R
from do_py.data_object.validator import Validator


class Validated(Validator):
    """
    This DataObject validates that we only have one of key or id, but not both. Since this can't be accomplished only
    using restrictions, we are inheriting from `Validator` so we can attach extra validations.
    """
    _restrictions = {
        'key': R.NULL_STR,
        'id': R.NULL_INT
        }

    def _validate(self):
        """
        Validate that we have exactly one of key or id.

        This function runs at instantiation and any time the instance is updated.
        """
        assert any([self.key, self.id]) and not all([self.key, self.id]), \
            'We need exactly one of id or key to not be None.'

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

do-py-0.4.0.tar.gz (27.1 kB view hashes)

Uploaded source

Built Distribution

do_py-0.4.0-py3-none-any.whl (29.5 kB view hashes)

Uploaded py3

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Huawei Huawei PSF Sponsor Microsoft Microsoft PSF Sponsor NVIDIA NVIDIA PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page