diff --git a/helpers/alt_pytest_asyncio_test_driver/contextvars_for_test.py b/helpers/alt_pytest_asyncio_test_driver/contextvars_for_test.py index 689e9df..9096bb5 100644 --- a/helpers/alt_pytest_asyncio_test_driver/contextvars_for_test.py +++ b/helpers/alt_pytest_asyncio_test_driver/contextvars_for_test.py @@ -1,22 +1,33 @@ -import string +from collections.abc import Iterable from contextvars import ContextVar -allvars = {} +allvars: dict[str, ContextVar[str]] = {} class Empty: pass -def assertVarsEmpty(excluding=None): +def assertVarsEmpty(excluding: Iterable[str] | None = None) -> None: for letter, var in allvars.items(): if not excluding or letter not in excluding: assert var.get(Empty) is Empty -for letter in string.ascii_letters: - var = ContextVar(letter) - locals()[letter] = var - allvars[letter] = var +a: ContextVar[str] = ContextVar("a") +allvars["a"] = a -__all__ = ["allvars", "Empty", "assertVarsEmpty", *allvars] +b: ContextVar[str] = ContextVar("b") +allvars["b"] = b + +c: ContextVar[str] = ContextVar("c") +allvars["c"] = c + +d: ContextVar[str] = ContextVar("d") +allvars["d"] = d + +e: ContextVar[str] = ContextVar("e") +allvars["e"] = e + +f: ContextVar[str] = ContextVar("f") +allvars["f"] = f diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/expected b/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/expected index df879ff..6095761 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/expected +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/expected @@ -1,26 +1,26 @@ =======* ERRORS =====* _______* ERROR at setup of test_fails_on_fixture_returns _____* -*test_fails.py:14: in fixture_returns +*test_fails.py:17: in fixture_returns await one*() -*test_fails.py:5: in one - return await two*() -*test_fails.py:9: in two +*test_fails.py:8: in one + await two*() +*test_fails.py:12: in two raise ValueError*("WAT") E ValueError: WAT _______* ERROR at setup of test_fails_on_fixture_yields _____* -*test_fails.py:19: in fixture_yields +*test_fails.py:22: in fixture_yields yield await one*() -*test_fails.py:5: in one - return await two*() -*test_fails.py:9: in two +*test_fails.py:8: in one + await two*() +*test_fails.py:12: in two raise ValueError*("WAT") E ValueError: WAT _______* ERROR at teardown of test_fails_on_fixture_fails_in_finally _____* -*test_fails.py:27: in fixture_fails_in_finally +*test_fails.py:30: in fixture_fails_in_finally await one*() -*test_fails.py:5: in one - return await two*() -*test_fails.py:9: in two +*test_fails.py:8: in one + await two*() +*test_fails.py:12: in two raise ValueError*("WAT") E ValueError: WAT =======* 1 passed, 3 error* in * ===* diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/test_fails.py b/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/test_fails.py index f156d25..8018d0d 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/test_fails.py +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_fixture_failures/test_fails.py @@ -1,39 +1,42 @@ +from collections.abc import AsyncGenerator +from typing import NoReturn + import pytest -async def one(): - return await two() +async def one() -> NoReturn: + await two() -async def two(): +async def two() -> NoReturn: raise ValueError("WAT") @pytest.fixture() -async def fixture_returns(): +async def fixture_returns() -> NoReturn: await one() @pytest.fixture() -async def fixture_yields(): +async def fixture_yields() -> AsyncGenerator[None]: yield await one() @pytest.fixture() -async def fixture_fails_in_finally(): +async def fixture_fails_in_finally() -> AsyncGenerator[int]: try: yield 1 finally: await one() -def test_fails_on_fixture_returns(fixture_returns): +def test_fails_on_fixture_returns(fixture_returns: int) -> None: pass -def test_fails_on_fixture_yields(fixture_yields): +def test_fails_on_fixture_yields(fixture_yields: int) -> None: pass -def test_fails_on_fixture_fails_in_finally(fixture_fails_in_finally): +def test_fails_on_fixture_fails_in_finally(fixture_fails_in_finally: int) -> None: pass diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/conftest.py b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/conftest.py index 7ad3f48..2128cd0 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/conftest.py +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/conftest.py @@ -1,25 +1,26 @@ import asyncio +from collections.abc import AsyncGenerator import pytest @pytest.fixture(scope="session") @pytest.mark.async_timeout(0.01) -async def fixture_timeout_in_setup_session(): +async def fixture_timeout_in_setup_session() -> AsyncGenerator[int]: await asyncio.sleep(1) yield 1 @pytest.fixture(scope="session") @pytest.mark.async_timeout(0.01) -async def fixture_timeout_session(): +async def fixture_timeout_session() -> int: await asyncio.sleep(1) return 1 @pytest.fixture(scope="session", autouse=True) @pytest.mark.async_timeout(0.01) -async def fixture_timeout_in_teardown_session(): +async def fixture_timeout_in_teardown_session() -> AsyncGenerator[int]: try: yield 1 finally: diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/expected b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/expected index ad6aefa..b76a6ed 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/expected +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/expected @@ -2,84 +2,84 @@ _______* ERROR at teardown of test_one ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:6 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:7 _______* ERROR at setup of test_two ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:15 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:16 _______* ERROR at setup of test_three ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:22 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:23 _______* ERROR at setup of test_four ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:29 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:30 _______* ERROR at setup of test_five ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:45 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails.py:46 _______* ERROR at setup of test_six ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:6 +E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:7 _______* ERROR at setup of test_seven ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:13 +E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:14 _______* ERROR at teardown of test_seven ____* */async_converters.py:* in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */test_fails.py:36 +E AssertionError: Took too long to complete: */test_fails.py:37 _______* ERROR at setup of TestAClass.test_2one ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails_method_fixtures.py:7 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails_method_fixtures.py:8 _______* ERROR at setup of TestAClass.test_2two ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails_method_fixtures.py:13 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails_method_fixtures.py:14 _______* ERROR at teardown of TestAClass.test_2three ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_fails_method_fixtures.py:19 +E AssertionError: Took too long to complete: */example_timeouts*/test_fails_method_fixtures.py:20 _______* ERROR at teardown of test_one ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:8 +E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:9 _______* ERROR at setup of test_two ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:16 +E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:17 _______* ERROR at setup of test_three ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:22 +E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:23 _______* ERROR at setup of test_four ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:28 +E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:29 _______* ERROR at setup of test_five ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:42 +E AssertionError: Took too long to complete: */example_timeouts*/test_surrounding_pytestmark.py:43 _______* ERROR at setup of test_six ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:6 +E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:7 _______* ERROR at setup of test_seven ____* */async_converters.py:*: in raise_error assert False, f"Took too long to complete: {fle}:{lineno}" -E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:13 +E AssertionError: Took too long to complete: */example_timeouts*/conftest.py:14 _______* ERROR at teardown of test_seven ____* + Exception Group Traceback (most recent call last): |*ExceptionGroup: errors during test teardown (2 sub-exceptions) +-+---------------- 1 ---------------- | Traceback (most recent call last): - | AssertionError: Took too long to complete: */conftest.py:20 + | AssertionError: Took too long to complete: */conftest.py:21 +---------------- 2 ---------------- | Traceback (most recent call last): - | AssertionError: Took too long to complete: */test_surrounding_pytestmark.py:34 + | AssertionError: Took too long to complete: */test_surrounding_pytestmark.py:35 +------------------------------------ =======* FAILURES ====* _______* test_takes_closest_pytestmark ____* diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails.py b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails.py index 4495166..6d80194 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails.py +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails.py @@ -1,11 +1,12 @@ import asyncio +from collections.abc import AsyncGenerator import pytest @pytest.fixture() @pytest.mark.async_timeout(0.01) -async def fixture_timeout_in_finally(): +async def fixture_timeout_in_finally() -> AsyncGenerator[int]: try: yield 1 finally: @@ -14,28 +15,28 @@ async def fixture_timeout_in_finally(): @pytest.fixture() @pytest.mark.async_timeout(0.01) -async def fixture_timeout_in_setup(): +async def fixture_timeout_in_setup() -> AsyncGenerator[int]: await asyncio.sleep(1) yield 1 @pytest.fixture() @pytest.mark.async_timeout(0.01) -async def fixture_timeout(): +async def fixture_timeout() -> int: await asyncio.sleep(1) return 1 @pytest.fixture(scope="module") @pytest.mark.async_timeout(0.01) -async def fixture_timeout_in_setup_module(): +async def fixture_timeout_in_setup_module() -> AsyncGenerator[int]: await asyncio.sleep(1) yield 1 @pytest.fixture(scope="module", autouse=True) @pytest.mark.async_timeout(0.01) -async def fixture_timeout_in_teardown_module(): +async def fixture_timeout_in_teardown_module() -> AsyncGenerator[int]: try: yield 1 finally: @@ -44,34 +45,34 @@ async def fixture_timeout_in_teardown_module(): @pytest.fixture(scope="module") @pytest.mark.async_timeout(0.01) -async def fixture_timeout_module(): +async def fixture_timeout_module() -> int: await asyncio.sleep(1) return 1 -def test_one(fixture_timeout_in_finally): +def test_one(fixture_timeout_in_finally: int) -> None: pass -def test_two(fixture_timeout_in_setup): +def test_two(fixture_timeout_in_setup: int) -> None: pass -def test_three(fixture_timeout): +def test_three(fixture_timeout: int) -> None: pass -def test_four(fixture_timeout_in_setup_module): +def test_four(fixture_timeout_in_setup_module: int) -> None: pass -def test_five(fixture_timeout_module): +def test_five(fixture_timeout_module: int) -> None: pass -def test_six(fixture_timeout_in_setup_session): +def test_six(fixture_timeout_in_setup_session: int) -> None: pass -def test_seven(fixture_timeout_session): +def test_seven(fixture_timeout_session: int) -> None: pass diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_method_fixtures.py b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_method_fixtures.py index 7f3fbbc..d87f5b4 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_method_fixtures.py +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_method_fixtures.py @@ -1,4 +1,5 @@ import asyncio +from collections.abc import AsyncGenerator import pytest @@ -6,32 +7,32 @@ class TestAClass: @pytest.fixture() @pytest.mark.async_timeout(0.01) - async def fixture_timeout(self): + async def fixture_timeout(self) -> int: await asyncio.sleep(1) return 1 @pytest.fixture() @pytest.mark.async_timeout(0.01) - async def fixture_timeout_in_setup(self): + async def fixture_timeout_in_setup(self) -> AsyncGenerator[int]: await asyncio.sleep(1) yield 1 @pytest.fixture() @pytest.mark.async_timeout(0.01) - async def fixture_timeout_in_teardown(self): + async def fixture_timeout_in_teardown(self) -> AsyncGenerator[int]: try: yield 1 finally: await asyncio.sleep(1) - def test_2one(self, fixture_timeout): + def test_2one(self, fixture_timeout: int) -> None: pass - def test_2two(self, fixture_timeout_in_setup): + def test_2two(self, fixture_timeout_in_setup: int) -> None: pass - def test_2three(self, fixture_timeout_in_teardown): + def test_2three(self, fixture_timeout_in_teardown: int) -> None: pass - def test_2four(self, fixture_timeout_in_teardown_session): + def test_2four(self, fixture_timeout_in_teardown_session: int) -> None: pass diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_tests.py b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_tests.py index 655f7ae..a8ca68e 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_tests.py +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_fails_tests.py @@ -5,15 +5,15 @@ pytestmark = pytest.mark.async_timeout(0.01) -async def test_takes_closest_pytestmark(): +async def test_takes_closest_pytestmark() -> None: await asyncio.sleep(0.03) @pytest.mark.async_timeout(0.04) -async def test_takes_pytestmark_on_function(): +async def test_takes_pytestmark_on_function() -> None: await asyncio.sleep(0.03) @pytest.mark.async_timeout(0.02) -async def test_takes_pytestmark_on_function2(): +async def test_takes_pytestmark_on_function2() -> None: await asyncio.sleep(0.03) diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_surrounding_pytestmark.py b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_surrounding_pytestmark.py index c56e7fa..1f0a8af 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_surrounding_pytestmark.py +++ b/helpers/alt_pytest_asyncio_test_driver/examples/example_timeouts/test_surrounding_pytestmark.py @@ -1,4 +1,5 @@ import asyncio +from collections.abc import AsyncGenerator import pytest @@ -6,7 +7,7 @@ @pytest.fixture() -async def fixture_timeout_in_finally(): +async def fixture_timeout_in_finally() -> AsyncGenerator[int]: try: yield 1 finally: @@ -14,25 +15,25 @@ async def fixture_timeout_in_finally(): @pytest.fixture() -async def fixture_timeout_in_setup(): +async def fixture_timeout_in_setup() -> AsyncGenerator[int]: await asyncio.sleep(1) yield 1 @pytest.fixture() -async def fixture_timeout(): +async def fixture_timeout() -> int: await asyncio.sleep(1) return 1 @pytest.fixture(scope="module") -async def fixture_timeout_in_setup_module(): +async def fixture_timeout_in_setup_module() -> AsyncGenerator[int]: await asyncio.sleep(1) yield 1 @pytest.fixture(scope="module", autouse=True) -async def fixture_timeout_in_teardown_module(): +async def fixture_timeout_in_teardown_module() -> AsyncGenerator[int]: try: yield 1 finally: @@ -40,34 +41,34 @@ async def fixture_timeout_in_teardown_module(): @pytest.fixture(scope="module") -async def fixture_timeout_module(): +async def fixture_timeout_module() -> int: await asyncio.sleep(1) return 1 -def test_one(fixture_timeout_in_finally): +def test_one(fixture_timeout_in_finally: int) -> None: pass -def test_two(fixture_timeout_in_setup): +def test_two(fixture_timeout_in_setup: int) -> None: pass -def test_three(fixture_timeout): +def test_three(fixture_timeout: int) -> None: pass -def test_four(fixture_timeout_in_setup_module): +def test_four(fixture_timeout_in_setup_module: int) -> None: pass -def test_five(fixture_timeout_module): +def test_five(fixture_timeout_module: int) -> None: pass -def test_six(fixture_timeout_in_setup_session): +def test_six(fixture_timeout_in_setup_session: int) -> None: pass -def test_seven(fixture_timeout_session): +def test_seven(fixture_timeout_session: int) -> None: pass diff --git a/helpers/alt_pytest_asyncio_test_driver/examples/interrupt_test/tests/test_example.py b/helpers/alt_pytest_asyncio_test_driver/examples/interrupt_test/tests/test_example.py index d9b57b0..2277c4b 100644 --- a/helpers/alt_pytest_asyncio_test_driver/examples/interrupt_test/tests/test_example.py +++ b/helpers/alt_pytest_asyncio_test_driver/examples/interrupt_test/tests/test_example.py @@ -1,31 +1,32 @@ import asyncio +from collections.abc import AsyncGenerator import pytest @pytest.fixture() -async def thing(): +async def thing() -> AsyncGenerator[int]: try: yield 1 finally: print("THING FINALLY") -async def test_aa_has_a_passing_test(): +async def test_aa_has_a_passing_test() -> None: pass -async def test_bb_shows_failed_tests(): +async def test_bb_shows_failed_tests() -> None: assert False, "NOOOOO" @pytest.mark.async_timeout(1) -async def test_cc_shows_timedout_tests(): +async def test_cc_shows_timedout_tests() -> None: await asyncio.sleep(3) -async def test_dd_executes_finally_on_interrupt(pytestconfig, thing): - async def wat(): +async def test_dd_executes_finally_on_interrupt(pytestconfig: pytest.Config, thing: int) -> None: + async def wat() -> None: while True: await asyncio.sleep(0.1) @@ -37,5 +38,5 @@ async def wat(): print("TEST FINALLY") -async def test_ee_does_not_try_to_make_a_test_after_loop_is_closed(): +async def test_ee_does_not_try_to_make_a_test_after_loop_is_closed() -> None: await asyncio.get_event_loop().create_future() diff --git a/helpers/alt_pytest_asyncio_test_driver/py.typed b/helpers/alt_pytest_asyncio_test_driver/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/mypy.ini b/mypy.ini index 7f33228..d03b94b 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,6 +7,7 @@ exclude = (?x)( | __pycache__ | ^tools/.bootstrap-venv | ^alt_pytest_asyncio - | ^tests - | ^helpers ) + +[mypy-tests.test_override_loop] +disable_error_code = no-untyped-call diff --git a/tests/conftest.py b/tests/conftest.py index 49e7f40..c6481d5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1 @@ -import pytest - pytest_plugins = ["pytester"] - - -@pytest.fixture(scope="session", autouse=True) -def change_pytester(pytestconfig: pytest.Config): - pytestconfig.option.runpytest = "subprocess" diff --git a/tests/test_contextvars/conftest.py b/tests/test_contextvars/conftest.py index 023db13..bd50cc4 100644 --- a/tests/test_contextvars/conftest.py +++ b/tests/test_contextvars/conftest.py @@ -1,25 +1,27 @@ +from collections.abc import AsyncGenerator + import alt_pytest_asyncio_test_driver.contextvars_for_test as ctxvars import pytest @pytest.fixture(scope="session", autouse=True) -async def a_set_conftest_fixture_session_autouse(): +async def a_set_conftest_fixture_session_autouse() -> None: ctxvars.a.set("a_set_conftest_fixture_session_autouse") @pytest.fixture(scope="session", autouse=True) -async def b_set_conftest_cm_session_autouse(): +async def b_set_conftest_cm_session_autouse() -> AsyncGenerator[None]: ctxvars.b.set("b_set_conftest_cm_session_autouse") yield @pytest.fixture() -async def c_set_conftest_fixture_test(): +async def c_set_conftest_fixture_test() -> None: ctxvars.c.set("c_set_conftest_fixture_test") @pytest.fixture() -async def c_set_conftest_cm_test(): +async def c_set_conftest_cm_test() -> AsyncGenerator[None]: token = ctxvars.c.set("c_set_conftest_cm_test") try: yield diff --git a/tests/test_contextvars/test_contextvars.py b/tests/test_contextvars/test_contextvars.py index 6d213ff..b6f4c8a 100644 --- a/tests/test_contextvars/test_contextvars.py +++ b/tests/test_contextvars/test_contextvars.py @@ -1,9 +1,11 @@ +from collections.abc import AsyncGenerator, Iterator + import alt_pytest_asyncio_test_driver.contextvars_for_test as ctxvars import pytest @pytest.fixture(scope="module", autouse=True) -def d_set_conftest_cm_module_autouse(): +def d_set_conftest_cm_module_autouse() -> Iterator[None]: token = ctxvars.d.set("d_set_conftest_fixture_test") try: yield @@ -12,7 +14,7 @@ def d_set_conftest_cm_module_autouse(): @pytest.fixture(scope="module", autouse=True) -async def e_set_conftest_cm_test(): +async def e_set_conftest_cm_test() -> AsyncGenerator[None]: assert ctxvars.f.get(ctxvars.Empty) is ctxvars.Empty ctxvars.e.set("e_set_conftest_cm_module") yield @@ -20,14 +22,14 @@ async def e_set_conftest_cm_test(): @pytest.fixture(scope="module", autouse=True) -async def f_set_conftest_cm_module(e_set_conftest_cm_test): +async def f_set_conftest_cm_module(e_set_conftest_cm_test: None) -> AsyncGenerator[None]: assert ctxvars.e.get() == "e_set_conftest_cm_module" ctxvars.f.set("f_set_conftest_cm_module") yield @pytest.mark.order(1) -async def test_gets_session_modified_vars(): +async def test_gets_session_modified_vars() -> None: assert ctxvars.a.get() == "a_set_conftest_fixture_session_autouse" assert ctxvars.b.get() == "b_set_conftest_cm_session_autouse" assert ctxvars.d.get() == "d_set_conftest_fixture_test" @@ -37,7 +39,7 @@ async def test_gets_session_modified_vars(): @pytest.mark.order(2) -async def test_can_use_a_fixture_to_change_the_var(c_set_conftest_fixture_test): +async def test_can_use_a_fixture_to_change_the_var(c_set_conftest_fixture_test: None) -> None: assert ctxvars.a.get() == "a_set_conftest_fixture_session_autouse" assert ctxvars.b.get() == "b_set_conftest_cm_session_autouse" assert ctxvars.d.get() == "d_set_conftest_fixture_test" @@ -49,7 +51,7 @@ async def test_can_use_a_fixture_to_change_the_var(c_set_conftest_fixture_test): @pytest.mark.order(3) -async def test_does_not_reset_contextvars_for_you(): +async def test_does_not_reset_contextvars_for_you() -> None: """ It's too hard to know when a contextvar should be reset. It should be up to whatever sets the contextvar to know when it should be unset @@ -65,7 +67,7 @@ async def test_does_not_reset_contextvars_for_you(): @pytest.mark.order(4) -async def test_works_in_context_manager_fixtures(c_set_conftest_cm_test): +async def test_works_in_context_manager_fixtures(c_set_conftest_cm_test: None) -> None: """ It's too hard to know when a contextvar should be reset. It should be up to whatever sets the contextvar to know when it should be unset @@ -81,7 +83,7 @@ async def test_works_in_context_manager_fixtures(c_set_conftest_cm_test): @pytest.mark.order(5) -def test_resets_the_contextvar_successfully_when_cm_attempts_that(): +def test_resets_the_contextvar_successfully_when_cm_attempts_that() -> None: """ It's too hard to know when a contextvar should be reset. It should be up to whatever sets the contextvar to know when it should be unset diff --git a/tests/test_contextvars/test_contextvars2.py b/tests/test_contextvars/test_contextvars2.py index f81cc42..dd9723f 100644 --- a/tests/test_contextvars/test_contextvars2.py +++ b/tests/test_contextvars/test_contextvars2.py @@ -3,7 +3,7 @@ @pytest.mark.order(100) -def test_only_keeps_values_from_other_module_fixtures_if_they_have_not_been_cleaned_up(): +def test_only_keeps_values_from_other_module_fixtures_if_they_have_not_been_cleaned_up() -> None: assert ctxvars.a.get() == "a_set_conftest_fixture_session_autouse" assert ctxvars.b.get() == "b_set_conftest_cm_session_autouse" assert ctxvars.c.get() == "c_set_conftest_fixture_test" diff --git a/tests/test_examples.py b/tests/test_examples.py index 6f3c7fb..10f2f1d 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,20 +1,23 @@ import asyncio +import contextlib import importlib.resources import os +import pathlib import shutil import signal import socket import subprocess import sys import tempfile -from contextlib import contextmanager +from collections.abc import Iterator +from typing import Protocol import pytest from alt_pytest_asyncio_test_driver import available_examples -@contextmanager -def listening(): +@contextlib.contextmanager +def listening() -> Iterator[tuple[socket.socket, str]]: try: with tempfile.NamedTemporaryFile(delete=False) as fle: fle.close() @@ -31,7 +34,14 @@ def listening(): os.remove(fle.name) -def example_dir_factory(tmp_path_factory, name): +class Factory(Protocol): + @property + def expected(self) -> str: ... + + def mktemp(self, p: str, **kwargs: object) -> pathlib.Path: ... + + +def example_dir_factory(tmp_path_factory: pytest.TempPathFactory, name: str) -> Factory: examples = importlib.resources.files("alt_pytest_asyncio_test_driver") / "examples" / name assert examples.is_dir() @@ -39,14 +49,15 @@ def example_dir_factory(tmp_path_factory, name): directory = tmp_path_factory.mktemp(name) shutil.rmtree(directory) - shutil.copytree(examples, directory) + with importlib.resources.as_file(examples) as examples_path: + shutil.copytree(examples_path, directory) class Factory: @property - def expected(s): + def expected(self) -> str: return expected - def mktemp(s, p, **kwargs): + def mktemp(self, p: str, **kwargs: object) -> pathlib.Path: if p.startswith("tmp-"): res = tmp_path_factory.mktemp(p) else: @@ -58,14 +69,18 @@ def mktemp(s, p, **kwargs): @pytest.mark.parametrize("name", available_examples) -async def test_shows_correctly_for_failing_fixtures(name, request, tmp_path_factory, monkeypatch): - factory = example_dir_factory(tmp_path_factory, name) - testdir = pytest.Pytester(request, factory, monkeypatch) - expected = factory.expected - result = testdir.runpytest("--tb", "short") +async def test_shows_correctly_for_failing_fixtures(name: str, pytester: pytest.Pytester) -> None: + examples = importlib.resources.files("alt_pytest_asyncio_test_driver") / "examples" / name + assert examples.is_dir() + expected = (examples / "expected").read_text() + + with importlib.resources.as_file(examples) as examples_path: + shutil.copytree(examples_path, pytester.path / name) + + result = pytester.runpytest_subprocess("--tb", "short") assert not result.errlines - lines = 0 + lines: int | list[str] = 0 for line in result.outlines: if line.startswith("=") and isinstance(lines, int): if lines < 1: @@ -76,32 +91,36 @@ async def test_shows_correctly_for_failing_fixtures(name, request, tmp_path_fact if isinstance(lines, list): lines.append(line) + assert isinstance(lines, list) + matcher = pytest.LineMatcher(lines) matcher.fnmatch_lines(expected.strip().split("\n")) @pytest.mark.async_timeout(7) @pytest.mark.skipif(os.name == "nt", reason="Can't use async subprocess on windows") -async def test_cleans_up_tests_properly_on_interrupt(): +async def test_cleans_up_tests_properly_on_interrupt(pytester: pytest.Pytester) -> None: examples = ( importlib.resources.files("alt_pytest_asyncio_test_driver") / "examples" / "interrupt_test" ) assert examples.is_dir() expected = (examples / "expected").read_text() - p = await asyncio.create_subprocess_exec( - sys.executable, - "-m", - "pytest", - cwd=examples, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) + with importlib.resources.as_file(examples) as directory: + p = await asyncio.create_subprocess_exec( + sys.executable, + "-m", + "pytest", + cwd=directory, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) await asyncio.sleep(4) p.send_signal(signal.SIGINT) await p.wait() + assert p.stdout is not None got = (await p.stdout.read()).decode().strip().split("\n") while got and not got[0].startswith("collected"): got.pop(0) diff --git a/tests/test_override_loop.py b/tests/test_override_loop.py index 053c5de..8229551 100644 --- a/tests/test_override_loop.py +++ b/tests/test_override_loop.py @@ -1,28 +1,33 @@ import asyncio +from collections.abc import AsyncGenerator, Coroutine, Iterator import pytest from alt_pytest_asyncio.plugin import OverrideLoop -def get_event_loop(): +def get_event_loop() -> asyncio.AbstractEventLoop: return asyncio.get_event_loop_policy().get_event_loop() @pytest.fixture() -def fut(): +def fut() -> asyncio.Future[None]: return get_event_loop().create_future() -async def things(futs, loop_info, works=False): - found = [] +async def things( + futs: dict[str, asyncio.Future[None]], + loop_info: dict[str, asyncio.AbstractEventLoop], + works: bool = False, +) -> None: + found: list[None] = [] - async def setter(): + async def setter() -> None: await asyncio.sleep(0.05) futs["a"].set_result(None) futs["b"].set_result(None) - task = None + task: asyncio.Task[None] | None = None try: task = get_event_loop().create_task(setter()) @@ -31,20 +36,24 @@ async def setter(): loop_info["thingsa"] = get_event_loop() if works: - found.append(await futs["a"]) + await futs["a"] + found.append(None) else: try: - found.append(await futs["a"]) + await futs["a"] + found.append(None) except RuntimeError as error: assert "attached to a different loop" in str(error) - found.append(await futs["b"]) + await futs["b"] + found.append(None) finally: if not works: loop_info["thingsb"] = get_event_loop() - task.cancel() - await asyncio.wait([task]) + if task: + task.cancel() + await asyncio.wait([task]) if works: assert found == [None, None] @@ -52,10 +61,10 @@ async def setter(): assert found == [None] -def test_will_not_let_you_do_run_until_complete_outside_the_context_manager(): - info = {"coro": None} +def test_will_not_let_you_do_run_until_complete_outside_the_context_manager() -> None: + info: dict[str, Coroutine[None, None, None] | None] = {"coro": None} - async def blah(): + async def blah() -> None: pass try: @@ -70,7 +79,7 @@ async def blah(): get_event_loop().run_until_complete(info["coro"]) -def test_can_replace_the_loop(fut): +def test_can_replace_the_loop(fut: asyncio.Future[None]) -> None: info = {} futs = {"a": fut} @@ -96,13 +105,13 @@ def test_can_replace_the_loop(fut): @pytest.fixture() -def loop_info(): +def loop_info() -> dict[str, asyncio.AbstractEventLoop]: return {} class TestNoNewLoop: - def test_sets_None(self): - info = {"coro": None} + def test_sets_None(self) -> None: + info: dict[str, Coroutine[None, None, None] | None] = {"coro": None} original = get_event_loop() @@ -116,7 +125,7 @@ def test_sets_None(self): try: - async def blah(): + async def blah() -> None: pass coro = info["coro"] = blah() @@ -130,14 +139,15 @@ async def blah(): assert get_event_loop() is original assert not original.is_closed() - assert info["coro"] is not None - get_event_loop().run_until_complete(info["coro"]) + made_coro = info["coro"] + assert made_coro is not None + get_event_loop().run_until_complete(made_coro) -def test_can_shutdown_async_gens(): - info1 = [] - info2 = [] - info3 = [] +def test_can_shutdown_async_gens() -> None: + info1: list[int | str | tuple[str, object]] = [] + info2: list[int | str | tuple[str, object]] = [] + info3: list[int | str | tuple[str, object]] = [] try: original = asyncio.get_running_loop() @@ -145,7 +155,7 @@ def test_can_shutdown_async_gens(): original = asyncio.new_event_loop() asyncio.set_event_loop(original) - async def my_generator(info): + async def my_generator(info: list[int | str | tuple[str, object]]) -> AsyncGenerator[None]: try: info.append(1) yield @@ -161,7 +171,7 @@ async def my_generator(info): # Test that the outside loop isn't affected by the inside loop outside_gen = my_generator(info1) - async def outside1(): + async def outside1() -> None: await outside_gen.__anext__() await outside_gen.__anext__() @@ -178,13 +188,13 @@ async def outside1(): assert info2 == [] assert info3 == [] - async def doit(): + async def doit() -> None: ag2 = my_generator(info3) - assert set(asyncio.get_event_loop()._asyncgens) == set() + assert set(asyncio.get_event_loop()._asyncgens) == set() # type: ignore[attr-defined] await ag2.__anext__() - assert set(asyncio.get_event_loop()._asyncgens) == set([ag2]) + assert set(asyncio.get_event_loop()._asyncgens) == set([ag2]) # type: ignore[attr-defined] await ag.__anext__() - assert set(asyncio.get_event_loop()._asyncgens) == set([ag2, ag]) + assert set(asyncio.get_event_loop()._asyncgens) == set([ag2, ag]) # type: ignore[attr-defined] await ag.__anext__() assert info3 == [1] @@ -199,7 +209,7 @@ async def doit(): assert info2 == [1, 2, "cancelled", ("done", asyncio.CancelledError)] assert info1 == [1, 2] - async def outside2(): + async def outside2() -> None: try: await outside_gen.__anext__() except StopAsyncIteration: @@ -212,7 +222,9 @@ async def outside2(): class TestTestingAutoUse: @pytest.fixture(autouse=True) - def custom_loop(self, loop_info): + def custom_loop( + self, loop_info: dict[str, asyncio.AbstractEventLoop] + ) -> Iterator[asyncio.AbstractEventLoop]: assert not loop_info loop_info["original"] = get_event_loop() @@ -235,7 +247,9 @@ def custom_loop(self, loop_info): ] self.assertLoopInfo(loop_info, closed=True) - def assertLoopInfo(self, loop_info, *, closed): + def assertLoopInfo( + self, loop_info: dict[str, asyncio.AbstractEventLoop], *, closed: bool + ) -> None: original = loop_info["original"] rest = [l for k, l in loop_info.items() if k != "original"] @@ -249,15 +263,19 @@ def assertLoopInfo(self, loop_info, *, closed): assert len(set([*rest, original])) == 2 @pytest.fixture(autouse=True) - async def fix1(self, custom_loop, loop_info): + async def fix1( + self, + custom_loop: asyncio.AbstractEventLoop, + loop_info: dict[str, asyncio.AbstractEventLoop], + ) -> None: loop_info["fix1"] = get_event_loop() @pytest.fixture() - async def fix2(self, loop_info): + async def fix2(self, loop_info: dict[str, asyncio.AbstractEventLoop]) -> None: loop_info["fix2"] = get_event_loop() @pytest.fixture() - async def fix3(self, loop_info): + async def fix3(self, loop_info: dict[str, asyncio.AbstractEventLoop]) -> AsyncGenerator[None]: loop_info["fix3a"] = get_event_loop() try: yield @@ -266,7 +284,7 @@ async def fix3(self, loop_info): loop_info["fix3c"] = get_event_loop() @pytest.fixture() - def fix4(self, loop_info): + def fix4(self, loop_info: dict[str, asyncio.AbstractEventLoop]) -> Iterator[None]: loop_info["fix4a"] = get_event_loop() try: yield @@ -275,25 +293,48 @@ def fix4(self, loop_info): loop_info["fix4c"] = get_event_loop() @pytest.fixture() - def fix5(self, loop_info): + def fix5(self, loop_info: dict[str, asyncio.AbstractEventLoop]) -> None: loop_info["fix5a"] = get_event_loop() - def test_works(self, fix1, fix2, fix3, fix4, fix5, loop_info): + def test_works( + self, + fix1: None, + fix2: None, + fix3: None, + fix4: None, + fix5: None, + loop_info: dict[str, asyncio.AbstractEventLoop], + ) -> None: loop_info["test"] = get_event_loop() assert list(loop_info) == ["original", "fix1", "fix2", "fix3a", "fix4a", "fix5a", "test"] self.assertLoopInfo(loop_info, closed=False) def test_has_the_loop_on_custom_loop( - self, custom_loop, fix1, fix2, fix3, fix4, fix5, loop_info - ): + self, + custom_loop: asyncio.AbstractEventLoop, + fix1: None, + fix2: None, + fix3: None, + fix4: None, + fix5: None, + loop_info: dict[str, asyncio.AbstractEventLoop], + ) -> None: loop_info["test"] = get_event_loop() assert list(loop_info) == ["original", "fix1", "fix2", "fix3a", "fix4a", "fix5a", "test"] - assert custom_loop.loop is get_event_loop() + assert custom_loop.loop is get_event_loop() # type: ignore[attr-defined] self.assertLoopInfo(loop_info, closed=False) def test_can_use_futures_from_fixtures( - self, custom_loop, fix1, fix2, fix3, fix4, fix5, loop_info, fut - ): + self, + custom_loop: asyncio.AbstractEventLoop, + fix1: None, + fix2: None, + fix3: None, + fix4: None, + fix5: None, + loop_info: dict[str, asyncio.AbstractEventLoop], + fut: asyncio.Future[None], + ) -> None: loop_info["test"] = get_event_loop() futs = {"a": fut, "b": get_event_loop().create_future()} diff --git a/tests/test_works.py b/tests/test_works.py index 5628d79..fc169db 100644 --- a/tests/test_works.py +++ b/tests/test_works.py @@ -1,28 +1,29 @@ import asyncio +from collections.abc import AsyncGenerator import pytest @pytest.fixture() -async def a_value(): +async def a_value() -> int: return 1 @pytest.fixture() -async def a_value_times_two(a_value): +async def a_value_times_two(a_value: int) -> int: """Make sure coroutine fixtures can receive other fixtures""" return a_value * 2 @pytest.fixture(scope="module") -async def a_list_fut(): - fut = asyncio.Future() +async def a_list_fut() -> asyncio.Future[list[int]]: + fut: asyncio.Future[list[int]] = asyncio.Future() fut.set_result([]) return fut @pytest.fixture(scope="module") -async def a_list(a_list_fut): +async def a_list(a_list_fut: asyncio.Future[list[int]]) -> AsyncGenerator[list[int]]: lst = await a_list_fut try: yield lst @@ -30,36 +31,36 @@ async def a_list(a_list_fut): assert set(lst) == set(range(4)) -async def test_works_for_async_functions(a_list): +async def test_works_for_async_functions(a_list: list[int]) -> None: await asyncio.sleep(0.01) a_list.append(0) assert True -def test_works_for_non_async_functions(a_list): +def test_works_for_non_async_functions(a_list: list[int]) -> None: a_list.append(1) assert True class TestAClass: - async def test_works_for_async_methods(self, a_list): + async def test_works_for_async_methods(self, a_list: list[int]) -> None: await asyncio.sleep(0.01) a_list.append(2) assert True - def test_works_for_non_async_methods(self, a_list): + def test_works_for_non_async_methods(self, a_list: list[int]) -> None: a_list.append(3) assert True - def test_uses_our_fixtures(self, a_value_times_two): + def test_uses_our_fixtures(self, a_value_times_two: int) -> None: assert a_value_times_two == 2 @pytest.mark.parametrize("func", [1]) -def test_allows_func_as_a_parametrize(func): +def test_allows_func_as_a_parametrize(func: int) -> None: assert func == 1 @pytest.mark.parametrize("func", [1]) -async def test_allows_func_as_a_parametrize_for_async_too(func): +async def test_allows_func_as_a_parametrize_for_async_too(func: int) -> None: assert func == 1