Home

Awesome

#IcanHasTypeCheck (ICHTC)

is a small and easy to use decorator to enable dynamic type checking for python method and function calls. Working and tested for python 2.7 but should run in other 2.x as well. Create an issue on github if you encounter any problems using it. (Plans and an example implementation for python 3.x is included )

Function type specification is based on a naming/docstring convention for python < 3.x

Typechecking is implemented as a decorator that can be attached to any function or method and will perform the according (dynamic) typechecking. It will raise a TypeError if the arguments don't match the function specification.

Python 2.x

Since function annotations are not available in python 2.x I chose to implement typechecking for python 2.x in two ways:

###Syntax for python 2.x using decorator arguments:

    @typesafe( { "param_a" : str, 
                 "param_b" : types.IntType, 
                 "param_c" : own_module.OwnType
                 "rtype"   : bool }
              )
    def foo(param_a, param_b, param_c):
        """ Some Docstring Info      """
        # Do Something 
        return True

###Syntax for python 2.x using (sphinx style) docstrings:

    @typesafe
    def foo(param_a, param_b):
        """ 
            :type param_a:  types.StringType
            :type param_b:  types.IntType
            :rtype:         types.BooleanType   
         """
        # Do Something 
        return True

###You can use any python type.

So if you have defined a Point() class in mod1:

   Class Point(object):
    
    def __init__(self, x = None, y = None):
        """ Initialize the Point. Can be used to give x,y directly."""
        self.x = x
        self.y = y

    def set_coord(self, x , y):
        self.x = x
        self.y = y      

    def __repr__(self):
        ostr = ""
        ostr = "x: " + str(self.x) + os.linesep
        ostr += "y: " + str(self.y) + os.linesep
        return ostr
    
    def __str__(self):
        return self.__repr__()
        

Then you could use it as a parameter type specification like this:

    # another module.py
    from mod1 import Point

    def foo(afunc):
        """ 
            :type afunc:    mod1.Point          
            :rtype:         types.BooleanType
        """
        return True

Or like this

    @typesafe({ "val" : mod1.Point })
    def point_check( val ):
        """ gets a point and prints it """
        print " ** Printing a mod1.Point "
        print val

The decorator typesafe will first check if it is running in a 3.x or 2.x environment and react accordingly.

Installation and Example:

Just download the zip or tarball. Unpack it and run

pyhton i_is_example.py

It will successfully call two functions and then fail with a type error. Which is intended to show the functionality.

Python 3.x (Just an implementation example -> Not tested)

The base technique for IcanHasTypeCheck in python 3.x are the Function Annotations proposed in PEP 3107. They were implemented in Python 3.0 (see section New Syntax).

###Syntax for python 3.x:

    @typesafe
    def foo(param_a: str, param_b: int) -> bool:
        # Do Something 
        return True

The @typesafe decorator will then check all arguments dynamically whenever the foo is called for valid types. As a quoting remark from the PEP 3107: "All annotated parameter types can be any python expression. " But for typechecking only types make sense, though. The idea and parts of the implementation were inspired by the book: Pro Python (Expert's Voice in Open Source)

Why is it called IcanHasTypeCheck ?

BTW: The project name "IcanHasTypeCheck" refers to the famous lolcats