[Python-ideas] Contracts in python -- a report & next steps

Marko Ristin-Kaufmann marko.ristin at gmail.com
Wed Oct 24 03:40:25 EDT 2018


Hi,
I would like to give you a short report on the development of icontract
library following the discussion about the introduction of contracts into
Python (e.g., see [1, 2, 3, 4]).

*Features*
The functionality of icontract library is basically on par with Eiffel,
Cobra and other languages supporting contracts except for loop invariants
and less readable syntax.

The features include:
* "require", "ensure" and "invariant" decorators to define the contracts
* "snapshot" decorator to capture the values prior to the function
invocation to allow postconditions to verify the state transitions
* inheritance of the contracts (with strengthening/weakening)
* tracing of the argument values on contract violation
* informative violation messages automatically parsed from the code of the
condition function
* The user can specify custom errors to be raised on a specific contract
violation
* individual toggling of the contracts
* linter to check the contract arguments
* sphinx extension to render the contracts automatically in the
documentation (including a sophisticated matching of logical implications)

We covered all the use cases we could find in our code base (at Parquery
AG) such as:
* functions
* instance methods
* class and static methods
* property getters, setters and deleters
* slot wrappers

*Roadblocks*
During the development, the following roadblocks were encountered:

* We wanted to include the contracts in the output of help().
Unfortunately, help() renders the __doc__ of the class and not of the
instance. For functions, this is the class "function" which you can not
inherit from. See [5] for more details.

* We need to inspect the source code of the condition and error lambdas to
generate the violation message and infer the error type in the
documentation, respectively. inspect.getsource(.) is broken on lambdas
defined in decorators in Python 3.5.2+ (see [6]). We circumvented this bug
by using inspect.findsource(.), inspect.getsourcefile(.) and examining the
local source code of the lambda by searching for other decorators above and
other decorators and a function or class definition below. The decorator
code is parsed and then we match the condition and error arguments in the
AST of the decorator. This is brittle as it prevents us from having partial
definitions of contract functions or from sharing the contracts among
functions.

Here is a short code snippet to demonstrate where the current
implementation fails:
import icontract

require_x_positive = icontract.require(
    lambda x: x > 0, error=lambda: ValueError("x must be positive"))

@require_x_positive
def some_func(x: int) -> None:
    pass

However, we haven't faced a situation in the code base where we would do
something like the above, so I am unsure whether this is a big issue. As
long as decorators are directly applied to functions and classes,
everything worked fine on our code base.


*Our Experience*
We have been using icontract in our team for our production code base
(~100K LOC) as well as for a couple of open source projects (each <10K LOC)
since August 1st 2018 (~ 3 months).

In contrast to points raised during the discussions in [1, 2, 3, 4], we did
not have issues with readability and modifying contracts. Thus far, we have
not encountered problems with variable renames and major code refactorings.
We use Pycharm, so it remains open whether this also applies to development
environments such as emacs and vim.

We confirm that contracts help us improve the documentation, keep the
documentation up-to-date, better reason about the function's input and
output and catch bugs early and more easily during the unit and integration
tests.


*Status*
The library interface is currently frozen after the version bump to 2.0.0
and is not expected to change in the next six months. All the reported bugs
have been fixed and no bug fixes are pending.

*Next Steps?*
I personally doubt that we are enough people to form a party to push for a
change in the language. A standardized library seems to me like a
realizable compromise given the state of the discussion on this mail list.

Before we organize a collective to write a proposal to standardize the
library, I would suggest that a couple of other interested teams adopt
icontract, apply it to their code bases and report their experiences on
this mail list. I hope that 2-3 reports would be insightful enough to
either convince other people that contracts in python are worth
standardizing (and highlight missing features yet to be implemented) or
provide solid material to discard the endeavor at the current moment.

In the meanwhile, it would be of great help if somebody could vet the
documentation and the code of icontract.

*Authors*
The library was mainly written by me (with some help of my colleague Adam
Radomski). We discussed the features within the dev team at Parquery
(Zurich, Switzerland) as well as in email correspondence with James Lu.

[1] https://groups.google.com/forum/#!topic/python-ideas/mvzLsdwGuww
[2] https://groups.google.com/forum/#!topic/python-ideas/JtMgpSyODTU
[3] https://groups.google.com/forum/#!topic/python-ideas/mvzLsdwGuww
[4]
https://groups.google.com/forum/#!topic/python-ideas/dmXz_7LH4GI%5B1-25%5D
[5] https://groups.google.com/forum/#!topic/python-ideas/c9ntrVuh6WE
[6] https://bugs.python.org/issue21217
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20181024/376cdd2f/attachment-0001.html>


More information about the Python-ideas mailing list