API

Values

deal.pre(validator, *, message: str | None = None, exception: ExceptionType | None = None)Callable[[C], C][source]

Decorator implementing precondition value contract.

Precondition is a condition that must be true before the function is executed. Raises PreContractError otherwise.

Parameters
  • validator – a function or validator that implements the contract.

  • message – error message for the exception raised on contract violation. No error message by default.

  • exception – exception type to raise on the contract violation. PreContractError by default.

Returns

a function wrapper.

>>> import deal
>>> @deal.pre(lambda a, b: a + b > 0)
... def example(a, b):
...     return (a + b) * 2
>>> example(1, 2)
6
>>> example(1, -2)
Traceback (most recent call last):
  ...
PreContractError: expected a + b > 0 (where a=1, b=-2)
deal.post(validator, *, message: str | None = None, exception: ExceptionType | None = None)Callable[[C], C][source]

Decorator implementing postcondition value contract.

Postcondition is a condition that must be true for the function result. Raises PostContractError otherwise.

Parameters
  • validator – a function or validator that implements the contract.

  • message – error message for the exception raised on contract violation. No error message by default.

  • exception – exception type to raise on the contract violation. PostContractError by default.

Returns

a function wrapper.

>>> import deal
>>> @deal.post(lambda res: res > 0)
... def example(a, b):
...     return a + b
>>> example(-1, 2)
1
>>> example(1, -2)
Traceback (most recent call last):
  ...
PostContractError: expected res > 0 (where res=-1)
deal.ensure(validator, *, message: str | None = None, exception: ExceptionType | None = None)Callable[[C], C][source]

Decorator implementing postcondition value contract.

Postcondition is a condition that must be true for the function result. Raises PostContractError otherwise. It’s like @deal.post but contract accepts as input value not only the function result but also the function input arguments. The function result is passed into validator as result keyword argument.

Parameters
  • validator – a function or validator that implements the contract.

  • message – error message for the exception raised on contract violation. No error message by default.

  • exception – exception type to raise on the contract violation. PostContractError by default.

Returns

a function wrapper.

>>> import deal
>>> @deal.ensure(lambda a, result: a < result)
... def example(a):
...     return a * 2
>>> example(2)
4
>>> example(0)
Traceback (most recent call last):
  ...
PostContractError: expected a < result (where result=0, a=0)
deal.inv(validator, *, message: str | None = None, exception: ExceptionType | None = None)Callable[[T], T][source]

Decorator implementing invariant value contract.

Invariant is a condition that can be relied upon to be true during execution of a program. @deal.inv is triggered in 3 cases:

  1. Before class method execution.

  2. After class method execution.

  3. After some class attribute setting.

Deal doesn’t rollback changes on contract violation.

Parameters
  • validator – a function or validator that implements the contract.

  • message – error message for the exception raised on contract violation. No error message by default.

  • exception – exception type to raise on the contract violation. InvContractError by default.

Returns

a class wrapper.

>>> import deal
>>> @deal.inv(lambda obj: obj.likes >= 0)
... class Video:
...   likes = 1
...   def like(self): self.likes += 1
...   def dislike(self): self.likes -= 1
...
>>> v = Video()
>>> v.dislike()
>>> v.likes
0
>>> v.dislike()
Traceback (most recent call last):
...
InvContractError: expected obj.likes >= 0
>>> v.likes
-1
>>> v.likes = 2
>>> v.likes = -2
Traceback (most recent call last):
...
InvContractError: expected obj.likes >= 0
>>> v.likes
-2
deal.example(validator: Callable[], bool])Callable[[C], C][source]

Decorator for providing a usage example for the wrapped function.

The example isn’t checked at runtime. Instead, it is run in tests and checked by the linter. The example should use the decorated function and return True if the result is expected.

>>> import deal
>>> @deal.example(lambda: double(3) == 6)
... def double(x):
...   return x * 2
...
deal.dispatch(func: C)Dispatch[C][source]

Combine multiple functions into one.

When the decorated function is called, it will try to call all registered functions and return the result from the first one that doesn’t raise PreContractError.

