Package: do-py
Do-PyDo-Py, shorthand for DataObject Python, is a data-validation and standardization library wrapping Python dictionaries.Project milestonesQuick-StartMake a basic DataObject.We will make a class and call itMyFavoriteStuff. We
will inherit the DataObject class to gain all its wonderful features.
Here you can see we must define the '_restrictions' attribute.fromdo_pyimportDataObject,RclassMyFavoriteStuff(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: Trueprint(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=2013print(instance.favorite_number)# output: 2013Using restrictions.Restrictions are written usingdo_py.R.Rallows 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.fromdo_pyimportDataObject,RclassTypeShorCuts(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,}classValueRestrictions(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 restrictionfavorite_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 forfavorite_candy.fromdo_pyimportDataObject,RclassMyFavoriteStuff(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.fromdo_pyimportDataObject,RclassContact(DataObject):_restrictions={'phone_number'}classAuthor(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}classVideoGame(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.fromdo_pyimportDataObject,Rfromdo_py.common.managed_listimportManagedListclassBook(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,}classLibrary(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_restrictionattribute. 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 UsesAdvanced DataObject validations.Certain use-cases require more complex validations or restrictions that cannot be supported without code execution.
The parent classValidatorallows us to execute code at instantiation and any time a key is updated. A child ofValidatoris required to define a_validateinstance method.fromdo_pyimportRfromdo_py.data_object.validatorimportValidatorclassValidated(Validator):"""This DataObject validates that we only have one of key or id, but not both. Since this can't be accomplished onlyusing 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."""assertany([self.key,self.id])andnotall([self.key,self.id]),\'We need exactly one of id or key to not be None.'