On Python 3.3, you can use function annotations and inspect:
import inspect def validate(f): def wrapper(*args): fname = f.__name__ fsig = inspect.signature(f) vars = ', '.join('{}={}'.format(*pair) for pair in zip(fsig.parameters, args)) params={k:v for k,v in zip(fsig.parameters, args)} print('wrapped call to {}({})'.format(fname, params)) for k, v in fsig.parameters.items(): p=params[k] msg='call to {}({}): {} failed {})'.format(fname, vars, k, v.annotation.__name__) assert v.annotation(params[k]), msg ret = f(*args) print(' returning {} with annotation: "{}"'.format(ret, fsig.return_annotation)) return ret return wrapper @validate def xXy(x: lambda _x: 10<_x<100, y: lambda _y: isinstance(_y,float)) -> ('x times y','in X and Y units'): return x*y xy = xXy(10,3) print(xy)
If there is a validation error, prints:
AssertionError: call to xXy(x=12, y=3): y failed <lambda>)
If there is not a validation error, prints:
wrapped call to xXy({'y': 3.0, 'x': 12}) returning 36.0 with annotation: "('x times y', 'in X and Y units')"
You can use a function rather than a lambda to get a name in the assertion failure.