>>> import deal
>>> @deal.dispatch
... def double(x: int) -> int:
...   raise NotImplementedError
...
>>> @double.register
... @deal.pre(lambda x: x == 3)
... def _(x: int) -> int:
...   return 6
...
>>> @double.register
... @deal.pre(lambda x: x == 4)
... def _(x: int) -> int:
...   return 8
...
>>> double(3)
6
>>> double(4)
8
>>> double(5)
Traceback (most recent call last):
    ...
NoMatchError: expected x == 3 (where x=5); expected x == 4 (where x=5)

Side-effects and exceptions

deal.has(*markers: str, message: str | None = None, exception: ExceptionType | None = None)Callable[[C], C][source]

Decorator controlling side-effects of the function.

Allows to specify markers identifying which side-effect the functon may have. Side-effects must be propagated into all callers using deal.has, this is controlled by the linter. Some side-effects are also checked at runtime.

>>> import deal
>>> @deal.has()
... def greet():
...   print('hello')
...
>>> greet()
Traceback (most recent call last):
    ...
SilentContractError
>>> @deal.has('stdout')
... def greet():
...   print('hello')
...
>>> greet()
hello
deal.raises(*exceptions: type[BaseException], message: str | None = None, exception: ExceptionType | None = None)Callable[[C], C][source]

Decorator listing the exceptions which the function can raise.

Implements exception contract. If the function raises an exception not listed in the decorator, RaisesContractError will be raised.

Parameters
  • exceptions – exceptions which the function can raise.

  • message – error message for the exception raised on contract violation. No error message by default.

  • exception – exception type to raise on the contract violation. RaisesContractError by default.

Returns

a function wrapper.

>>> import deal
>>> @deal.raises(ZeroDivisionError, ValueError)
... def div(a, b):
...   return a / b
...
>>> div(1, 0)
Traceback (most recent call last):
    ...
ZeroDivisionError: division by zero
>>> div(1, '')
Traceback (most recent call last):
    ...
 TypeError: unsupported operand type(s) for /: 'int' and 'str'
 The above exception was the direct cause of the following exception:
    ...
RaisesContractError
deal.reason(event: type[Exception], validator, *, message: str | None = None, exception: ExceptionType | None = None)Callable[[C], C][source]

Decorator implementing exception contract.

Allows to assert precondition for raised exception. It’s like @deal.ensure but when instead of returning result the function raises an exception.

Parameters
  • event – exception raising which will trigger contract validation.

  • validator – a function or validator that implements the contract.

  • message – error message for the exception raised on contract violation. No error message by default.

  • exception – exception type to raise on the contract violation. ReasonContractError by default.

Returns

a function wrapper.

>>> import deal
>>> @deal.reason(ZeroDivisionError, lambda a, b: b == 0)
... def div(a, b):
...   return a / (a - b)
>>> div(2, 1)
2.0
>>> div(0, 0)
Traceback (most recent call last):
    ...
ZeroDivisionError: division by zero
>>> div(2, 2)
Traceback (most recent call last):
    ...
 ZeroDivisionError: division by zero
 The above exception was the direct cause of the following exception:
    ...
ReasonContractError: expected b == 0 (where a=2, b=2)

Helpers

deal.inherit(func: TF)TF[source]

Inherit contracts from base classes.

Can be used to decorate either the whole class or a separate method.

>>> import deal
>>> class Shape:
...   @deal.post(lambda r: r > 2)
...   def get_sides(self):
...     raise NotImplementedError
...
>>> class Triangle(Shape):
...   @deal.inherit
...   def get_sides(self):
...     return 3
...
>>> class Line(Shape):
...   @deal.inherit
...   def get_sides(self):
...     return 2
...
>>> triangle = Triangle()
>>> triangle.get_sides()
3
>>> line = Line()
>>> line.get_sides()
Traceback (most recent call last):
    ...
PreContractError: expected r > 0 (where r=2)
deal.chain(*contracts: Callable[[C], C])Callable[[F], F][source]

Decorator to chain 2 or more contracts together.

It can be helpful to store contracts separately from the function. Consider using it when you have too many contracts. Otherwise, the function will be lost under a bunch of decorators.

