Skip to content

Commit c206a37

Browse files
committed
Scope styxcache wrap to pipeline_data fixture only
styxcache 0.1.0 doesn't preserve stdout on cache hits, which breaks tools like ants.print_header whose .output carries stdout lines. The previous wrap replaced the session's global runner, so integration tests running in the same xdist worker after full_pipeline inherited the CachingRunner and failed on cache hits (test_bold_masking_* and test_resample_bold_to_template). The only niwrap tool calls in full_pipeline live inside pipeline_data, so wrap there and restore the base runner in a finally block. The styxcache benefit for the long pipeline computation is preserved; everything else runs uncached.
1 parent d5edc34 commit c206a37

1 file changed

Lines changed: 46 additions & 31 deletions

File tree

tests/full_pipeline/conftest.py

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -86,24 +86,13 @@ def _niwrap_session_runner(
8686
runner.data_dir = data_dir
8787
logger = logging.getLogger(runner.logger_name)
8888
logger.setLevel(logging.DEBUG)
89-
90-
cache_dir = os.environ.get("RBC_STYXCACHE_DIR")
91-
if cache_dir and runner_type in {"docker", "podman"}:
92-
resolver = (
93-
docker_digest_resolver
94-
if runner_type == "docker"
95-
else podman_digest_resolver
96-
)
97-
wrapped = _AttrProxyCachingRunner(
98-
base=runner,
99-
cache_dir=cache_dir,
100-
policy=CachePolicy(image_digest=resolver),
101-
)
102-
niwrap.set_global_runner(wrapped)
103-
return wrapped
10489
return runner
10590

10691

92+
def _runner_kind(runner: niwrap.Runner) -> str:
93+
return type(runner).__name__.lower().replace("runner", "")
94+
95+
10796
@pytest.fixture(scope="session")
10897
def manifest() -> Generator[dict[str, object], None, None]:
10998
"""Shared manifest that tests populate; written to disk at session end."""
@@ -127,22 +116,48 @@ def pipeline_data(
127116
_niwrap_session_runner: niwrap.Runner,
128117
manifest: dict[str, object],
129118
) -> PipelineData:
130-
"""Run anatomical and functional preprocessing once for all e2e tests."""
131-
anat = anatomical_preprocess(test_subject.t1w)
132-
func_metadata = FunctionalMetadata.load(test_subject.bold)
133-
func = functional_preprocess(
134-
in_bold=test_subject.bold,
135-
t1w_brain=anat.brain,
136-
wm_bbr_mask=anat.wm_bbr_mask,
137-
brain_mask=anat.brain_mask,
138-
csf_mask=anat.csf_mask,
139-
wm_mask=anat.wm_mask,
140-
anat_to_template=anat.anat_to_template_xfm,
141-
metadata=func_metadata,
142-
)
143-
template_brain_mask = _warp_mask_to_template(
144-
anat.brain_mask, REGISTRATION_TEMPLATES.brain_2mm, anat.anat_to_template_xfm
145-
)
119+
"""Run anatomical and functional preprocessing once for all e2e tests.
120+
121+
The styxcache wrap is scoped to this fixture's computation only.
122+
Integration/unit tests that share the worker see the base runner so
123+
they don't risk cache hits on tool calls whose semantics exceed what
124+
styxcache persists (e.g. stdout-returning tools like ants.print_header).
125+
"""
126+
base = _niwrap_session_runner
127+
cache_dir = os.environ.get("RBC_STYXCACHE_DIR")
128+
kind = _runner_kind(base)
129+
wrapped: niwrap.Runner | None = None
130+
if cache_dir and kind in {"docker", "podman"}:
131+
resolver = (
132+
docker_digest_resolver if kind == "docker" else podman_digest_resolver
133+
)
134+
wrapped = _AttrProxyCachingRunner(
135+
base=base,
136+
cache_dir=cache_dir,
137+
policy=CachePolicy(image_digest=resolver),
138+
)
139+
niwrap.set_global_runner(wrapped)
140+
try:
141+
anat = anatomical_preprocess(test_subject.t1w)
142+
func_metadata = FunctionalMetadata.load(test_subject.bold)
143+
func = functional_preprocess(
144+
in_bold=test_subject.bold,
145+
t1w_brain=anat.brain,
146+
wm_bbr_mask=anat.wm_bbr_mask,
147+
brain_mask=anat.brain_mask,
148+
csf_mask=anat.csf_mask,
149+
wm_mask=anat.wm_mask,
150+
anat_to_template=anat.anat_to_template_xfm,
151+
metadata=func_metadata,
152+
)
153+
template_brain_mask = _warp_mask_to_template(
154+
anat.brain_mask,
155+
REGISTRATION_TEMPLATES.brain_2mm,
156+
anat.anat_to_template_xfm,
157+
)
158+
finally:
159+
if wrapped is not None:
160+
niwrap.set_global_runner(base)
146161
manifest["anat"] = _to_dict(anat)
147162
manifest["func"] = _to_dict(func)
148163
manifest["template_brain_mask"] = str(template_brain_mask)

0 commit comments

Comments
 (0)