icontract provides design-by-contract for Python, allowing developers to define preconditions, postconditions, and invariants for functions and classes using expressive lambda functions. It helps ensure correctness and provides informative violation messages, failing fast when contracts are broken. The current version is 2.7.3, and it maintains a steady release cadence with frequent patch updates addressing bugs and adding support for newer Python versions.
pip install icontractVerified import paths — ran on the pinned version, not inferred.
This quickstart demonstrates how to apply preconditions (`@require`), postconditions (`@ensure`), and class invariants (`@invariant`) to Python functions and classes. It shows basic usage with lambda expressions and how to catch `ViolationError` when contracts are broken. Note the use of `old` in `@ensure` to refer to the state before the function call.
Upgrade to icontract 2.7.2 or newer. Ensure your invariant logic correctly accounts for the superclass's state during its initialization phase.
Upgrade to icontract 2.7.1 or newer. This bug was a critical fix addressed in 2.7.1.
Be mindful of performance implications when enabling `enforce_on_setattr`. Only enable it for critical attributes where immediate invariant validation on modification is required, and profile your application to ensure acceptable performance.
Always include `old` in your `@ensure` lambda signature if you intend to refer to the object's state or arguments' values before the function was called. For class methods, `old` will contain the instance before the call.
Review the contract condition that failed and the input values or object state that led to its violation. This often indicates a bug in the calling code or an incorrect assumption about the function's behavior.
Modify your `@ensure` lambda to accept `old` as its first argument (e.g., `lambda old, result: ...` for a function returning a value, or `lambda old, self: ...` for a method to access the state before the call).
Ensure the lambda signature for `@ensure` correctly matches the function's return type. If the function has no return value, use `lambda old, self: ...` or simply `lambda self: ...` for methods, or `lambda: ...` for functions without arguments.
Adjust the lambda signature to accept all arguments `icontract` provides for that context (e.g., `lambda self: ...` for a simple invariant, `lambda self, old: ...` for invariant checking `old` state, `lambda x: ...` for a precondition on argument `x`).