Known Issues
Integration with help()
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 this
discussion on python-ideas for more details.
Defining contracts outside of decorators
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
this bug report). 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)
>>> @require_x_positive
... def some_func(x: int) -> None:
... pass
>>> some_func(x=0)
Traceback (most recent call last):
...
SyntaxError: Decorator corresponding to the line 1 could not be found in file <doctest known_issues.rst[1]>: 'require_x_positive = icontract.require(lambda x: x > 0)\n'
However, we haven’t faced a situation in the code base where we would do something like the above, so we are 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.
*args
and **kwargs
Since handling variable number of positional and/or keyword arguments requires complex logic and entails many edge cases (in particular in relation to how the arguments from the actual call are resolved and passed to the contract), we did not implement it. These special cases also impose changes that need to propagate to rendering the violation messages and related tools such as pyicontract-lint and sphinx-icontract. This is a substantial effort and needs to be prioritized accordingly.
Before we spend a large amount of time on this feature, please give us a signal through the issue 147 and describe your concrete use case and its relevance. If there is enough feedback from the users, we will of course consider implementing it.
dataclasses
When you define contracts for dataclasses, make sure you define the contracts after decorating the class with @dataclass
decorator:
>>> import icontract
>>> import dataclasses
>>> @icontract.invariant(lambda self: self.x > 0)
... @dataclasses.dataclass
... class Foo:
... x: int = dataclasses.field(default=42)
This is necessary as we can not re-decorate the methods that dataclass
decorator inserts.