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.1.tar.gz (27.1 kB view details)

Uploaded Source

Built Distribution

do_py-0.4.1-py3-none-any.whl (29.5 kB view details)

Uploaded Python 3

File details

Details for the file do-py-0.4.1.tar.gz.

File metadata

  • Download URL: do-py-0.4.1.tar.gz
  • Upload date:
  • Size: 27.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.8.2 requests/2.27.1 setuptools/61.3.1 requests-toolbelt/0.9.1 tqdm/4.63.1 CPython/3.9.9

File hashes

Hashes for do-py-0.4.1.tar.gz
Algorithm Hash digest
SHA256 1afc307e5c50607d8eb4877fbeccab3a8339289eeae649ca1d368029e8529e9d
MD5 56462b02ffc77a9361720fd96e4c5614
BLAKE2b-256 e382ab7853338419f8bcdf4e994f9856c552f356a9c55239226e4ab693ef518a

See more details on using hashes here.

File details

Details for the file do_py-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: do_py-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 29.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.8.2 requests/2.27.1 setuptools/61.3.1 requests-toolbelt/0.9.1 tqdm/4.63.1 CPython/3.9.9

File hashes

Hashes for do_py-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d554a2d5ecaa63a3fd392a2c87b04cb8f0cf1df9d355973ae97d2a3e79227fd4
MD5 f4ff8af5f933859549979e5e39019402
BLAKE2b-256 375e44aec4469865cf7078f5d942c9835ae86915926880a1a0e98e2e7d5af139

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