From c1e11fb8a03e343b8d9d83867b35cc63333768d8 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 6 Jan 2022 18:39:02 +0100 Subject: [PATCH 1/4] refactor: Moved test_hypothesis_integration to "hypothesis" subfolder and renamed it to "test_base". This allows us to add more tests for the Hypothesis integration while at the same time keep different tests tidy by using different files. Signed-off-by: Michael Seifert --- tests/{test_hypothesis_integration.py => hypothesis/test_base.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_hypothesis_integration.py => hypothesis/test_base.py} (100%) diff --git a/tests/test_hypothesis_integration.py b/tests/hypothesis/test_base.py similarity index 100% rename from tests/test_hypothesis_integration.py rename to tests/hypothesis/test_base.py From 12b6ccbbae84ca4d5a7889221961345a7448b4fb Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 6 Jan 2022 18:56:00 +0100 Subject: [PATCH 2/4] fix: Fixed double wrapping of inherited Hypothesis tests. Pytest-asyncio identifies Hypothesis test cases by their `is_hypothesis_test` flag. When setting up an async Hypothesis test pytest-asyncio replaces the function's `hypothesis.inner_test` attribute. The the top level function never changes. When a Hypothesis test case is defined in a base class and inherited by subclasses, the test is collected in each subclass. Since the top-level Hypothesis test never changes, its inner test will be wrapped multiple times. Double wrapping leads to execution errors caused by stale (closed) event loops in all test executions after the first. This change adds an `original_test_function` attribute to the async function wrapper, in order to keep track of the original Hypothesis test. When re-wrapping would occur in subclasses pytest-asyncio wraps the original test function rather than the wrapper function. Closes #231 Signed-off-by: Michael Seifert --- README.rst | 1 + pytest_asyncio/plugin.py | 2 +- tests/hypothesis/test_inherited_test.py | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/hypothesis/test_inherited_test.py diff --git a/README.rst b/README.rst index f2d64987..0faac438 100644 --- a/README.rst +++ b/README.rst @@ -175,6 +175,7 @@ Changelog ~~~~~~~~~~~~~~~~~~~ - `pytest-asyncio` no longer alters existing event loop policies. `#168 `_, `#188 `_ - Drop support for Python 3.6 +- Fixed an issue when pytest-asyncio was used in combination with `flaky` or inherited asynchronous Hypothesis tests. `#178 `_ `#231 `_ 0.16.0 (2021-10-16) ~~~~~~~~~~~~~~~~~~~ diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 461bbe5f..81ac2e3f 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -176,7 +176,7 @@ def wrap_in_sync(func, _loop): # if the function is already wrapped, we rewrap using the original one # not using __wrapped__ because the original function may already be # a wrapped one - if hasattr(func, '_raw_test_func'): + if hasattr(func, "_raw_test_func"): func = func._raw_test_func @functools.wraps(func) diff --git a/tests/hypothesis/test_inherited_test.py b/tests/hypothesis/test_inherited_test.py new file mode 100644 index 00000000..86e92efd --- /dev/null +++ b/tests/hypothesis/test_inherited_test.py @@ -0,0 +1,22 @@ +import hypothesis.strategies as st +from hypothesis import given +import pytest + + +class BaseClass: + @pytest.mark.asyncio + @given(value=st.integers()) + async def test_hypothesis(self, value: int) -> None: + assert True + + +class TestOne(BaseClass): + """During the first execution the Hypothesis test is wrapped in a synchronous function.""" + + pass + + +class TestTwo(BaseClass): + """Execute the test a second time to ensure that the test receives a fresh event loop.""" + + pass From 441205bc798eddafd881edba2d212082923cdbf5 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 6 Jan 2022 19:49:04 +0100 Subject: [PATCH 3/4] doc: Updated docstring of pytest_pyfunc_call. Signed-off-by: Michael Seifert --- pytest_asyncio/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 81ac2e3f..634d1fb7 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -153,8 +153,9 @@ async def setup(): @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_pyfunc_call(pyfuncitem): """ - Run asyncio marked test functions in an event loop instead of a normal - function call. + Pytest hook called before a test case is run. + + Wraps marked tests in a synchronous function where the wrapped test coroutine is executed in an event loop. """ if "asyncio" in pyfuncitem.keywords: if getattr(pyfuncitem.obj, "is_hypothesis_test", False): From dc74a2020588c501d91d000ad109e1eb828a6a0a Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 7 Jan 2022 08:38:03 +0100 Subject: [PATCH 4/4] doc: Mentioned additional test dependency on flaky in the changelog. This is an important information for downstream packagers. Signed-off-by: Michael Seifert --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 0faac438..4d264ce2 100644 --- a/README.rst +++ b/README.rst @@ -176,6 +176,7 @@ Changelog - `pytest-asyncio` no longer alters existing event loop policies. `#168 `_, `#188 `_ - Drop support for Python 3.6 - Fixed an issue when pytest-asyncio was used in combination with `flaky` or inherited asynchronous Hypothesis tests. `#178 `_ `#231 `_ +- Added `flaky `_ to test dependencies 0.16.0 (2021-10-16) ~~~~~~~~~~~~~~~~~~~