{"id":24995312,"url":"https://github.com/launchplatform/container-helpers","last_synced_at":"2026-04-02T18:25:58.042Z","repository":{"id":177718310,"uuid":"660799245","full_name":"LaunchPlatform/container-helpers","owner":"LaunchPlatform","description":"Helpers for running docker or podman containers easily in Python","archived":false,"fork":false,"pushed_at":"2026-03-29T20:28:23.000Z","size":57,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-29T22:22:54.184Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LaunchPlatform.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-06-30T22:02:02.000Z","updated_at":"2026-03-29T20:28:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"2ea9508f-8bf3-41de-963d-18c7c9370249","html_url":"https://github.com/LaunchPlatform/container-helpers","commit_stats":null,"previous_names":["launchplatform/container-helpers"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/LaunchPlatform/container-helpers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LaunchPlatform%2Fcontainer-helpers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LaunchPlatform%2Fcontainer-helpers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LaunchPlatform%2Fcontainer-helpers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LaunchPlatform%2Fcontainer-helpers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LaunchPlatform","download_url":"https://codeload.github.com/LaunchPlatform/container-helpers/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LaunchPlatform%2Fcontainer-helpers/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31312854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-04T15:35:04.338Z","updated_at":"2026-04-02T18:25:58.001Z","avatar_url":"https://github.com/LaunchPlatform.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# container-helpers\nHelpers for running docker or podman containers easily in Python\n\n## Install\n\n```bash\npip install container-helpers\n```\n\n## Usage\n\nBasic data types are provided in the `containers` package.\nYou can use them to construct a container and build the command line for running that container easily.\nFor example\n\n```python\nfrom containers import Container\nfrom containers import Podman\n\ncontainer = Container(\n    image=\"my-image\",\n    command=(\"git\", \"status\"),\n    environ=dict(ENV_VAR0=\"VAL0\", ENV_VAR1=\"VAL1\"),\n)\npodman = Podman()\ncommand = podman.build_command(container)\nprint(command)\n```\n\nThen you get the command for running the container like this as a tuple:\n\n```bash\npodman run --env ENV_VAR0=VAL0 --env ENV_VAR1=VAL1 my-image git status\n```\n\nWith the command, you can then use [subprocess](https://docs.python.org/3/library/subprocess.html) or other module to run it.\nA more complex container can be easily constructed with different data types, like one with image mounts or bind mounts.\n\n```python\nfrom containers import Container\nfrom containers import ImageMount\nfrom containers import BindMount\n\nContainer(\n    image=\"my-image\",\n    command=(\"git\", \"log\", \"-l3\"),\n    mounts=[\n        ImageMount(\n            target=\"/data\",\n            source=\"git-repo-data:write\",\n            read_write=True,\n        ),\n        BindMount(\n            target=\"/artifacts\",\n            source=\"/var/tmp/artifacts\",\n            readonly=False,\n            relabel=\"private\",\n            bind_propagation=\"rslave\"\n        ),\n    ],\n)\n# ...\n```\n\nFor more options, please see the `data_types.py` module.\nAt this moment, we only support the options needed for [LaunchPlatform](https://launchplatform.com) projects.\nAnd the only container cli we support for now is [podman](https://podman.io).\nWhile the most of the command generated from this package should also work for [docker](https://docker.com), but we never really tested it.\nPlease feel free to submit PRs for extending the package.\n\n### Run the container with ContainersService\n\nFrom time to time, we found ourselves in needs of a service for starting the container with some setting up and tearing down work.\nIf you happen to have similar needs, `ContainersService` comes pretty handy.\nYou can run your container like this:\n\n```python\nimport asyncio\nimport asyncio.subprocess\n\nfrom containers import Container\nfrom containers import ImageMount\nfrom containers import BindMount\nfrom containers import ContainersService\n\nservice = ContainersService()\n\nasync def run():\n    container = Container(\n        image=\"alpine\",\n        command=(\"/path/to/my/exe\", \"arg0\", \"arg1\"),\n        mounts=[\n            ImageMount(\n                target=\"/data\",\n                source=\"my-data\",\n                read_write=True,\n            ),\n            BindMount(\n                target=\"/artifacts\",\n                source=\"/var/tmp/artifacts\",\n                readonly=False,\n                relabel=\"private\",\n                bind_propagation=\"rslave\"\n            ),\n        ],\n    )\n    async with service.run(container, stdout=asyncio.subprocess.PIPE) as proc:\n        stdout = await proc.stdout.read()\n        code = await proc.wait()\n        if code != 0:\n            raise RuntimeError(\"Failed\")\n        # ... \n\nasyncio.run(run())\n```\n\nWith the context manager, we can easily manipulate the container and make some preparation before running it and tear down after the container is done.\nFor example, under Windows, if you are running the container with a seccomp profile with a WSL UNC path, podman won't be able to access the seccomp profile file.\n\n```python\nimport asyncio\nimport copy\nimport typing\nimport pathlib\nimport contextlib\nimport tempfile\nimport shutil\n\nfrom containers import Container\nfrom containers import ContainersService\nfrom containers.data_types import PathType\n\n\ndef _to_wsl_path(path: pathlib.Path):\n    if not isinstance(path, pathlib.WindowsPath):\n        raise ValueError(f\"Expected windows path but got {path.__class__} instead\")\n    new_path = pathlib.Path(path)\n    if new_path.drive == \"\":\n        return new_path.as_posix()\n    _, *parts = new_path.parts\n    return str(\n        pathlib.PurePosixPath(\"/mnt\", new_path.drive.rstrip(\":\").lower(), *parts)\n    )\n\n\ndef _is_unc_path(path: PathType) -\u003e bool:\n    path = pathlib.Path(path)\n    return path.drive.startswith(\"\\\\\")\n\n\n\nclass WindowsContainersService(ContainersService):\n    @contextlib.asynccontextmanager\n    async def _make_temp_copy(\n        self, src: typing.Optional[pathlib.Path], suffix: typing.Optional[str] = None\n    ) -\u003e typing.AsyncContextManager[pathlib.Path]:\n        # Only copy if the drive is provided and a UNC path\n        if src is not None and _is_unc_path(src):\n            with (\n                open(src, \"rb\") as src_file,\n                tempfile.NamedTemporaryFile(suffix=suffix) as temp_file,\n            ):\n                self.logger.debug(\n                    \"Make a temp copy of seccomp profile from %s to native windows filesystem at %s\",\n                    src,\n                    temp_file.name,\n                )\n                shutil.copyfileobj(src_file, temp_file)\n                temp_file.flush()\n                yield pathlib.Path(temp_file.name)\n        else:\n            yield src\n\n    @contextlib.asynccontextmanager\n    async def run(\n        self,\n        container: Container,\n        stdin: typing.Optional[int] = None,\n        stdout: typing.Optional[int] = None,\n        stderr: typing.Optional[int] = None,\n        log_level: typing.Optional[str] = None,\n    ) -\u003e typing.AsyncContextManager[asyncio.subprocess.Process]:\n        container = copy.deepcopy(container)\n\n        seccomp_profile = None\n        if (\n            container.security_options is not None\n            and container.security_options.seccomp is not None\n        ):\n            seccomp_profile = container.security_options.seccomp\n        # Under windows, if the project is living in WSL, the seccomp profile file path\n        # might look like this:\n        # \\\\wsl$\\Ubuntu-18.04\\home\\user\\workspace\\my-project\\seccomp-profiles\\git.json\n        # And it's not going to work with podman, so we need to make a temp copy into\n        # native windows file system\n        async with self._make_temp_copy(seccomp_profile, suffix=\".json\") as temp_seccomp_profile:\n            if temp_seccomp_profile is not None:\n                # Not only we need to copy to native window filesystem, we also\n                # need to convert it into WSL path for podman in the podman WSL machine\n                # to read\n                # ref: https://github.com/containers/podman/issues/14494\n                container.security_options.seccomp = _to_wsl_path(temp_seccomp_profile)\n            async with super().run(container, stdin, stdout, stderr, log_level) as proc:\n                yield proc\n\n```\n\nThis is just an example shows that it's very powerful to use a context manager for running your container in Python.\nThis allows us to work around many issues while dealing with different quirk of the container in different platform.\nWe actually provide the `WindowsContainerService` in the package for working around problems we saw:\n\n - Automatically make a temp copy of seccomp profile with a WSL UNC path and use the temp copy instead\n - Automatically make a temp copy of a readonly UNC mount path and use the temp copy instead\n\nYou can import and use it directly, or use `make_containers_service` to create the container service based on your current operating system.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaunchplatform%2Fcontainer-helpers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaunchplatform%2Fcontainer-helpers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaunchplatform%2Fcontainer-helpers/lists"}