Monday, February 2, 2009

Python Typechecker

Excited about the superdynamicness of Python, I decided to build a simple typechecking system for it, as a friend of mine mentioned he did quite some time ago:

types.py

#!/usr/bin/python

def typechecker(types, args):
return not False in [types[i] in type(args[i]).__mro__ for i in xrange(len(types))]

def typed(return_type, *types):
def typedf(f):
def ret(*args):
if len(args) != len(types):
raise TypeError, 'Invalid arguments; requires %u arguments.' % len(types)
elif not typechecker(types, args):
raise TypeError, 'Invalid arguments (%s); requires: %s' % ([type(arg) for arg in args], types)
r = f(*args)
if not (return_type is None) and not (return_type in type(r).__mro__):
raise TypeError, 'Invalid return type (%s); requires: %s' % (type(r), return_type)
return r
return ret
return typedf

Here's an example of its use:

if __name__ == '__main__':
@typed(basestring, basestring, int, int)
def safe_str_getslice(string, start, length):
return string[start:length]


print safe_str_getslice('Pastafarianism', 0, 5)
print safe_str_getslice('Pastafarianism', 0, 5.1)
Pasta
Traceback (most recent call last):
File "types.py", line 28, in <module>
print safe_str_getslice('Pastafarianism', 0, 5.1)
File "types.py", line 12, in ret
raise TypeError, 'Invalid arguments (%s); requires: %s' % ([type(arg) for arg in args], types)
TypeError: Invalid arguments ([<type 'str'>, <type 'int'>, <type 'float'>]); requires: (<type 'basestring'>, <type 'int'>, <type 'int'>)

No comments: