Package: contract-decorator
IntroductionThe following decorator can be used to provide parameter checking for regular and class methods.This decorator would not be possible without the inspiration of the following:So you want to be a Python expert? by James PowellPre/Post-Condition Decorator ExampleAndrew P accepts decoratorThe above inspired me to create this decorator to reduce the clutter of my code.Even thou the accepts decorator already checks for the type.  It is somewhat limited
because it expects the exact number of parameters of the method.  It forces the users
to decorator all of the parameters instead of the ones that one wants checked.  This
limitation has been removed for this decorator.ObjectiveTo allow for a wide range of parameter checking that can be used by
anyone without having to include the parameter checks within the calling method.
This will allow someone to provide cleaner code for the implemented method while
adding some complexity to the decorator.FeaturesThecontractdecorator provides the ability for a user to check a parameter for
type and/or value expectation.  This also includes the option of checking the return
value.Thecontractdecorator uses the following syntax:importcontract@contract({'a':[checktype(float),closed(0.0,1.0)],'b':[checktype(int),gteq(1)],'c':[checktype(int),gteq(1)]})def__init__(self,a,b,c,d):The abovecontractwill check that theaparameter is of type float and is between 0.0
and 1.0.  While thebparameter is checked that is it an integer and is greater than of equal
to 1.  The same for parameterc.  While parameterddoes not required any special condition
to be true.As can be seen, thecontractdecorator expects you to pass it a dictionary.  Where thekeyis the name of the parameter and thevalueis a tuple containing the different checks that will
be called for each parameter value during run-time.The following table contains the different options that the can be used with this decorator.NameParametersTypeDescriptionvalidvaluestupleValue CheckChecks that the parameter value is part of the tuplechecktypetypeType CheckChecks if the parameter is an instance of the expected typecloseda,b such that a <= bClosed Range CheckChecks if the value is between two values a and b including a and bopeneda,b such that a < bOpened Range CheckChecks if the value is between two values a and b excluding a and bclosedopeneda,b such that a < bClosed/Opened CheckChecks if the value is between two values a and b including a but excluding bopenedcloseda,b such that a < bOpened/Closed CheckChecks if the value is between two values a and b including b but excluding agtaValue CheckChecks that the parameter value is greater than altaValue CheckChecks that the parameter value is less than agteqaValue CheckChecks that the parameter value is greater than or equal to alteqaValue CheckChecks that the parameter value is less than or equal to aExamplesThe following example offers some ways that thecontractdecorator can be used within someone code.Type Check.The following code shows how you can check that a given parameter is of a given type.importcontract@contract({'a':[checktype(float)]})defm(a):The above will determine if the passed valueais afloattype.Range ChecksThe following contains the different range checks that can be applied.importcontract@contract({'a':[closed(0,1)],'b':[gteq(2)],'c':[gt(3)],'d':[lt(4)],'e':[lteq(5)],'f':[opened(10.0,11.0)],'g':[closedopened(12,13)],'h':[openedclosed(14,15)],})defrange_check(self,a,b,c,d,e,f,g,h):The above will then perform the expected checks for each of the referenced parameters.Note that the above checks are not restricted to the primitive values but can be
any class that implements the different range checks methods of the Data Model.
You can then use the Range checks with a users defined class that implements
the __lt__, __le__, __gt__, and __ge__ methods.Tuple Check@contract({'a':[validvalues(set('blue','green','red'))]})defcontains(a,b):The above example will insure that the value of parameterawill be set to 'blue',
'green' or 'red'.Return Value Check@contract({None:[gt(101)]})defcontains(a,b):The above check will determine that the return value is greater than 100.  Note
that you need to specify that the return parameter name asNone.  This is how we
associate the check for the return value.User Defined ChecksThe contract decorator provides the ability for a user to create their own user defined
checks.  This will allow users to define checks that are not covered above.  There are
two different ways of creating user-defined checks.  You can create a class that has
a callable defined that expects a single parameter or you can define a method that
expects a single parameter.  Both implementations will be passed the value that was
passed to the decorated method.  The following section will give examples of each.User Defined Class CheckHere is a simple example of how to create and use a user define class to check
that the passed method parameter value agrees with the method contract.classUserDefinedCheck(object):def__init__(self,cond):self.cond_=cond...def__call__(self,v):# Example of using a simple assert checkassertv!=None,"The value passed cannot be None"# Example of using a simple method or instance object.# The method or instance should raise an AssertionError whenever the condition is falsecond(v)...@contract({'a':[UserDefinedCheck()]})defcheck_me(a,b):...TheUserDefinedCheckclass that was created above has a distinctive requirement.The need for the class to implement the__call__method with a single
parameter.  The callable method is called by the contract decorator and it expects
the call to raise an AssertionError whenever the condition is not valid.  Lastly,
the class is used by passing an instance of theUserDefinedCheckclass to the
contract decorator.In the example above, the parameter passed will be the value of theaparameter
when thecheck_mefunction is called.User Defined Method CheckHere is a simple example showing how a user-defined check method can be implemented
and used by the contract decorator.defuser_defined_check(v):assertv!=None,'The value cannot be set to None'...@contract({'b':[user_defined_check]})defcheck_me(a,b):...Theuser_defined_checkmethod was created with a similar requirement as the user
defined class.  The method was defined with a simple parameter.  The contract
decorator will then pass the value of the checking parameter when the method that
is being checked is called.  Lastly, a reference to theuser_defined_checkmethod
is passed to the contract decorator.In the example above, the check_me method will use theuser_defined_checkmethod
to check the contract of thebparameter.