Skip to content
This repository was archived by the owner on Apr 14, 2026. It is now read-only.

Commit cd81ce9

Browse files
committed
Updates for PodmanRunner
1 parent 169821e commit cd81ce9

1 file changed

Lines changed: 27 additions & 78 deletions

File tree

src/styxpodman/__init__.py

Lines changed: 27 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,20 @@
2020
StyxRuntimeError,
2121
)
2222

23+
if os.name == "posix":
24+
_HOST_UID: int | None = os.getuid()
25+
else:
26+
_HOST_UID = None
2327

24-
def _podman_mount(host_path: str, container_path: str, readonly: bool) -> str:
25-
"""Construct Podman mount argument.
26-
27-
Args:
28-
host_path: Path on the host filesystem to mount.
29-
container_path: Path inside the container where the host path will be mounted.
30-
readonly: If True, mount as read-only; otherwise mount as read-write.
3128

32-
Returns:
33-
Formatted mount string in the format "host:container[:ro]".
34-
35-
Raises:
36-
ValueError: If host_path or container_path contains illegal characters
37-
(comma, backslash, or colon).
38-
"""
39-
# Check for illegal characters
40-
charset = set(host_path + container_path)
41-
if any(c in charset for c in r",\\:"):
42-
raise ValueError("Illegal characters in path")
43-
return f"{host_path}:{container_path}{':ro' if readonly else ''}"
29+
def _podman_mount(host_path: str, container_path: str, readonly: bool) -> str:
30+
"""Construct Podman mount argument."""
31+
host_path = host_path.replace('"', r"\"")
32+
container_path = container_path.replace('"', r"\"")
33+
host_path = host_path.replace("\\", "\\\\")
34+
container_path = container_path.replace("\\", "\\\\")
35+
readonly_str = ",readonly" if readonly else ""
36+
return f"type=bind,source={host_path},target={container_path}{readonly_str}"
4437

4538

4639
class StyxPodmanError(StyxRuntimeError):
@@ -55,14 +48,7 @@ def __init__(
5548
command_args: list[str] | None = None,
5649
podman_args: list[str] | None = None,
5750
) -> None:
58-
"""Create StyxPodmanError.
59-
60-
Args:
61-
return_code: The exit code returned by the failed process.
62-
command_args: The command arguments that were executed inside the container.
63-
podman_args: The Podman-specific arguments used to run the
64-
container.
65-
"""
51+
"""Create StyxPodmanError."""
6652
super().__init__(
6753
return_code=return_code,
6854
command_args=command_args,
@@ -87,19 +73,10 @@ def __init__(
8773
container_tag: str,
8874
podman_executable: str,
8975
podman_extra_args: list[str],
76+
podman_user_id: int | None,
9077
environ: dict[str, str],
9178
) -> None:
92-
"""Create PodmanExecution.
93-
94-
Args:
95-
logger: Logger instance for execution logging.
96-
output_dir: Directory where output files will be written.
97-
metadata: Metadata about the command being executed.
98-
container_tag: Podman container image tag (e.g., "docker://ubuntu:20.04").
99-
podman_executable: Path to the podman executable.
100-
podman_extra_args: Additional arguments to pass to podman.
101-
environ: Environment variables to set in the container.
102-
"""
79+
"""Create PodmanExecution."""
10380
self.logger: logging.Logger = logger
10481
self.input_mounts: list[tuple[pl.Path, str, bool]] = []
10582
self.input_file_next_id = 0
@@ -108,6 +85,7 @@ def __init__(
10885
self.container_tag = container_tag
10986
self.podman_executable = podman_executable
11087
self.podman_extra_args = podman_extra_args
88+
self.podman_user_id = podman_user_id
11189
self.environ = environ
11290

11391
def input_file(
@@ -291,36 +269,20 @@ def __init__(
291269
image_overrides: dict[str, str] | None = None,
292270
podman_executable: str = "podman",
293271
podman_extra_args: list[str] | None = None,
272+
podman_user_id: int | None = None,
294273
data_dir: InputPathType | None = None,
295274
environ: dict[str, str] | None = None,
296275
) -> None:
297-
"""Create a new PodmanRunner.
298-
299-
Args:
300-
image_overrides: Dictionary mapping container image tags to alternative
301-
tags. Useful for using local or custom container images.
302-
podman_executable: Path to the podman executable. Defaults to
303-
"podman" (assumes it's in PATH).
304-
podman_extra_args: Additional arguments to pass to all
305-
podman commands.
306-
Defaults to ["--no-mount", "hostfs"] to prevent automatic host
307-
filesystem mounting.
308-
data_dir: Directory for temporary execution data and outputs.
309-
Defaults to "styx_tmp" in the current directory.
310-
environ: Environment variables to set in all container executions.
311-
312-
Raises:
313-
ValueError: If running on Windows (Podman is not supported on Windows).
314-
"""
315-
if os.name == "nt":
316-
raise ValueError("PodmanRunner is not supported on Windows")
317-
276+
"""Create a new PodmanRunner."""
318277
self.data_dir = pl.Path(data_dir or "styx_tmp")
319278
self.uid = os.urandom(8).hex()
320279
self.execution_counter = 0
321-
self.image_overrides = image_overrides or {}
322280
self.podman_executable = podman_executable
323-
self.podman_extra_args = podman_extra_args or ["--no-mount", "hostfs"]
281+
self.podman_extra_args = podman_extra_args or []
282+
self.podman_user_id = (
283+
podman_user_id if podman_user_id is not None else _HOST_UID
284+
)
285+
self.image_overrides = image_overrides or {}
324286
self.environ = environ or {}
325287

326288
# Configure logger
@@ -334,28 +296,14 @@ def __init__(
334296
self.logger.addHandler(ch)
335297

336298
def start_execution(self, metadata: Metadata) -> Execution:
337-
"""Start a new execution context.
338-
339-
Creates a new execution instance with a unique output directory
340-
and configured container image.
341-
342-
Args:
343-
metadata: Metadata describing the command to execute, including
344-
the container image tag.
345-
346-
Returns:
347-
Execution context for running commands in the container.
348-
349-
Raises:
350-
ValueError: If metadata doesn't specify a container image tag.
351-
"""
299+
"""Start a new execution context."""
352300
if metadata.container_image_tag is None:
353301
raise ValueError("No container image tag specified in metadata")
354302
container_tag = self.image_overrides.get(
355303
metadata.container_image_tag, metadata.container_image_tag
356304
)
357-
if not container_tag.startswith("docker://"):
358-
container_tag = f"docker://{container_tag}"
305+
if not container_tag.startswith("docker.io/"):
306+
container_tag = f"docker.io/{container_tag}"
359307

360308
self.execution_counter += 1
361309
return _PodmanExecution(
@@ -364,6 +312,7 @@ def start_execution(self, metadata: Metadata) -> Execution:
364312
/ f"{self.uid}_{self.execution_counter - 1}_{metadata.name}",
365313
metadata=metadata,
366314
container_tag=container_tag,
315+
podman_user_id=self.podman_user_id,
367316
podman_executable=self.podman_executable,
368317
podman_extra_args=self.podman_extra_args,
369318
environ=self.environ,

0 commit comments

Comments
 (0)