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 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 means you can use Result
, Maybe
, etc. in your own property tests, and hypothesis
will generate values for them as expected.
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 laws in both YourCustomContainer
and all its super types, including our internal ones and user-defined 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, use returns_lawful
marker to exclude them from pytest-xdist
execution plan.
hypothesis
works by looking up strategies for the provided type annotations. Given that the types provided by returns
are very complicated and not really native to Python, they may not be understood by hypothesis
, and you may get runtime exceptions such as ResolutionFailed
.
In such cases, you may want to register custom strategies for types for which hypothesis
does not find any strategies.
The main use case is registering a custom strategy to generate your container when running its laws:
from hypothesis import strategies as st check_all_laws(Number, container_strategy=st.builds(Number, st.integers()))
You can also register strategies for other types:
from hypothesis import strategies as st check_all_laws( Number, container_strategy=st.builds(Number, st.integers()), type_strategies={Foo: st.builds(Foo, st.text())}, )
These custom strategies will be used only when running the tests generated by the check_all_laws
call above. They will have no effect on any other property tests that involve the same types. You cannot use this argument together with use_init
.
We provide a utility function to create hypothesis
strategy from any container: strategy_from_container
.
You can use it to 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.
Avoid directly registering your container’s strategy with hypothesis
using st.register_type_strategy
. Because of the way we emulate higher-kinded types, hypothesis
may mistakenly use the strategy for other incompatible containers and cause spurious test failures. We specify how to do it just in case you need it and you know what you’re doing.
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¶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
[Lawful
])
use_init (bool
)
Callable
[[type
], SearchStrategy
]
classDiagram Generic <|-- Law1 Generic <|-- Law2 Generic <|-- Law3 Generic <|-- Lawful Immutable <|-- Law Law <|-- Law1 Law <|-- Law2 Law <|-- Law3
Special alias to define laws as functions even inside a class
Bases: Immutable
Base class for all laws. Does not have an attached signature.
Should not be used directly. Use Law1
, Law2
or Law3
instead.
Callable
¶
Function used to define this law.
Returns a name of the given law. Basically a name of the function.
Bases: Law
, Generic
[_TypeArgType1
, _ReturnType
]
Law definition for functions with a single argument.
function (Callable
[[TypeVar
(_TypeArgType1
)], TypeVar
(_ReturnType
)])
Bases: Law
, Generic
[_TypeArgType1
, _TypeArgType2
, _ReturnType
]
Law definition for functions with two arguments.
function (Callable
[[TypeVar
(_TypeArgType1
), TypeVar
(_TypeArgType2
)], TypeVar
(_ReturnType
)])
Bases: Law
, Generic
[_TypeArgType1
, _TypeArgType2
, _TypeArgType3
, _ReturnType
]
Law definition for functions with three argument.
function (Callable
[[TypeVar
(_TypeArgType1
), TypeVar
(_TypeArgType2
), TypeVar
(_TypeArgType3
)], TypeVar
(_ReturnType
)])
Bases: Generic
[_Caps
]
Base class for all lawful classes.
Allows to smartly collect all defined laws from all parent classes.
Bases: object
Base class for all collection of laws aka LawSpecs.
Bases: object
Settings for the law tests.
This sets the context for each generated law test, by temporarily registering strategies for various types and passing any hypothesis
settings.
Any settings passed by the user will override the value from default_settings()
.
settings_kwargs (dict
[str
, Any
])
use_init (bool
)
container_strategy (Union
[SearchStrategy
[TypeVar
(Example_co
, covariant=True)], Callable
[[type
[TypeVar
(Example_co
, covariant=True)]], SearchStrategy
[TypeVar
(Example_co
, covariant=True)]], None
])
type_strategies (dict
[type
[object
], Union
[SearchStrategy
[TypeVar
(Example_co
, covariant=True)], Callable
[[type
[TypeVar
(Example_co
, covariant=True)]], SearchStrategy
[TypeVar
(Example_co
, covariant=True)]]]])
dict
[str
, Any
]¶
Settings directly passed on to hypothesis. We support all kwargs from @settings
, see @settings docs.
bool
¶
Whether to create examples using __init__
instead of the default .
Union
[SearchStrategy
[TypeVar
(Example_co
, covariant=True)], Callable
[[type
[TypeVar
(Example_co
, covariant=True)]], SearchStrategy
[TypeVar
(Example_co
, covariant=True)]], None
]¶
Strategy for generating the container. By default, we generate examples of a container using: returns.contrib.hypothesis.containers.strategy_from_container()
.
dict
[type
[object
], Union
[SearchStrategy
[TypeVar
(Example_co
, covariant=True)], Callable
[[type
[TypeVar
(Example_co
, covariant=True)]], SearchStrategy
[TypeVar
(Example_co
, covariant=True)]]]]¶
Strategies for generating values of types other than the container and its lawful interfaces. This can be useful for overriding TypeVar
, Callable
, etc. in case you use certain types that hypothesis
is unable to find.
Return default settings for creating law tests.
We use some special strategies by default, but they can be overridden by the user if needed:
TypeVar
: We need to make sure that the values generated behave sensibly when tested for equality.
collections.abc.Callable
: We need to generate pure functions, which are not the default.
Note that this is collections.abc.Callable, NOT typing.Callable. This is because, at runtime, typing.get_origin(Callable[[int], str]) is collections.abc.Callable. So, this is the type we should register with hypothesis.
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, settings_kwargs={'max_examples': 100})
Note
Cannot be used inside doctests because of the magic we use inside.
container_type (type
[Lawful
[TypeVar
(Example_co
, covariant=True)]])
settings_kwargs (dict
[str
, Any
] | None
)
use_init (bool
)
container_strategy (Union
[SearchStrategy
[TypeVar
(Example_co
, covariant=True)], Callable
[[type
[TypeVar
(Example_co
, covariant=True)]], SearchStrategy
[TypeVar
(Example_co
, covariant=True)]], None
])
type_strategies (dict
[type
[object
], Union
[SearchStrategy
[TypeVar
(Example_co
, covariant=True)], Callable
[[type
[TypeVar
(Example_co
, covariant=True)]], SearchStrategy
[TypeVar
(Example_co
, covariant=True)]]]] | None
)
None
Factory to create pure functions.
SearchStrategy
Strategy factory for TypeVar
objects.
We ensure that values inside strategies are self-equal. For example, float('nan')
does not work for us.
thing (type
[object
])
Union
[SearchStrategy
[TypeVar
(Example_co
, covariant=True)], Callable
[[type
[TypeVar
(Example_co
, covariant=True)]], SearchStrategy
[TypeVar
(Example_co
, covariant=True)]]]
We register a lot of types in _entrypoint.py, we need to clean them.
Otherwise, some types might be messed up.
Iterator
[None
]
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4