In the thread I started a few days ago, I was told that "programming by contract," could be done with "decorators." I was skeptical that this was a good approach, but as an exercise, I tried to code it up in a reasonably elegant form. I'd like to think I succeeded -- and I must admit that those who told me it could be done with decorators were right.
My code is shown below. It shows a function called "myfunction," and another function for testing it called "myfunction_test." More detailed explanation follows the listing. Comments and feedback are welcome, of course. #!/usr/bin/env python def contract(test, check=0): "enforce programming by contract" if check: return test def null(func): def null2(*args, **keys): return func(*args, **keys) return null2 return null def getarg(args, keys, num, name): if name in keys: return keys[name] return args[num] def myfunction_test(myfunction): "contract test for function myfunction" def test(*args, **keys): x = getarg(args, keys, 0, 'x') y = getarg(args, keys, 1, 'y') z = getarg(args, keys, 2, 'z') # preconditions would go here, if there were any result = myfunction(x, y, z) # execute function assert result == x**2 + 3 * y + z # post-condition return result return test #======================================= CheckContracts = 1 @contract(myfunction_test, CheckContracts) def myfunction(x, y, z): return x**2 + 3 * y + z print myfunction(4, z=1, y=3) ------------------- end of listing --------------------- Here's what is going on. At the bottom of the listing, you will see the definition of a function called "myfunction," followed by a call of the function. It's a trivial function, but I gave it three arguments just to test passing them out of order by keyword. Just above the definition of "myfunction" is a "decorator" called "contract," which takes two arguments. The first argument specifies the name of the self-test contract function, which is "myfunction_test" in this case. The second argument is used to enable or disable the contract tests. Note that I could have just called the decorator "myfunction_test" and omitted the "contract" decorator, but I think this form, although slightly less efficient, is more readable. It also allows the enabling/ disabling logic to be put in one function rather than having to repeat it in every contract test function. Dealing with the arguments was not a trivial matter -- at least not for me. I had to experiment a bit to get it right. The "getarg" function is simply a utility for parsing the ordered and keyword arguments. It's very simple, but if something like this already exists, please let me know. Thanks. -- http://mail.python.org/mailman/listinfo/python-list