Skip to content

container

container

Core Container functionality for managing OCI images.

Container

Container(*, image: str, user: int | None = None, group: int | None = None, mounts: list[tuple[PathOrStr, PathOrStr]] | None = None, cwd: PathOrStr | None = '/tmp', engine: ContainerEngine = 'docker', name: str | None = None, stdout: T.IO[bytes] | T.IO[str] | None = None, output_path: Path | None = None, logs_path: PathOrStr | None = None, additional_options: list[str] | None = None)

An object that represents a running OCI container.

Source code in src/mapyde/container.py
def __init__(
    self,
    *,
    image: str,
    user: int | None = None,
    group: int | None = None,
    mounts: list[tuple[PathOrStr, PathOrStr]] | None = None,
    cwd: PathOrStr | None = "/tmp",
    engine: ContainerEngine = "docker",
    name: str | None = None,
    stdout: T.IO[bytes] | T.IO[str] | None = None,
    output_path: Path | None = None,
    logs_path: PathOrStr | None = None,
    additional_options: list[str] | None = None,
):
    if not image:
        msg = "Must specify an image to run."
        raise ValueError(msg)

    try:
        subprocess.run(["bash", "-c", f"hash {engine}"], check=True)
    except subprocess.CalledProcessError as err:
        msg = f"{engine} does not exist on your system."
        raise OSError(msg) from err

    self.image = image
    self.user = user or os.geteuid()
    self.group = group or os.getegid()
    self.mounts = mounts or []
    self.cwd = cwd
    self.engine = engine
    self.name = name
    self.stdin_config = subprocess.PIPE
    self.stdout_config = stdout or subprocess.PIPE
    self.stderr_config = subprocess.STDOUT
    self.output_path = (output_path or Path()).resolve()
    self.logs_path = logs_path
    self.additional_options = additional_options or []

    self.output_path.mkdir(parents=True, exist_ok=True)
    for host, container in self.mounts:
        if not Path(container).is_absolute():
            msg = f"The mount {host}:{container} does not point to an absolute path in the container."
            raise ValueError(msg)

call

call(args: bytes, cwd: PathOrStr | None = None, env: dict[str, str] | None = None) -> tuple[bytes, bytes]

Execute the provided command in the container. A smarter version of Container.process.communicate(args).

Optionally change the current working directory (cwd) and set some environment variables.

Source code in src/mapyde/container.py
def call(
    self,
    args: bytes,
    cwd: PathOrStr | None = None,
    env: dict[str, str] | None = None,
) -> tuple[bytes, bytes]:
    """
    Execute the provided command in the container. A smarter version of Container.process.communicate(args).

    Optionally change the current working directory (cwd) and set some environment variables.
    """
    if cwd is None:
        cwd = self.cwd

    chdir = ""
    if cwd:
        # singularity/apptainer mount host $TMPDIR into /tmp which might be
        # unexpectedly full of files so make an empty temporary directory
        chdir = "cd $(mktemp -d)" if cwd == "/tmp" else f"cd {cwd}"

    env_assignments = (
        " ".join(f"{shlex.quote(k)}={shlex.quote(v)}" for k, v in env.items())
        if env is not None
        else ""
    )

    command = bytes(
        f"""{chdir}
            env {env_assignments} {args.decode()}
            """,
        "utf-8",
    )

    return self.process.communicate(command)

Last update: June 15, 2023