Package: pycheck
checked(f)checked is a function decorator that uses function annotations as type declarationsand verifies that the function has been passed, and is returning, thecorrect types of values.Yes, yes, I know, this is python and we're not supposed to have to havetype checking. Before you pound your keyboard angrily telling me what anidiot I am, please at least read the "Motivation" section below. Thank you.SAMPLE USAGE:------------@checkeddef function(parameter:<type declaration>) -> <type declaration>...."function" may be a function, a method, or a generator*.* For generators the actual return type is type <generator> however@checked will check that the values yielded by the generator areof the specified type.<type declaration> may be any of the following:* a class or type object (int, str, MyClass, etc.)Example:``def f( x:int ): # f() only accepts int``* A tuple (or any iterable) of type objects, indicating that the value may be an instance ofany of those types.Example:``def f( x: (int, str) ): # f() must return a str OR None``* The literal None, to indicate that only None is an acceptable value(usually used for declaring that a function does not return a value)Example:``def f( x ) -> None: # f() has no return value``* A tuple (or any other iterable) containing type objects and the value None, indicating thatthe value may be any of the indicated types or the special value None.Example:``def f( x ) -> (str, None) # f() must return a str or None``* Dict (or other mapping type) object which maps a collection type to an element type,{<collection type> : <element type>}.This usage means that the function ruturns a collection of type <collection type>(e.g. set, list, tuple, etc.), which contains only elements of type <element type>.Example:``def f(x) -> {set:int}: # f() must return a set of integers``* A callable object which accepts one parameter and returns true or false. The callableobject is treated as a pre-condition when annotating function parameters and apost-condition when annotating the return value. The value passed or returned will bepassed to the callable and an error will be raised if the result is not true.Example``def f( x: lambda x: x > 0 ): # f() only accepting values greater than zero````def g(x) -> lambda y: y >= 0: # g() must return a value >= 0``DEFAULTS, *ARG, AND **KWD--------------------------------------Parameters with default values, as well as *arg and **kwd arguments can also havetheir types declared:@checkeddef function(x:<type declaration>, y:<type declaration>=1, *args:<type declaration>, **kwds:<type declaration):DEBUG MODE:-----------------------------@checked is intended to be used as a tool duringdevelopment and testing to help identify errors more quickly. Forthis reason ``@checked`` is only functional in debug mode(i.e. when ``__debug__ == True``). If not in debug mode it is definedsimply as,def checked(f):return fMOTIVATION:-----------The original motivation for this function was my attempt to apply the excercises outlined in Ben Nadel's whitepaper,"Object Calisthenics" (http://www.bennadel.com/resources/uploads/2012/ObjectCalisthenics.pdf).One of Nadel's excercises is to, "wrap all primitives and Strings." What he means is that instead of passing simpleints, floats, and strings to a function, you'd create simple subclasses of int, float and str to represent thetype of data the parameter actually represents and to protect you from accidently passing an integer thatrepresents Hours to a function that is intended to work with dollar ammounts, for example.Of course, Nadel wrote his paper with Java in mind, so the java compiler would actually catch an error like that.In attempting to apply that recommendation to python something was needed in order implement type checking.Not finding a suitable package after an exhaustive five minute search of the internet I decided to try toimplement my one on my own (okay, I really wanted to see if I could do it or not).LIMITATIONS-----------*  there's currently no way to verify if the default value specified for a parameter violates thetype declaration for that parameter.Example:def f( x:int="bad default value" ):....Hopefully the fact that the type declaration is RIGHT THERE IN FRONT OF YOU will be enough toget you to use the right type.This actually has at least one legitamate use, actually, in the case where the default valueis a magic value (typically None) which indicates that the user didn't supply a value:def f( x:int=None )# user is never intended to specify a value of None. User is expected# to either supply an int or no value at all.if x is None:...do something special...* If the annotations are callable objects, they can only operate on a single parameter, or the return,value. The validation cannot evaluate all of the parameters -- for example you can't write apre-condition that checks that the parameters are ordered from least to greatest.KNOWN ISSUES:-------------Although an error is raised as it should be, the text of the error message for a container containing andelement of the wrong type is not cleartype_proxy(typename)type_proxy(typename) is used when an actual typename cannot be used becauseit is not yet defined in the namespace and cannot be.The two typical causes are,1. Using a class in an annotation of a method of that same class.Example:class A:@checked@classmethoddef factory(cls, ...) -> A: # Error, when the annotation is processed, A has not yet been defined...Solution:class A:@checked@classmethoddef factory(cls, ...) -> type_proxy('A') # valid...2. Trying to create an annotation which refers to a class not yet defined, which itself uses an annotationthat refers back to _this_ class (i.e. circular references). Because the two classes refer to each otherthe solution is not so simple as to just move the second class definition before the first.Example:class A:@checkeddef to_b(self) -> B : # Error: B not yet definedreturn B(self)class B:@checkeddef to_a(self) -> A : # A refers to B and B refers to A, so it doesn't matterreturn A(self)    # which class is defined first, there's going to be an error.Solution:class A:@checkeddef to_b(self) -> type_proxy("B") : # Fixedreturn B(self)class B:...unchanged...