hypothesis plugin

We provide several extra features for Hypothesis users. And encourage to use it together with returns.

Installation

You will need to install hypothesis separately. It is not bundled with returns.

We also require anyio package for this plugin to work with async laws.

hypothesis entrypoint

We support a hypothesis entrypoint that is executed on hypothesis import.

There we are regestering all our containers as strategies. So, you don’t have to. Example:

from returns.result import Result
from hypothesis import strategies as st

assert st.from_type(Result).example()

This is a convenience thing only.

strategy_from_container

We provide a utility function to create hypothesis strategy from any container.

You can use it to easily register your own containers.

from hypothesis import strategies as st
from returns.contrib.hypothesis.containers import strategy_from_container

st.register_type_strategy(
    YourContainerClass,
    strategy_from_container(YourContainerClass),
)

You can also pass use_init keyword argument if you wish to use __init__ method to instanciate your containers. Turned off by default. Example:

st.register_type_strategy(
    YourContainerClass,
    strategy_from_container(YourContainerClass, use_init=True),
)

Or you can write your own hypothesis strategy. It is also fine.

check_all_laws

We also provide a very powerful mechanism of checking defined container laws. It works in a combitation with “Laws as Values” feature we provide in the core.

from returns.contrib.hypothesis.laws import check_all_laws
from your.module import YourCustomContainer

check_all_laws(YourCustomContainer)

This one line of code will generate ~100 tests for all defined law in both YourCustomContainer and all its super types, including our internal ones.

We also provide a way to configure the checking process with settings_kwargs:

check_all_laws(YourCustomContainer, settings_kwargs={'max_examples': 500})

This will increase the number of generated test to 500. We support all kwargs from @settings, see @settings docs.

You can also change how hypothesis creates instances of your container. By default, we use .from_value, .from_optional, and .from_failure if we are able to find them.

But, you can also pass types without these methods, but with __init__ defined:

from typing import Callable, TypeVar
from typing_extensions import final
from returns.interfaces.mappable import Mappable1
from returns.primitives.container import BaseContainer
from returns.primitives.hkt import SupportsKind1

_ValueType = TypeVar('_ValueType')
_NewValueType = TypeVar('_NewValueType')

@final
class Number(
    BaseContainer,
    SupportsKind1['Number', _ValueType],
    Mappable1[_ValueType],
):
    def __init__(self, inner_value: _ValueType) -> None:
        super().__init__(inner_value)

    def map(
        self,
        function: Callable[[_ValueType], _NewValueType],
    ) -> 'Number[_NewValueType]':
        return Number(function(self._inner_value))

# We want to allow ``__init__`` method to be used:
check_all_laws(Number, use_init=True)

As you see, we don’t support any from methods here. But, __init__ would be used to generate values thanks to use_init=True.

By default, we don’t allow to use __init__, because there are different complex types like Future, ReaderFutureResult, etc that have complex __init__ signatures. And we don’t want to mess with them.

Warning::

Checking laws is not compatible with pytest-xdist, because we use a lot of global mutable state there. Please, use returns_lawful marker to exclude them from pytest-xdist execution plan.

API Reference

Types we have already registered for you

Used to register all our types as hypothesis strategies.

See: https://hypothesis.readthedocs.io/en/latest/strategies.html

But, beware that we only register concrete types here, interfaces won’t be registered!

REGISTERED_TYPES: Sequence[Type[returns.primitives.laws.Lawful]] = (<class 'returns.result.Result'>, <class 'returns.maybe.Maybe'>, <class 'returns.io.IO'>, <class 'returns.io.IOResult'>, <class 'returns.future.Future'>, <class 'returns.future.FutureResult'>, <class 'returns.context.requires_context.RequiresContext'>, <class 'returns.context.requires_context_result.RequiresContextResult'>, <class 'returns.context.requires_context_ioresult.RequiresContextIOResult'>, <class 'returns.context.requires_context_future_result.RequiresContextFutureResult'>)

Our types that we register in hypothesis to be working with st.from_type

DSL to register custom containers

strategy_from_container(container_type, *, use_init=False)[source]

Creates a strategy from a container type.

Basically, containers should not support __init__ even when they have one. Because, that can be very complex: for example FutureResult requires Awaitable[Result[a, b]] as an __init__ value.