>>> import deal
>>> sum_contract = deal.chain(
...   deal.pre(lambda a, b: a > 0),
...   deal.pre(lambda a, b: b > 0),
...   deal.post(lambda res: res > 0),
... )
>>> @sum_contract
... def sum(a, b):
...   return a + b
...
>>> sum(2, 3)
5
>>> sum(2, -3)
Traceback (most recent call last):
    ...
PreContractError: expected b > 0 (where a=2, b=-3)
>>> sum(-2, 3)
Traceback (most recent call last):
    ...
PreContractError: expected a > 0 (where a=-2, b=3)
Parameters

contracts – contracts to chain.

Returns

a function wrapper

deal.pure(_func: C)C[source]

Decorator for pure functions.

Alias for @deal.chain(deal.has(), deal.safe).

Pure function has no side-effects and doesn’t raise any exceptions.

>>> import deal
>>> @deal.pure
... def div(a, b, log=False):
...   if log:
...     print('div called')
...   return a / b
...
>>> div(2, 4)
0.5
>>> div(2, 0)
Traceback (most recent call last):
    ...
 ZeroDivisionError: division by zero
 The above exception was the direct cause of the following exception:
    ...
RaisesContractError
>>> div(2, 3, log=True)
Traceback (most recent call last):
    ...
SilentContractError
deal.safe(*, message: str | None = 'None', exception: ExceptionType | None = 'None')Callable[[C], C][source]
deal.safe(_func: C)C

Alias for @deal.raises(). Wraps a function that never raises an exception.

>>> import deal
>>> @deal.safe
... def div(a, b):
...   return a / b
...
>>> div(2, 4)
0.5
>>> div(2, 0)
Traceback (most recent call last):
    ...
 ZeroDivisionError: division by zero
 The above exception was the direct cause of the following exception:
    ...
RaisesContractError
deal.implies(test, then: T)bool | T[source]

Check then only if test is true.

A convenient helper for contracts that must be checked only for some cases. It is known as “implication” or material conditional.

>>> import deal
>>> deal.implies(False, False)
True
>>> deal.implies(False, True)
True
>>> deal.implies(True, False)
False
>>> deal.implies(True, True)
True
deal.catch(func: Callable, *args, **kwargs)type[Exception] | None[source]

Call the function with the given arguments, catching any exception.

The catched exception is returned. This function may be useful in combination with {py:func}deal.example.

>>> import deal
>>> @deal.example(lambda: deal.catch(div, 4, 0) is ZeroDivisionError)
... @deal.raises(ZeroDivisionError)
... @deal.reason(ZeroDivisionError, lambda x: x == 0)
... def div(x, y):
...   return x / y
...
>>>

Testing

Keep in mind that sphinx skipped some of the docstrings for deal.cases.

class deal.cases(func: Callable, *, count: int = 50, kwargs: dict[str, Any] | None = None, check_types: bool | None = None, settings: hypothesis.settings | None = None, seed: int | None = None)[source]

Generate test cases for the given function.

__call__(test_func: Callable[[], None])Callable[[], None][source]
__call__()None
__call__(buffer: bytes | bytearray | memoryview | BinaryIO)bytes | None

Allows deal.cases to be used as decorator, test function, or fuzzing target.

__init__(func: Callable, *, count: int = 50, kwargs: dict[str, Any] | None = None, check_types: bool | None = None, settings: hypothesis.settings | None = None, seed: int | None = None)None[source]

Create test cases generator.

>>> import deal
>>> @deal.pre(lambda a, b: b != 0)
... def div(a: int, b: int) -> float:
...   return a / b
...
>>> cases = deal.cases(div)
>>>
__iter__()Iterator[TestCase][source]

Emits test cases.

It can be helpful when you want to see what test cases are generated. The recommend way is to use deal.cases as a decorator instead.

>>> import deal
>>> @deal.pre(lambda a, b: b != 0)
... def div(a: int, b: int) -> float:
...   return a / b
...
>>> cases = iter(deal.cases(div))
>>> next(cases)
TestCase(args=(), kwargs=..., func=<function div ...>, exceptions=(), check_types=True)
>>> for case in cases:
...   result = case()  # execute the test case
>>>
__repr__()str[source]

