We provide several extra features for Hypothesis users.
And encourage to use it together with returns
.
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.
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.
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.
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.
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.
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!
Our types that we register in hypothesis to be working with st.from_type
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.
container_type (Type
[ForwardRef
]) –
use_init (bool
) –
Callable
[[type
], SearchStrategy
]
alias of staticmethod
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.
Function used to define this law.
Returns a name of the given law. Basically a name of the function.
str
Bases: returns.primitives.laws.Law
, Generic
[returns.primitives.laws._TypeArgType1
, returns.primitives.laws._ReturnType
]
Law definition for functions with a single argument.
function (Callable
[[~_TypeArgType1], ~_ReturnType]) –
Bases: returns.primitives.laws.Law
, Generic
[returns.primitives.laws._TypeArgType1
, returns.primitives.laws._TypeArgType2
, returns.primitives.laws._ReturnType
]
Law definition for functions with two arguments.
function (Callable
[[~_TypeArgType1, ~_TypeArgType2], ~_ReturnType]) –
Bases: returns.primitives.laws.Law
, Generic
[returns.primitives.laws._TypeArgType1
, returns.primitives.laws._TypeArgType2
, returns.primitives.laws._TypeArgType3
, returns.primitives.laws._ReturnType
]
Law definition for functions with three argument.
function (Callable
[[~_TypeArgType1, ~_TypeArgType2, ~_TypeArgType3], ~_ReturnType]) –
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 hypothesis
settings inside:
check_all_laws(IO, {'max_examples': 100})
Note
Cannot be used inside doctests because of the magic we use inside.
See also
container_type (Type
[Lawful
]) –
settings_kwargs (Optional
[Dict
[str
, Any
]]) –
use_init (bool
) –
None
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.
container_type (Type
[Lawful
]) –
settings (_Settings
) –
Iterator
[None
]
Temporary registers a container if it is not registered yet.
container_type (Type
[Lawful
]) –
use_init (bool
) –
Iterator
[None
]