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 asresult
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:Before class method execution.
After class method execution.
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 iftest
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 >>>
-
__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
-
property
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.
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
, andsocket
.
-
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
, andstderr
.