Package: preconditions
preconditions- A precondition decorator utility which relies onparameter-name equivalencefor conciseness and consistency.ExamplesFirst let’s take a tour of examples. All examples assume thepreconditionsdecorator has been imported:frompreconditionsimportpreconditionsBasic type checkingThedoubleapplication functionrequires that theiparameter
is anint, which is verified by a singlepredicate(thelambdaexpression):@preconditions(lambdai:isinstance(i,int))defdouble(i):return2*iMultiple predicatesMultiple predicates may be specified:@preconditions(lambdai:isinstance(i,int),lambdai:i>0,)defdouble(i):return2*iNote that this is functionally equivalent to this single predicate
version:@preconditions(lambdai:isinstance(i,int)andi>0,)defdouble(i):return2*iThe multi-predicate version should (eventually) have more specific
error reporting for a failure, while the single predicate version may
be more efficient.Multiple argumentsMultiple predicates can express preconditions for multiple arguments:@preconditions(lambdas:isinstance(s,unicode),lambdan:isinstance(n,int)andn>=0,)defrepeat(s,n):returns*nHowever, asingle predicatecan express preconditions for multiple
arguments. This allowsrelational preconditions:@preconditions(lambdaa,b:a<=b)defstrict_range(a,b):returnrange(a,b)Method preconditionsPredicates can be expressed for methods, including relations toself. For example, aMonotonicinstance ensures that each call to.setmust pass a value larger than any previous call:classMonotonic(object):def__init__(self):self.v=0@preconditions(lambdaself,v:v>self.v)defset(self,v):self.v=vPreconditions can be applied to special methods, such as__new__,__init__,__call__, etc…classLinearRange(tuple):@preconditions(lambdaa:isinstance(a,float),lambdab:isinstance(b,float),lambdaa,b:a<b,)def__new__(cls,a,b):returnsuper(OrderedTuple,cls).__new__(cls,(a,b))@preconditions(lambdaw:0<=w<1.0)def__call__(self,w):lo,hi=selfreturnw*(hi-lo)+lo@preconditions(lambdax:self[0]<=x<self[1])definvert(self,x):lo,hi=selfreturn(x-lo)/(hi-lo)ConceptsAnapplication functionmay be guarded withprecondition
predicates. Thesepredicatesare callables passed to thepreconditionsdecorator. Consider this code:@preconditions(lambdaa:isinstance(a,float)anda>=0,lambdab:isinstance(b,float)andb>=0,)defarea(a,b):returna*bThe application function isarea, and it has two predicates defined
withlambda, each of which ensures one of the arguments is a
non-negative float.Parameter Name EquivalenceThe parameter names in a predicate must match parameter names in
the application function. This is known asparameter name equivalence[1].[1]This is a bit magical, relying on function introspection. The
design assumes the conciseness and consistency benefits outweigh
the potential confusion of “magic”.One exception to this rule is for default parameters within
predicates. Default parameters may be used to associate some state at
predicate definition time. For example:scores={}@preconditions(lambdacolor,_colors=['RED','GREEN','BLUE']:colorin_colors)defget_color_score(color):returnscores[color]This feature may be most convenient when there’s a need to remember a
local loop variable.Similar Projectscovenant- Code contracts for Python 3.