In the past two years I have fallen in love with Python. In the course of my work I developed a few libraries that I use consistently across projects and save me quite a bit of development time. Some of them are worth sharing, and I hope to have the time to do it (ah!).
PyContracts is one such library, stable enough to merit a version number larger than 1.0. I uploaded it on the PyPI software repository, so that anybody can install it using:
easy_install PyContracts
(or: pip install PyContracts
)
PyContracts can be used to declare constraints on the arguments and return value of the functions. While it can be used for type-checking, I found it much more valuable for expressing complicated constraints on the values rather than on the types. For example:
@contract(color='seq[3](>=0,<=1)')
def paint(color):
...
This checks that color
is a sequence of 3 elements, and each element is a number between 0 and 1. Any sequence is allowed: lists, tuples, Numpy arrays, etc.
Note that it would be very tedious to do by hand:
def paint(color):
if ( not isinstance(color, Sequence)
or len(color) != 3):
raise ValueError('I need a sequence of length 3.')
for c in color:
if (not isinstance(c, (int, float))
or c<0 or c>1):
raise ValueError('Colors must be in [0,1]')
...
All these tests are done automatically by PyContracts, and similar error messages are given.
Here’s a more complicated example. Suppose you want to write a function blend()
that takes a list of RGB or RGBA images, blends them together, and return the blended images.
There are several checks you need to do: the input is a list, there are at least two images, each image is RGB/RGBA, each image is the same size, and so on.
With PyContracts you can express all of that very compactly:
@contract(images="list[>=2]( array[HxWx(3|4)](uint8) )",
returns="array[HxWx3](uint8)")
def blend(images):
''' Blends a series of images together. '''
...
See the PyContracts home page for more information.