-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathniwrap.py
More file actions
152 lines (124 loc) · 4.76 KB
/
niwrap.py
File metadata and controls
152 lines (124 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""NiWrap helpers used throughout the library.
Provides utilities for setting up and tearing down runners. Allows us to use runner
of choice depending on what is available on the system.
"""
from __future__ import annotations
import logging
import shutil
import tempfile
from pathlib import Path
from typing import Literal, NamedTuple
import niwrap
from styxpodman import PodmanRunner
_LOG_LEVELS = [logging.WARNING, logging.INFO, logging.DEBUG]
RunnerType = Literal["local", "docker", "podman", "singularity"]
_RUNNER_EXECUTABLES: list[tuple[RunnerType, list[str]]] = [
("docker", ["docker"]),
("podman", ["podman"]),
("singularity", ["apptainer", "singularity"]),
]
class StyxContext(NamedTuple):
"""Styx execution context with logger and runner."""
logger: logging.Logger
runner: niwrap.Runner
verbose: bool
def resolve_runner(
runner: RunnerType | Literal["auto"] = "auto",
) -> tuple[RunnerType, str]:
"""Resolve runner selection, auto-detecting if needed.
When runner is "auto", checks for available container runtimes on PATH
in order of preference: docker > podman > apptainer/singularity > local.
Args:
runner: Runner type or "auto" for auto-detection.
Returns:
Tuple of (runner_type, executable_name).
"""
if runner != "auto":
return runner, runner
for runner_type, executables in _RUNNER_EXECUTABLES:
for exe in executables:
if shutil.which(exe):
return runner_type, exe
return "local", "local"
def setup_runner(
runner: RunnerType | Literal["auto"] = "auto",
tmp_dir: str | Path | None = None,
image_overrides: dict[str, str] | None = None,
verbose: int = 0,
**kwargs, # noqa: ANN003 (ignore annotation for kwargs)
) -> StyxContext:
"""Setup Styx with appropriate runner for NiWrap.
Args:
runner: Type of runner to use. "auto" detects the first available
container runtime, falling back to "local".
tmp_dir: Working directory to output to
image_overrides: Dictionary containing overrides for container tags.
verbose: Verbosity level (0=WARNING, 1=INFO, 2+=DEBUG)
**kwargs: Additional keyword arguments passed for runner setup.
Returns:
Configured logger instance and initialized runner
"""
runner_type, runner_exec = resolve_runner(runner)
match runner_type:
case "local":
niwrap.use_local()
case "docker":
niwrap.use_docker(
docker_executable=runner_exec,
image_overrides=image_overrides,
docker_user_id=0,
**kwargs,
)
case "podman":
niwrap.set_global_runner(
runner=PodmanRunner(
podman_executable=runner_exec,
image_overrides=image_overrides,
podman_user_id=0,
**kwargs,
)
)
case "singularity":
niwrap.use_singularity(
singularity_executable=runner_exec,
image_overrides=image_overrides,
**kwargs,
)
case _:
raise NotImplementedError(
f"Unknown runner selection '{runner}' - please select one of "
"'auto', 'local', 'docker', 'podman', or 'singularity'"
)
styx_runner = niwrap.get_global_runner()
if tmp_dir is not None:
Path(tmp_dir).mkdir(parents=True, exist_ok=True)
styx_runner.data_dir = Path(tempfile.mkdtemp(dir=tmp_dir))
log_level = min(verbose, len(_LOG_LEVELS) - 1)
# Expose styx execution logs at max verbosity (e.g. debug), warning otherwise
styx_logger = logging.getLogger(styx_runner.logger_name)
styx_logger.setLevel(
logging.DEBUG if verbose >= len(_LOG_LEVELS) - 1 else logging.WARNING
)
neuromaps_prime_logger = logging.getLogger("neuromaps_prime")
neuromaps_prime_logger.setLevel(_LOG_LEVELS[log_level])
if not neuromaps_prime_logger.handlers:
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(name)s - %(message)s"))
neuromaps_prime_logger.addHandler(handler)
return StyxContext(
logger=neuromaps_prime_logger, runner=styx_runner, verbose=verbose > 0
)
def generate_exec_folder(suffix: str = "python") -> Path:
"""Generate an execution folder following Styx hash pattern.
Args:
suffix: Task to append to suffix of folder (default: 'python')
Returns:
Path to created execution folder
"""
runner = niwrap.get_global_runner()
dir_path = (
Path(runner.data_dir) / f"{runner.uid}_{runner.execution_counter}_{suffix}"
)
dir_path.mkdir(parents=True)
runner.execution_counter += 1
return dir_path