Package: pytechecker
pytecheckerPytecheckeris a utility library for type checking objects against sample objects.Table of ContentsA simple exampleEmbedded dictsWhat about lists?Dicts as list elementsTuplesDicts and lists in tuplesPytecheckerworks excellently for checking API payloads. For how it can be used for this,here's an example of how it can be used together with a flask api.ExamplesThe gist of it allUse pip to installpytecheckerpython3 -m pip install pytechecker.Then you simply use thecheckfunction from the module.frompytecheckerimportchecksample={"name":{"required":True,"allowed_types":[str]},"age":{"required":True,"allowed_types":[int]}}obj={"name":"dcronqvist","age":21}# succ will be True if it passed type checking# errors will be an array of strings that tell you# what went wrong during type checking, if succ is False.succ,errors=check(sample,obj)A simple exampleLet's start by taking a look at a very simple sample object:{"name":{"required":True,"allowed_types":[str]},"age":{"required":False,"allowed_types":[int]}}Here we have defined asample objectwhich has one required keynameand one optional keyage. In the sample object we have also specified that the keynamecan only be of the typestr, andagecan only be anint. Let's see which objects that fit this sample.{"name":"Daniel","age":21}Above we have an object which fits the sample object. It's an object which has the required keynameand the optional keyage, and they both are their respective required types.{"name":"Daniel"}Above is an object that still fits the sample object. Since the keyageisoptional, we can omit it from the object without it causing it to be unfit.{"age":21}The above object does, however,NOTfit the sample object. Upon attempting to match the object against the sample, you'll be met with the following error:ERROR: Key 'name' is required, but was absent in supplied object.We can also look at an example like this:{"name":"Daniel","age":21.4}The above object is unfit since one of its keys is of a type that is not allowed for that key. You'll be met with the following error:ERROR: On key 'age', expected one of ['int'], got float.So there we have it. That's a very simple example of how it works.Embedded dictsWe don't want to be limited to dicts of only one level, so thankfullypytecheckercan handle virtually infinite levels of dicts in dicts. Let's look at an example.{"tenant":{"required":True,"allowed_types":[dict],"embedded_dict":{"name":{"required":True,"allowed_types":[str]},"age":{"required":True,"allowed_types":[int]}}},"room":{"required":True,"allowed_types":[str]}}Above we have a sample object which has a top level key which is allowed to a dict. We can very easily also specify what this embedded dict must look like for it to fit. Let's look at some objects!{"tenant":{"name":"Daniel","age":21},"room":"RM-212512"}This is an object which fits, like a glove! We have the keytenantwhich of course is a dict (or object) and then the keyroomwhich is a string, like specified! We can also see that all keys inside oftenantare required, so leaving one out would result in something like the following:ERROR: Key 'tenant.age' is required, but was absent in supplied object.Pytecheckeris very nice and also tells you the entire key from top level and down, and won't just say thatageis missing.Like said, this works for a virtually infinite amount of dicts in dicts - it's done via recursion!What about lists?Lists are handled very nicely bypytechecker. It will go through all elements in the list and type check them for you. You can even have list elements that are dicts, and they'll also be handled! Let's look at a simple example with lists to begin with.{"nums":{"required":True,"allowed_types":[list],"list_element":{"allowed_types":[int,float]}}}Here we have a sample object with a required keynumswhich must be alistof numbers.{"nums":[5,1.2,9,12.4]}Above here is an object which fits since it has they keynumsand all elements are of the correct types.{"nums":[5,1.2,9,12.4,"5"]}Having something like the object above would give an error like the following:ERROR: On key 'nums[4]', expected one of ['int', 'float'], got str.See that[4]?Pytecheckeralso tells you which element(s) that are wrong in a list. If we would have multiple wrong elements:{"nums":[5,"1.2",9,12.4,"5"]}We would get multiple errors like this:["ERROR: On key 'nums[1]', expected one of ['int', 'float'], got str.","ERROR: On key 'nums[4]', expected one of ['int', 'float'], got str."]Important to note is that all errors that have been shown before this example have also been part of an array, they have just been the only element in that array of errors.Dicts as list elements{"people":{"required":True,"allowed_types":[list],"list_element":{"allowed_types":[dict],"embedded_dict":{"name":{"required":True,"allowed_types":[str]},"age":{"required":False,"allowed_types":[int]}}}}}Right, so here we have a sample object which contains a keypeoplewhich should contain a list of objects with a required keynameand optional keyage. Let's look at some objects.{"people":[{"name":"Daniel","age":21},{"name":"John","age":42},{"name":"Betty"}]}Here is our list of people with their names and optional age. This object fits, so let's look at one that doesn't fit.{"people":[{"name":"Daniel","age":21},{"name":"John","age":42},{"age":35}]}The personBettynow doesn't have a name, instead it only has an age. This will result in the following error:ERROR: Key 'people[2].name' is required, but was absent in supplied object.Once again,pytecheckertells you which of these objects that is unfit, and also what about that object that is unfit.TuplesTuples are much like lists, and handled a lot like them. However, tuples are specified by type order, so that the supplied object must match type order as well, let's look at an example.{"tuple":{"required":True,"allowed_types":[tuple],"tuple_order":[str,int,int,float]}}Here we have a sample object which specifies a tuple with the specific order of(str, int, int, float). Let's look at objects!{"tuple":("Hello there!",5,2,1.1)}There we are, an object which fits the tuple description perfectly. If we, however, were to flip around some values to not be in the same type order:{"tuple":("Hello there!",5,1.1,2)}Then we'd be met with the following error:ERROR: On key 'tuple', expected tuple with order (str,int,int,float), got tuple with order (str,int,float,int).Dicts and lists in tuples{"tuple":{"required":True,"allowed_types":[tuple],"tuple_order":[list,int,dict],"list_element":{"allowed_types":[dict],"embedded_dict":{"name":{"required":True,"allowed_types":[str]}}},"embedded_dict":{"num":{"required":True,"allowed_types":[int,float]}}}}A sample object which should contain a keytuple, which also should be a tuple. This tuple should, respectively, contain alistof objects, anintand adict.{"tuple":([{"name":"Daniel"},{"name":"John"}],5,{"num":25})}The above object fits like a glove!