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.
Project milestones
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
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.