Skip to content

Commit f2040fd

Browse files
committed
Closes #672
1 parent ca5b8f6 commit f2040fd

File tree

5 files changed

+54
-39
lines changed

5 files changed

+54
-39
lines changed

docs/pages/contrib/pytest_plugins.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ It tests that containers do handle error track.
4040
.. code:: python
4141
4242
from returns.result import Failure, Success
43+
from returns.contrib.pytest import ReturnsAsserts
4344
44-
def test_my_container(returns):
45+
def test_my_container(returns: ReturnsAsserts):
4546
assert not returns.is_error_handled(Failure(1))
4647
assert returns.is_error_handled(
4748
Failure(1).lash(lambda _: Success('default value')),
@@ -79,6 +80,7 @@ created and looking for the desired function.
7980
.. code:: python
8081
8182
>>> from returns.result import Result, Success, Failure
83+
>>> from returns.contrib.pytest import ReturnsAsserts
8284
8385
>>> returns_fixture = getfixture('returns')
8486
@@ -87,11 +89,15 @@ created and looking for the desired function.
8789
... return Success(int(arg))
8890
... return Failure('"{0}" is not a number'.format(arg))
8991
90-
>>> def test_if_failure_is_created_at_convert_function(returns):
92+
>>> def test_if_failure_is_created_at_convert_function(
93+
... returns: ReturnsAsserts,
94+
... ):
9195
... with returns.has_trace(Failure, desired_function):
9296
... Success('not a number').bind(desired_function)
9397
94-
>>> def test_if_success_is_created_at_convert_function(returns):
98+
>>> def test_if_success_is_created_at_convert_function(
99+
... returns: ReturnsAsserts,
100+
... ):
95101
... with returns.has_trace(Success, desired_function):
96102
... Success('42').bind(desired_function)
97103

returns/contrib/pytest/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from returns.contrib.pytest.plugin import ReturnsAsserts as ReturnsAsserts

returns/contrib/pytest/plugin.py

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from typing_extensions import Final, final
1010

1111
if TYPE_CHECKING:
12-
from returns.interfaces.specific.result import ResultBasedN
12+
from returns.interfaces.failable import FailableN
13+
from returns.interfaces.specific.result import ResultLikeN
1314

1415
_ERROR_FIELD: Final = '_error_handled'
1516
_ERROR_HANDLERS: Final = (
@@ -21,46 +22,32 @@
2122
)
2223

2324
_FunctionType = TypeVar('_FunctionType', bound=Callable)
24-
_ResultCallableType = TypeVar(
25-
'_ResultCallableType', bound=Callable[..., 'ResultBasedN'],
25+
_ReturnsResultType = TypeVar(
26+
'_ReturnsResultType', bound=Callable[..., 'ResultLikeN'],
2627
)
2728

2829

29-
def pytest_configure(config) -> None:
30-
"""
31-
Hook to be executed on import.
32-
33-
We use it define custom markers.
34-
"""
35-
config.addinivalue_line(
36-
'markers',
37-
"""
38-
returns_lawful: all tests under `check_all_laws` is marked this way,
39-
use `-m "not returns_lawful"` to skip them.
40-
""",
41-
)
42-
43-
44-
class _DesiredFunctionFound(RuntimeError):
45-
"""Exception to raise when expected function is found."""
46-
47-
4830
@final
49-
class _ReturnsAsserts(object):
31+
class ReturnsAsserts(object):
5032
"""Class with helpers assertions to check containers."""
5133

5234
__slots__ = ()
5335

54-
def is_error_handled(self, container) -> bool:
36+
def is_error_handled(self, container: 'FailableN') -> bool:
5537
"""Ensures that container has its error handled in the end."""
5638
return bool(getattr(container, _ERROR_FIELD, False))
5739

5840
@contextmanager
5941
def has_trace(
6042
self,
61-
trace_type: _ResultCallableType,
43+
trace_type: _ReturnsResultType,
6244
function_to_search: _FunctionType,
6345
) -> Iterator[None]:
46+
"""
47+
Ensures that a given function was called during execution.
48+
49+
Use it to determine where the failure happened.
50+
"""
6451
old_tracer = sys.gettrace()
6552
sys.settrace(partial(_trace_function, trace_type, function_to_search))
6653

@@ -76,6 +63,27 @@ def has_trace(
7663
sys.settrace(old_tracer)
7764

7865

66+
@pytest.fixture(scope='session')
67+
def returns(_patch_containers) -> ReturnsAsserts: # noqa: WPS442
68+
"""Returns our own class with helpers assertions to check containers."""
69+
return ReturnsAsserts()
70+
71+
72+
def pytest_configure(config) -> None:
73+
"""
74+
Hook to be executed on import.
75+
76+
We use it define custom markers.
77+
"""
78+
config.addinivalue_line(
79+
'markers',
80+
"""
81+
returns_lawful: all tests under `check_all_laws` is marked this way,
82+
use `-m "not returns_lawful"` to skip them.
83+
""",
84+
)
85+
86+
7987
@pytest.fixture(scope='session')
8088
def _patch_containers() -> None:
8189
"""
@@ -92,12 +100,6 @@ def _patch_containers() -> None:
92100
_patch_error_handling(_ERRORS_COPIERS, _PatchedContainer.copy_handler)
93101

94102

95-
@pytest.fixture(scope='session')
96-
def returns(_patch_containers) -> _ReturnsAsserts: # noqa: WPS442
97-
"""Returns our own class with helpers assertions to check containers."""
98-
return _ReturnsAsserts()
99-
100-
101103
def _patch_error_handling(methods, patch_handler) -> None:
102104
for container in _PatchedContainer.containers_to_patch():
103105
for method in methods:
@@ -107,7 +109,7 @@ def _patch_error_handling(methods, patch_handler) -> None:
107109

108110

109111
def _trace_function(
110-
trace_type: _ResultCallableType,
112+
trace_type: _ReturnsResultType,
111113
function_to_search: _FunctionType,
112114
frame: FrameType,
113115
event: str,
@@ -192,3 +194,7 @@ def factory(self, *args, **kwargs):
192194
)
193195
return original_result
194196
return wraps(original)(factory)
197+
198+
199+
class _DesiredFunctionFound(RuntimeError):
200+
"""Exception to raise when expected function is found."""

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ per-file-ignores =
6969
returns/_internal/futures/*.py: WPS204, WPS433, WPS437
7070
# We allow a lot of durty hacks in our plugins:
7171
returns/contrib/mypy/*.py: S101, WPS201
72+
returns/contrib/pytest/__init__.py: F401
7273
returns/contrib/pytest/plugin.py: WPS201, WPS430, WPS437, WPS609
7374
returns/contrib/hypothesis/*.py: WPS437, WPS609
7475
# TODO: remove after mypy@0.800

tests/test_contrib/test_pytest/test_plugin_has_trace.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22

3+
from returns.contrib.pytest import ReturnsAsserts
34
from returns.io import IOFailure, IOSuccess
45
from returns.result import Failure, Success
56

@@ -20,7 +21,7 @@ def _create_container_function_intermediate(container_type, container_value):
2021
IOSuccess,
2122
IOFailure,
2223
])
23-
def test_has_trace1(container_type, returns):
24+
def test_has_trace1(container_type, returns: ReturnsAsserts):
2425
"""Test if our plugin will identify the container creation correctly."""
2526
with returns.has_trace(container_type, _create_container_function):
2627
_create_container_function(container_type, 1) # type: ignore
@@ -32,7 +33,7 @@ def test_has_trace1(container_type, returns):
3233
IOSuccess,
3334
IOFailure,
3435
])
35-
def test_has_trace2(container_type, returns):
36+
def test_has_trace2(container_type, returns: ReturnsAsserts):
3637
"""Test if our plugin will identify the container creation correctly."""
3738
with returns.has_trace(container_type, _create_container_function):
3839
_create_container_function_intermediate( # type: ignore
@@ -46,7 +47,7 @@ def test_has_trace2(container_type, returns):
4647
(IOSuccess, IOFailure),
4748
(IOFailure, IOSuccess),
4849
])
49-
def test_failed_has_trace1(desired_type, wrong_type, returns):
50+
def test_failed_has_trace1(desired_type, wrong_type, returns: ReturnsAsserts):
5051
"""Test if our plugin will identify the conainter was not created."""
5152
with pytest.raises(pytest.fail.Exception): # noqa: PT012
5253
with returns.has_trace(desired_type, _create_container_function):
@@ -59,7 +60,7 @@ def test_failed_has_trace1(desired_type, wrong_type, returns):
5960
(IOSuccess, IOFailure),
6061
(IOFailure, IOSuccess),
6162
])
62-
def test_failed_has_trace2(desired_type, wrong_type, returns):
63+
def test_failed_has_trace2(desired_type, wrong_type, returns: ReturnsAsserts):
6364
"""Test if our plugin will identify the conainter was not created."""
6465
with pytest.raises(pytest.fail.Exception): # noqa: PT012
6566
with returns.has_trace(desired_type, _create_container_function):

0 commit comments

Comments
 (0)