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 registering 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 instantiate 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 combination 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, 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, usereturns_lawfulmarker to exclude them frompytest-xdistexecution plan.
Further reading¶
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!
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 exampleFutureResultrequiresAwaitable[Result[a, b]]as an__init__value.But, custom containers pass
use_initif they are not an instance ofApplicativeNand do not have a working.from_valuemethod. For example, pureMappableNcan 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[Lawful])use_init (
bool)
- Return type:
Callable[[type],SearchStrategy]
DSL to define laws¶
classDiagram
Generic <|-- Law1
Generic <|-- Law2
Generic <|-- Law3
Generic <|-- Lawful
Immutable <|-- Law
Law <|-- Law1
Law <|-- Law2
Law <|-- Law3
- law_definition¶
Special alias to define laws as functions even inside a class
- class Law(function)[source]¶
Bases:
ImmutableBase class for all laws. Does not have an attached signature.
Should not be used directly. Use
Law1,Law2orLaw3instead.-
definition:
Callable¶ Function used to define this law.
- property name: str¶
Returns a name of the given law. Basically a name of the function.
-
definition:
- final class Law1(function)[source]¶
Bases:
Law,Generic[_TypeArgType1,_ReturnType]Law definition for functions with a single argument.
- Parameters:
function (
Callable[[TypeVar(_TypeArgType1)],TypeVar(_ReturnType)])
- final class Law2(function)[source]¶
Bases:
Law,Generic[_TypeArgType1,_TypeArgType2,_ReturnType]Law definition for functions with two arguments.
- Parameters:
function (
Callable[[TypeVar(_TypeArgType1),TypeVar(_TypeArgType2)],TypeVar(_ReturnType)])
- final class Law3(function)[source]¶
Bases:
Law,Generic[_TypeArgType1,_TypeArgType2,_TypeArgType3,_ReturnType]Law definition for functions with three argument.
- Parameters:
function (
Callable[[TypeVar(_TypeArgType1),TypeVar(_TypeArgType2),TypeVar(_TypeArgType3)],TypeVar(_ReturnType)])
Plugin internals¶
- check_all_laws(container_type, *, settings_kwargs=None, use_init=False)[source]¶
Function to check all defined 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
hypothesissettings inside:check_all_laws(IO, settings_kwargs={'max_examples': 100})
Note
Cannot be used inside doctests because of the magic we use inside.
See also
- Parameters:
container_type (
type[Lawful])settings_kwargs (
dict[str,Any] |None)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
Resulttype. It is a subtype ofContainerN,MappableN,BindableN, etc. When we check this type, we needMappableNto resolve toResult.Can be used independently from other functions.
- Parameters:
container_type (
type[Lawful])settings (
_Settings)
- Return type:
Iterator[None]
- register_container(container_type, *, use_init)[source]¶
Temporary registers 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
Callableas pure functions.It is not a default in
hypothesis.- Return type:
Iterator[None]