top | item 2724548

Contract based programming (Python)

50 points| irahul | 14 years ago |github.com | reply

12 comments

order
[+] tdavis|14 years ago|reply
The huge number of lambdas had a tendency to muddle the first example; somebody should tell this gentleman about the `operator` module. Other than that, looks interesting! The decorator syntax may be a more apparent way to handle assertions about arguments in situations where numerous (or repetitive ones) are required. Hooks can be similarly handy.
[+] irahul|14 years ago|reply
> The huge number of lambdas had a tendency to muddle the first example; somebody should tell this gentleman about the `operator` module.

The lambdas are how you specify the constraints. How does operator module help here?

    @ensure_args(a=lambda x: x > 10 and x < 100)
    def foo(a):
        pass
Here, the constraint is `a` should be greater than 10 and less than 100. Where does operator module comes in here?

And the first example is using multiple lambdas to show the multiple use cases:

1. `a=lambda x: x > 10` is for the positional argument `a`.

2. `b=r'^?-\d+(\.\d+)$'` is to show if you need the arg to match a regex, you can directly pass it.

3. c=lambda x: x < 5) # `c` will be picked from `kwargs`.

And here, the comment clarifies that c isn't there in the positional args. But since arguments are looked for in both kwargs and positional args, this constraint will work.

[+] irahul|14 years ago|reply
I liked contracts in Racket, so ported some of it to Python. These checks can very well be done inside the called function but decorators enable a more declarative style.
[+] grncdr|14 years ago|reply
Very nice. I did something very similar for an RPC server I wrote, but it's less general and relies on the ORM (SQLAlchemy). Essentially, it turns database id's into the mapped objects, checks the objects against the constraints declared as arguments to the decorator (much like this module), and then passes the mapped objects in to the original function in place of the id. It's proven to be a very useful pattern, but I'll probably use this library instead for future projects.
[+] St-Clock|14 years ago|reply
One thing I may have missed from the documentation: is it possible to check post-conditions (side effects of the function and return value)?
[+] irahul|14 years ago|reply
Side effects can be checked in `@leave(check_side_effects)`. I plan to add something to check results.