Coverage for /opt/homebrew/lib/python3.11/site-packages/anyio/pytest_plugin.py: 38%
96 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
1from contextlib import contextmanager
2from inspect import isasyncgenfunction, iscoroutinefunction
3from typing import TYPE_CHECKING, Any, Dict, Generator, Optional, Tuple, cast
5import pytest
6import sniffio
7from _pytest.fixtures import FixtureRequest
9from ._core._eventloop import get_all_backends, get_asynclib
10from .abc import TestRunner
12if TYPE_CHECKING:
13 from _pytest.config import Config
15_current_runner: Optional[TestRunner] = None
18def extract_backend_and_options(backend: object) -> Tuple[str, Dict[str, Any]]:
19 if isinstance(backend, str):
20 return backend, {}
21 elif isinstance(backend, tuple) and len(backend) == 2:
22 if isinstance(backend[0], str) and isinstance(backend[1], dict):
23 return cast(Tuple[str, Dict[str, Any]], backend)
25 raise TypeError("anyio_backend must be either a string or tuple of (string, dict)")
28@contextmanager
29def get_runner(
30 backend_name: str, backend_options: Dict[str, Any]
31) -> Generator[TestRunner, object, None]:
32 global _current_runner
33 if _current_runner:
34 yield _current_runner
35 return
37 asynclib = get_asynclib(backend_name)
38 token = None
39 if sniffio.current_async_library_cvar.get(None) is None:
40 # Since we're in control of the event loop, we can cache the name of the async library
41 token = sniffio.current_async_library_cvar.set(backend_name)
43 try:
44 backend_options = backend_options or {}
45 with asynclib.TestRunner(**backend_options) as runner:
46 _current_runner = runner
47 yield runner
48 finally:
49 _current_runner = None
50 if token:
51 sniffio.current_async_library_cvar.reset(token)
54def pytest_configure(config: "Config") -> None:
55 config.addinivalue_line(
56 "markers",
57 "anyio: mark the (coroutine function) test to be run "
58 "asynchronously via anyio.",
59 )
62def pytest_fixture_setup(fixturedef: Any, request: FixtureRequest) -> None:
63 def wrapper(*args, anyio_backend, **kwargs): # type: ignore[no-untyped-def]
64 backend_name, backend_options = extract_backend_and_options(anyio_backend)
65 if has_backend_arg:
66 kwargs["anyio_backend"] = anyio_backend
68 with get_runner(backend_name, backend_options) as runner:
69 if isasyncgenfunction(func):
70 yield from runner.run_asyncgen_fixture(func, kwargs)
71 else:
72 yield runner.run_fixture(func, kwargs)
74 # Only apply this to coroutine functions and async generator functions in requests that involve
75 # the anyio_backend fixture
76 func = fixturedef.func
77 if isasyncgenfunction(func) or iscoroutinefunction(func):
78 if "anyio_backend" in request.fixturenames:
79 has_backend_arg = "anyio_backend" in fixturedef.argnames
80 fixturedef.func = wrapper
81 if not has_backend_arg:
82 fixturedef.argnames += ("anyio_backend",)
85@pytest.hookimpl(tryfirst=True)
86def pytest_pycollect_makeitem(collector: Any, name: Any, obj: Any) -> None:
87 if collector.istestfunction(obj, name):
88 inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj
89 if iscoroutinefunction(inner_func):
90 marker = collector.get_closest_marker("anyio")
91 own_markers = getattr(obj, "pytestmark", ())
92 if marker or any(marker.name == "anyio" for marker in own_markers):
93 pytest.mark.usefixtures("anyio_backend")(obj)
96@pytest.hookimpl(tryfirst=True)
97def pytest_pyfunc_call(pyfuncitem: Any) -> Optional[bool]:
98 def run_with_hypothesis(**kwargs: Any) -> None:
99 with get_runner(backend_name, backend_options) as runner:
100 runner.run_test(original_func, kwargs)
102 backend = pyfuncitem.funcargs.get("anyio_backend")
103 if backend:
104 backend_name, backend_options = extract_backend_and_options(backend)
106 if hasattr(pyfuncitem.obj, "hypothesis"):
107 # Wrap the inner test function unless it's already wrapped
108 original_func = pyfuncitem.obj.hypothesis.inner_test
109 if original_func.__qualname__ != run_with_hypothesis.__qualname__:
110 if iscoroutinefunction(original_func):
111 pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis
113 return None
115 if iscoroutinefunction(pyfuncitem.obj):
116 funcargs = pyfuncitem.funcargs
117 testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
118 with get_runner(backend_name, backend_options) as runner:
119 runner.run_test(pyfuncitem.obj, testargs)
121 return True
123 return None
126@pytest.fixture(params=get_all_backends())
127def anyio_backend(request: Any) -> Any:
128 return request.param
131@pytest.fixture
132def anyio_backend_name(anyio_backend: Any) -> str:
133 if isinstance(anyio_backend, str):
134 return anyio_backend
135 else:
136 return anyio_backend[0]
139@pytest.fixture
140def anyio_backend_options(anyio_backend: Any) -> Dict[str, Any]:
141 if isinstance(anyio_backend, str):
142 return {}
143 else:
144 return anyio_backend[1]