Return repr(self).

__weakref__

list of weak references to the object (if defined)

check_types: bool

check that the result matches return type of the function. Enabled by default.

count: int

how many test cases to generate, defaults to 50.

func: Callable

the function to test. Should be type annotated.

kwargs: dict[str, Any]

keyword arguments to pass into the function.

seed: int | None

Random seed to use when generating test cases. Use it to make tests deterministic.

settings: hypothesis.settings

Hypothesis settings to use instead of default ones.

class deal.TestCase(args: tuple[Any, ], kwargs: dict[str, Any], func: Callable, exceptions: tuple[type[Exception], ], check_types: bool)[source]

A callable object, wrapper around a function that must be tested.

When called, calls the wrapped function, suppresses expected exceptions, checks the type of the result, and returns it.

property args

Positional arguments to be passed in the function

property check_types

Check that the result matches return type of the function.

property exceptions

Exceptions that must be suppressed.

property func

The function which will be called when the test case is called

property kwargs

Keyword arguments to be passed in the function

Introspection

The module provides get_contracts function which enumerates contracts wrapping the given function. Every contract is returned in wrapper providing a stable interface.

Usage example:

import deal

@deal.pre(lambda x: x > 0)
def f(x):
    return x + 1

contracts = deal.introspection.get_contracts(f)
for contract in contracts:
    assert isinstance(contract, deal.introspection.Contract)
    assert isinstance(contract, deal.introspection.Pre)
    assert contract.source == 'x > 0'
    assert contract.exception is deal.PreContractError
    contract.validate(1)

State management

deal.disable(*, permament: bool = False, warn: bool = True)None

Disable all contracts.

If permament=True, contracts are permanently disabled for the current interpreter runtime and cannot be turned on again.

By default, deal will do a few sanity checks to make sure you haven’t unintentionally disabled contracts on a test environment. Pass warn=False to disable this behavior.

deal.enable(warn: bool = True)None

Enable all contracts.

By default, deal will do a few sanity checks to make sure you haven’t unintentionally enabled contracts on a production environment. Pass warn=False to disable this behavior.

deal.reset()None

Restore contracts state to the default.

All contracts are disabled on production by default. See runtime documentation.

Other functions

deal.autodoc(app: SphinxApp)None[source]

Activate the hook for sphinx that includes contracts into documentation generated by autodoc.

deal.activate()bool[source]

Activate module-level checks.

This function must be called before importing anything with deal.module_load contract. Otherwise, the contract won’t be executed.

The best practice is to place it in __init__.py of your project:

>>> import deal
>>> deal.activate()

See Contracts for importing modules documentation for more details.

deal.module_load(*contracts)None[source]

Specify contracts that will be checked at module import time. Keep in mind that deal.activate must be called before importing a module with module_load contract.

>>> import deal
>>> deal.module_load(deal.has(), deal.safe)

See Contracts for importing modules documentation for more details.

Exceptions

exception deal.ContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)[source]

The base class for all errors raised by deal contracts.

exception deal.ExampleContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.example for contract violation.

deal.example contracts are checked only during testing and linting, not at runtime.

exception deal.InvContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.inv for contract violation.

exception deal.MarkerError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The base class for errors raised by deal.has for contract violation.

exception deal.NoMatchError(exceptions: tuple[PreContractError, ])

The error raised by deal.dispatch when there is no matching implementation.

“No matching implementation” means that all registered functions raised PreContractError.

exception deal.OfflineContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.has for networking markers violation.

The networking can be allowed by markers io, network, and socket.

exception deal.PostContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.post for contract violation.

exception deal.PreContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.pre for contract violation.

exception deal.RaisesContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.raises for contract violation.

exception deal.ReasonContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.reason for contract violation.

exception deal.SilentContractError(message: str = '', errors=None, validator=None, params: dict[str, Any] | None = None, origin: object | None = None)

The error raised by deal.has for printing markers violation.

The printing can be allowed by markers io, print, stdout, and stderr.