I am looking to start using DBC on a large number of Python-based projects at work and am wondering what experiences others have had with it. So far my research turned up the following:
My questions are: have you used DBC with Python for mature production code? How well did it work/was it worth the effort? Which tools would you recommend?
Contracts for Python Functions Contracts can be used to validate the input or output of a function. Data flow among components can be hard to keep track or maintained, sometimes forcing us to write print statements everywhere trying to catch malformed data.
Design by contract also defines criteria for correctness for a software module: If the class invariant AND precondition are true before a supplier is called by a client, then the invariant AND the postcondition will be true after the service has been completed.
Design by Contract™ is an approach to designing robust yet simple software. It provides methodological guidelines to achieve these goals without resorting to defensive programming. Instead, Design by Contract builds class invariants and pre/post validation of methods arguments and return values into the code itself.
Microsoft has released a library for design by contract in version 4.0 of the . net framework. One of the coolest features of that library is that it also comes with a static analysis tools (similar to FxCop I guess) that leverages the details of the contracts you place on the code.
The PEP you found hasn't yet been accepted, so there isn't a standard or accepted way of doing this (yet -- you could always implement the PEP yourself!). However, there are a few different approaches, as you have found.
Probably the most light-weight is just to simply use Python decorators. There's a set of decorators for pre-/post-conditions in the Python Decorator Library that are quite straight-forward to use. Here's an example from that page:
>>> def in_ge20(inval): ... assert inval >= 20, 'Input value < 20' ... >>> def out_lt30(retval, inval): ... assert retval < 30, 'Return value >= 30' ... >>> @precondition(in_ge20) ... @postcondition(out_lt30) ... def inc(value): ... return value + 1 ... >>> inc(5) Traceback (most recent call last): ... AssertionError: Input value < 20
Now, you mention class invariants. These are a bit more difficult, but the way I would go about it is to define a callable to check the invariant, then have something like the post-condition decorator check that invariant at the end of every method call. As a first cut you could probably just use the postcondition decorator as-is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With