Icontract: Design-by-Contract for Python

2.7.3 · active · verified Thu Apr 16

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.

Common errors

Warnings

Install

Imports

Quickstart

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.

import icontract

@icontract.require(lambda x: x > 0, "Input must be positive")
@icontract.ensure(lambda result: result > 0, "Result must be positive")
def double(x: int) -> int:
    return x * 2

@icontract.invariant(lambda self: self.value >= 0, "Value must be non-negative")
class Counter:
    def __init__(self, initial_value: int) -> None:
        self.value = initial_value

    @icontract.ensure(lambda old, self: self.value == old.value + 1)
    def increment(self) -> None:
        self.value += 1

# Example usage
try:
    print(f"Doubling 5: {double(5)}")
    # This will raise a ViolationError
    # double(-1)
except icontract.ViolationError as e:
    print(f"Contract violation caught: {e}")

c = Counter(0)
print(f"Initial counter value: {c.value}")
c.increment()
print(f"Counter after increment: {c.value}")

try:
    # This would violate the invariant if direct assignment was not restricted
    # c.value = -5 
    # To see invariant violation on setattr, 'enforce_on_setattr' must be set
    pass
except icontract.ViolationError as e:
    print(f"Invariant violation caught: {e}")

view raw JSON →