Skip to content

Commit 9e7d609

Browse files
committed
hypothesis: extract a pure function for types-to-strategies.
Phew. Finally.
1 parent e093247 commit 9e7d609

2 files changed

Lines changed: 54 additions & 93 deletions

File tree

returns/contrib/hypothesis/laws.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import dataclasses
22
import inspect
3-
from collections.abc import Callable, Iterator
3+
from collections.abc import Callable, Iterator, Mapping
44
from contextlib import ExitStack, contextmanager
55
from typing import Any, TypeVar, final, overload
66

@@ -209,28 +209,29 @@ def _run_law(
209209
def factory(source: st.DataObject) -> None:
210210
with ExitStack() as stack:
211211
stack.enter_context(clean_plugin_context())
212-
_enter_hypothesis_context(stack, container_type, settings)
212+
stack.enter_context(
213+
strategies_for_types(
214+
_types_to_strategies(container_type, settings)
215+
)
216+
)
213217
source.draw(st.builds(law.definition))
214218

215219
return factory
216220

217221

218-
def _enter_hypothesis_context(
219-
stack: ExitStack,
222+
def _types_to_strategies(
220223
container_type: type[Lawful],
221224
settings: _Settings,
222-
) -> None:
223-
stack.enter_context(
224-
strategies_for_types({
225-
TypeVar: type_vars_factory, # type: ignore[dict-item]
226-
Callable: pure_functions_factory, # type: ignore[dict-item]
227-
**{
228-
interface: _strategy_for_container(container_type, settings)
229-
for interface in container_type.laws()
230-
},
231-
container_type: _strategy_for_container(container_type, settings),
232-
})
233-
)
225+
) -> Mapping[type[object], StrategyFactory]:
226+
return {
227+
TypeVar: type_vars_factory, # type: ignore[dict-item]
228+
Callable: pure_functions_factory, # type: ignore[dict-item]
229+
**{
230+
interface: _strategy_for_container(container_type, settings)
231+
for interface in container_type.laws()
232+
},
233+
container_type: _strategy_for_container(container_type, settings),
234+
}
234235

235236

236237
def _strategy_for_container(

tests/test_contrib/test_hypothesis/test_type_resolution.py

Lines changed: 37 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from collections.abc import Callable, Sequence
2-
from contextlib import ExitStack
32
from typing import Any, TypeVar
43

54
import pytest
@@ -17,8 +16,8 @@
1716
RequiresContextResultE,
1817
)
1918
from returns.contrib.hypothesis.laws import (
20-
_enter_hypothesis_context, # noqa: PLC2701
2119
_Settings, # noqa: PLC2701
20+
_types_to_strategies, # noqa: PLC2701
2221
)
2322
from returns.contrib.hypothesis.type_resolver import (
2423
StrategyFactory,
@@ -116,134 +115,95 @@ def test_custom_readerresult_types_resolve(
116115
_ValueType = TypeVar('_ValueType')
117116

118117

119-
def test_hypothesis_state_outside_context() -> None: # noqa: WPS210
120-
"""Check values of strategies before we register them.
121-
122-
This is mostly useful as a baseline to compare the the values when we do
123-
register them.
124-
"""
118+
def test_types_to_strategies_default() -> None: # noqa: WPS210
119+
"""Check the default strategies for types."""
125120
container_type = test_custom_type_applicative._Wrapper # noqa: SLF001
126121
# NOTE: There is a type error because `Callable` is a
127122
# special form, not a type.
128123
callable_type: type[object] = Callable # type: ignore[assignment]
129124

130-
container_strategy_outside = look_up_strategy(container_type)
131-
interface_strategies_outside = _interface_factories(container_type)
132-
pure_functions_strategy_outside = look_up_strategy(callable_type)
133-
type_var_strategy_outside = look_up_strategy(TypeVar)
134-
135-
assert (
136-
_strategy_string(container_strategy_outside, container_type) == 'None'
137-
)
138-
assert _strategy_strings(interface_strategies_outside, container_type) == [
139-
'None',
140-
'None',
141-
]
142-
assert (
143-
_strategy_string(
144-
pure_functions_strategy_outside, Callable[[int, str], bool]
145-
)
146-
== 'functions(like=lambda *a, **k: None, returns=booleans())'
147-
)
148-
assert (
149-
_strategy_string(type_var_strategy_outside, _ValueType)
150-
== "shared(sampled_from([<class 'NoneType'>, <class 'bool'>,"
151-
" <class 'int'>, <class 'float'>, <class 'str'>, <class 'bytes'>]),"
152-
" key='typevar=~_ValueType').flatmap(from_type)"
125+
result = _types_to_strategies(
126+
container_type,
127+
_Settings(
128+
settings_kwargs={},
129+
use_init=False,
130+
container_strategy=None,
131+
),
153132
)
154133

155-
156-
def test_hypothesis_state_inside_context() -> None: # noqa: WPS210
157-
"""Check that strategies are registered correctly."""
158-
container_type = test_custom_type_applicative._Wrapper # noqa: SLF001
159-
# NOTE: There is a type error because `Callable` is a
160-
# special form, not a type.
161-
callable_type: type[object] = Callable # type: ignore[assignment]
162-
163-
with ExitStack() as stack:
164-
_enter_hypothesis_context(
165-
stack,
166-
container_type,
167-
settings=_Settings(
168-
settings_kwargs={}, use_init=False, container_strategy=None
169-
),
170-
)
171-
container_strategy = look_up_strategy(container_type)
172-
interface_strategies = _interface_factories(container_type)
173-
pure_functions_strategy = look_up_strategy(callable_type)
174-
type_var_strategy = look_up_strategy(TypeVar)
175-
176134
wrapper_strategy = (
177135
"builds(from_value, shared(sampled_from([<class 'NoneType'>,"
178136
" <class 'bool'>, <class 'int'>, <class 'float'>, <class 'str'>,"
179137
" <class 'bytes'>]), key='typevar=~_FirstType').flatmap(from_type))"
180138
)
181139
assert (
182-
_strategy_string(container_strategy, container_type) == wrapper_strategy
140+
_strategy_string(result[container_type], container_type)
141+
== wrapper_strategy
183142
)
184-
assert _strategy_strings(interface_strategies, container_type) == [
143+
assert _strategy_strings(
144+
[result[interface] for interface in container_type.laws()],
145+
container_type,
146+
) == [
185147
wrapper_strategy,
186148
wrapper_strategy,
187149
]
188150
assert (
189-
_strategy_string(pure_functions_strategy, Callable[[int, str], bool])
151+
_strategy_string(result[callable_type], Callable[[int, str], bool])
190152
== 'functions(like=lambda *args, **kwargs: <unknown>,'
191153
' returns=booleans(), pure=True)'
192154
)
193155
assert (
194-
_strategy_string(pure_functions_strategy, Callable[[], None])
156+
_strategy_string(result[callable_type], Callable[[], None])
195157
== 'functions(like=lambda: None, returns=none(), pure=True)'
196158
)
197159
assert (
198-
_strategy_string(type_var_strategy, _ValueType)
160+
_strategy_string(result[TypeVar], _ValueType)
199161
== "shared(sampled_from([<class 'NoneType'>, <class 'bool'>,"
200162
" <class 'int'>, <class 'float'>, <class 'str'>, <class 'bytes'>]),"
201163
" key='typevar=~_ValueType').flatmap(from_type).filter(lambda"
202164
' inner: inner == inner)'
203165
)
204166

205167

206-
def test_hypothesis_state_with_setting() -> None: # noqa: WPS210
168+
def test_types_to_strategies_overrides() -> None: # noqa: WPS210
207169
"""Check that we prefer the strategies in settings."""
208170
container_type = test_custom_type_applicative._Wrapper # noqa: SLF001
209171
# NOTE: There is a type error because `Callable` is a
210172
# special form, not a type.
211173
callable_type: type[object] = Callable # type: ignore[assignment]
212174

213-
with ExitStack() as stack:
214-
_enter_hypothesis_context(
215-
stack,
216-
container_type,
217-
settings=_Settings(
218-
settings_kwargs={},
219-
use_init=False,
220-
container_strategy=st.builds(container_type, st.integers()),
221-
),
222-
)
223-
container_strategy = look_up_strategy(container_type)
224-
interface_strategies = _interface_factories(container_type)
225-
pure_functions_strategy = look_up_strategy(callable_type)
226-
type_var_strategy = look_up_strategy(TypeVar)
175+
result = _types_to_strategies(
176+
container_type,
177+
_Settings(
178+
settings_kwargs={},
179+
use_init=False,
180+
container_strategy=st.builds(container_type, st.integers()),
181+
),
182+
)
227183

228184
wrapper_strategy = 'builds(_Wrapper, integers())'
229185
assert (
230-
_strategy_string(container_strategy, container_type) == wrapper_strategy
186+
_strategy_string(result[container_type], container_type)
187+
== wrapper_strategy
231188
)
232-
assert _strategy_strings(interface_strategies, container_type) == [
189+
assert _strategy_strings(
190+
[result[interface] for interface in container_type.laws()],
191+
container_type,
192+
) == [
233193
wrapper_strategy,
234194
wrapper_strategy,
235195
]
236196
assert (
237-
_strategy_string(pure_functions_strategy, Callable[[int, str], bool])
197+
_strategy_string(result[callable_type], Callable[[int, str], bool])
238198
== 'functions(like=lambda *args, **kwargs: <unknown>,'
239199
' returns=booleans(), pure=True)'
240200
)
241201
assert (
242-
_strategy_string(pure_functions_strategy, Callable[[], None])
202+
_strategy_string(result[callable_type], Callable[[], None])
243203
== 'functions(like=lambda: None, returns=none(), pure=True)'
244204
)
245205
assert (
246-
_strategy_string(type_var_strategy, _ValueType)
206+
_strategy_string(result[TypeVar], _ValueType)
247207
== "shared(sampled_from([<class 'NoneType'>, <class 'bool'>,"
248208
" <class 'int'>, <class 'float'>, <class 'str'>, <class 'bytes'>]),"
249209
" key='typevar=~_ValueType').flatmap(from_type).filter(lambda"

0 commit comments

Comments
 (0)