But, custom containers pass use_init if they are not an instance of ApplicativeN and do not have a working .from_value method. For example, pure MappableN can do that.

We also try to resolve generic arguments. So, Result[_ValueType, Exception] will produce any value for success cases and only exceptions for failure cases.

Parameters
  • container_type (Type[ForwardRef]) –

  • use_init (bool) –

Return type

Callable[[type], SearchStrategy]

DSL to define laws

classDiagram Law <|-- Law2 Generic <|-- Law2 Law <|-- Law3 Immutable <|-- Law Generic <|-- Law1 Generic <|-- Lawful Generic <|-- Law3 Law <|-- Law1
law_definition

alias of builtins.staticmethod

class Law(function)[source]

Bases: returns.primitives.types.Immutable

Base class for all laws. Does not have an attached signature.

Should not be used directly. Use Law1, Law2 or Law3 instead.

definition: Callable

Function used to define this law.

property name

Returns a name of the given law. Basically a name of the function.

Return type

str

class Law1(function)[source]

Bases: returns.primitives.laws.Law, typing.Generic

Law definition for functions with a single argument.

Parameters

function (Callable[[~_TypeArgType1], ~_ReturnType]) –

definition: Callable[[Law1, _TypeArgType1], _ReturnType]
class Law2(function)[source]

Bases: returns.primitives.laws.Law, typing.Generic

Law definition for functions with two arguments.

Parameters

function (Callable[[~_TypeArgType1, ~_TypeArgType2], ~_ReturnType]) –

definition: Callable[[Law2, _TypeArgType1, _TypeArgType2], _ReturnType]
class Law3(function)[source]

Bases: returns.primitives.laws.Law, typing.Generic

Law definition for functions with three argument.

Parameters

function (Callable[[~_TypeArgType1, ~_TypeArgType2, ~_TypeArgType3], ~_ReturnType]) –

definition: Callable[[Law3, _TypeArgType1, _TypeArgType2, _TypeArgType3], _ReturnType]
class Lawful(*args, **kwds)[source]

Bases: typing.Generic

Base class for all lawful classes.

Allows to smartly collect all defined laws from all parent classes.

classmethod laws()[source]

Collects all laws from all parent classes.

Algorithm:

  1. First, we collect all unique parents in __mro__

  2. Then we get the laws definition from each of them

  3. Then we structure them in a type: its_laws way

Return type

Dict[Type[Lawful], Sequence[Law]]

class LawSpecDef[source]

Bases: object

Base class for all collection of laws aka LawSpecs.

Plugin internals

check_all_laws(container_type, *, settings_kwargs=None, use_init=False)[source]

Function to check all definied mathematical laws in a specified container.

Should be used like so:

from returns.contrib.hypothesis.laws import check_all_laws
from returns.io import IO

check_all_laws(IO)

You can also pass different hypothesis settings inside:

check_all_laws(IO, {'max_examples': 100})

Note

Cannot be used inside doctests because of the magic we use inside.

See: https://mmhaskell.com/blog/2017/3/13/obey-the-type-laws

Parameters
  • container_type (Type[Lawful]) –

  • settings_kwargs (Optional[Dict[str, Any]]) –

  • use_init (bool) –

Return type

None

container_strategies(container_type, *, settings)[source]

Registers all types inside a container to resolve to a correct strategy.

For example, let’s say we have Result type. It is a subtype of ContainerN, MappableN, BindableN, etc. When we check this type, we need MappableN to resolve to Result.

Can be used independently from other functions.

Parameters
  • container_type (Type[Lawful]) –

  • settings (_Settings) –

Return type

Iterator[None]

maybe_register_container(container_type, *, use_init)[source]

Temporary registeres a container if it is not registered yet.

Parameters
  • container_type (Type[Lawful]) –

  • use_init (bool) –

Return type

Iterator[None]

pure_functions()[source]

Context manager to resolve all Callable as pure functions.

It is not a default in hypothesis.

Return type

Iterator[None]

type_vars()[source]

Our custom TypeVar handling.

There are several noticable differences:

  1. We add mutable types to the tests: like list and dict

  2. We ensure that values inside strategies are self-equal, for example, nan does not work for us

Return type

Iterator[None]