#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.
Since function annotations are not available in python 2.x I chose to implement typechecking for python 2.x in two ways:
- as a documentation convention for parameters based on the info field lists of sphinx. So even when you don't use typechecking you can use it to auto-generate a function documentation.
- and as parameters for a decorator. For those of you who don't like docstings in sphinx format.
###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.
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.
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)
BTW: The project name "IcanHasTypeCheck" refers to the famous lolcats