From a617b52140a11d6e3cb98917aaaeac237133020d Mon Sep 17 00:00:00 2001 From: goodboy Date: Tue, 21 Apr 2026 21:24:51 -0400 Subject: [PATCH] Add `skipon_spawn_backend` pytest marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A reusable `@pytest.mark.skipon_spawn_backend( '' [, ...], reason='...')` marker for backend-specific known-hang / -borked cases — avoids scattering `@pytest.mark.skipif(lambda ...)` branches across tests that misbehave under a particular `--spawn-backend`. Deats, - `pytest_configure()` registers the marker via `addinivalue_line('markers', ...)`. - New `pytest_collection_modifyitems()` hook walks each collected item with `item.iter_markers( name='skipon_spawn_backend')`, checks whether the active `--spawn-backend` appears in `mark.args`, and if so injects a concrete `pytest.mark.skip( reason=...)`. `iter_markers()` makes the decorator work at function, class, or module (`pytestmark = [...]`) scope transparently. - First matching mark wins; default reason is `f'Borked on --spawn-backend={backend!r}'` if the caller doesn't supply one. Also, tighten type annotations on nearby `pytest` integration points — `pytest_configure`, `debug_mode`, `spawn_backend`, `tpt_protos`, `tpt_proto` — now taking typed `pytest.Config` / `pytest.FixtureRequest` params. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tractor/_testing/pytest.py | 61 +++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/tractor/_testing/pytest.py b/tractor/_testing/pytest.py index c33406ff..ef3cc9a7 100644 --- a/tractor/_testing/pytest.py +++ b/tractor/_testing/pytest.py @@ -224,8 +224,10 @@ def pytest_addoption( ) -def pytest_configure(config): - backend = config.option.spawn_backend +def pytest_configure( + config: pytest.Config, +): + backend: str = config.option.spawn_backend from tractor.spawn._spawn import try_set_start_method try: try_set_start_method(backend) @@ -241,10 +243,52 @@ def pytest_configure(config): 'markers', 'no_tpt(proto_key): test will (likely) not behave with tpt backend' ) + config.addinivalue_line( + 'markers', + 'skipon_spawn_backend(*start_methods, reason=None): ' + 'skip this test under any of the given `--spawn-backend` ' + 'values; useful for backend-specific known-hang / -borked ' + 'cases (e.g. the `subint` GIL-starvation class documented ' + 'in `ai/conc-anal/subint_sigint_starvation_issue.md`).' + ) + + +def pytest_collection_modifyitems( + config: pytest.Config, + items: list[pytest.Function], +): + ''' + Expand any `@pytest.mark.skipon_spawn_backend(''[, + ...], reason='...')` markers into concrete + `pytest.mark.skip(reason=...)` calls for tests whose + backend-arg set contains the active `--spawn-backend`. + + Uses `item.iter_markers(name=...)` which walks function + + class + module-level marks in the correct scope order (and + handles both the single-`MarkDecorator` and `list[Mark]` + forms of a module-level `pytestmark`) — so the same marker + works at any level a user puts it. + + ''' + backend: str = config.option.spawn_backend + default_reason: str = f'Borked on --spawn-backend={backend!r}' + for item in items: + for mark in item.iter_markers(name='skipon_spawn_backend'): + if backend in mark.args: + reason: str = mark.kwargs.get( + 'reason', + default_reason, + ) + item.add_marker(pytest.mark.skip(reason=reason)) + # first matching mark wins; no value in stacking + # multiple `skip`s on the same item. + break @pytest.fixture(scope='session') -def debug_mode(request) -> bool: +def debug_mode( + request: pytest.FixtureRequest, +) -> bool: ''' Flag state for whether `--tpdb` (for `tractor`-py-debugger) was passed to the test run. @@ -258,12 +302,16 @@ def debug_mode(request) -> bool: @pytest.fixture(scope='session') -def spawn_backend(request) -> str: +def spawn_backend( + request: pytest.FixtureRequest, +) -> str: return request.config.option.spawn_backend @pytest.fixture(scope='session') -def tpt_protos(request) -> list[str]: +def tpt_protos( + request: pytest.FixtureRequest, +) -> list[str]: # allow quoting on CLI proto_keys: list[str] = [ @@ -291,7 +339,7 @@ def tpt_protos(request) -> list[str]: autouse=True, ) def tpt_proto( - request, + request: pytest.FixtureRequest, tpt_protos: list[str], ) -> str: proto_key: str = tpt_protos[0] @@ -343,7 +391,6 @@ def pytest_generate_tests( metafunc: pytest.Metafunc, ): spawn_backend: str = metafunc.config.option.spawn_backend - if not spawn_backend: # XXX some weird windows bug with `pytest`? spawn_backend = 'trio'