Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-podman for openSUSE:Factory checked in at 2026-01-22 15:13:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-podman (Old) and /work/SRC/openSUSE:Factory/.python-podman.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-podman" Thu Jan 22 15:13:57 2026 rev:25 rq:1328508 version:5.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-podman/python-podman.changes 2025-10-23 16:39:46.080270146 +0200 +++ /work/SRC/openSUSE:Factory/.python-podman.new.1928/python-podman.changes 2026-01-22 15:15:56.310632974 +0100 @@ -1,0 +2,29 @@ +Wed Jan 21 12:30:14 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 5.7.0: + * Fix attr getters in Network Resource by @nlacasse in #576 + * Include test files in the source distribution by @mgorny in + #586 + * Inknos update publish to pypy steps by @inknos in #590 + * Update testing section by @inknos in #591 + * Add support for export and import commands by @Riushda in #593 + * fix: use active service connection when no explicit connection + specified by @Honny1 in #597 + * Add direct support to --manifest when building container + images. by @MisterOwlPT in #596 + * fix: typing in podman.domain.containers_run by @dklimpel in + #599 + * misc: improve type checking by @dklimpel in #601 + * bump tmt pre-commit hook to 1.62.1 by @Honny1 in #606 + * Enable tests downstream via packit/tmt by @inknos in #588 + * Add support to --secrets when building container images by + @gsilva00 in #603 + * fix image build not using cache by @fulminemizzega in #559 + * Delete custom cached_property in favor of + functools.cached_property by @yangdanny97 in #607 + * Fixed filtering for networks by @sekyHC in #616 + * networks_manager: fix dns_enabled parameter description by + @sekyHC in #615 + * Bump release to 5.7.0 by @inknos in #620 + +------------------------------------------------------------------- Old: ---- podman-5.6.0.tar.gz New: ---- podman-5.7.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-podman.spec ++++++ --- /var/tmp/diff_new_pack.iP49Oy/_old 2026-01-22 15:15:58.214712160 +0100 +++ /var/tmp/diff_new_pack.iP49Oy/_new 2026-01-22 15:15:58.234712991 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-podman # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -27,7 +27,7 @@ %bcond_with test %endif Name: python-podman%{psuffix} -Version: 5.6.0 +Version: 5.7.0 Release: 0 Summary: A library to interact with a Podman server License: Apache-2.0 @@ -42,6 +42,7 @@ BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros +BuildRequires: distribution-release Requires: (python-tomli >= 1.2.3 if python-base < 3.11) Requires: python-requests >= 2.24 Requires: python-urllib3 @@ -57,7 +58,6 @@ BuildRequires: %{python_module dataclasses} %endif BuildRequires: podman -BuildRequires: distribution-release # /SECTION %endif %python_subpackages ++++++ podman-5.6.0.tar.gz -> podman-5.7.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/.github/actions/build-python-package/action.yml new/podman-py-5.7.0/.github/actions/build-python-package/action.yml --- old/podman-py-5.6.0/.github/actions/build-python-package/action.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/podman-py-5.7.0/.github/actions/build-python-package/action.yml 2026-01-21 11:39:13.000000000 +0100 @@ -0,0 +1,54 @@ +name: 'Build Python Package' +description: 'Build Python distribution packages (wheel and source tarball)' +inputs: + python-version: + description: 'Python version to use for building' + required: false + default: '3.x' + upload-artifact: + description: 'Whether to upload the built packages as artifacts' + required: false + default: 'false' + artifact-name: + description: 'Name for the uploaded artifact' + required: false + default: 'python-package-distributions' + build-backend: + description: 'Build backend to use (build, hatch, etc.)' + required: false + default: 'build' + +outputs: + dist-path: + description: 'Path to the built distribution packages' + value: 'dist/' + +runs: + using: 'composite' + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python-version }} + + - name: Install build backend + shell: bash + run: | + python3 -m pip install ${{ inputs.build-backend }} --user + + - name: Build distribution packages + shell: bash + run: | + python3 -m build + + - name: Upload distribution packages + if: ${{ inputs.upload-artifact == 'true' }} + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.artifact-name }} + path: dist/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/.github/workflows/build.yml new/podman-py-5.7.0/.github/workflows/build.yml --- old/podman-py-5.6.0/.github/workflows/build.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/podman-py-5.7.0/.github/workflows/build.yml 2026-01-21 11:39:13.000000000 +0100 @@ -0,0 +1,18 @@ +name: Test Build Python distribution 📦 + +on: + pull_request: + +jobs: + build: + name: Test Build Python distribution 📦 + runs-on: ubuntu-latest + environment: + name: build + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Build Python package + uses: ./.github/actions/build-python-package diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/.github/workflows/publish-to-pypi.yml new/podman-py-5.7.0/.github/workflows/publish-to-pypi.yml --- old/podman-py-5.6.0/.github/workflows/publish-to-pypi.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/podman-py-5.7.0/.github/workflows/publish-to-pypi.yml 2026-01-21 11:39:13.000000000 +0100 @@ -0,0 +1,41 @@ +name: Publish Python 🐍 distribution 📦 to PyPI + +on: + push: + tags: + - 'v[0-9]+\.[0-9]+\.[0-9]+\.?[0-9]?' + +jobs: + build: + name: Build distribution 📦 + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Build Python package + uses: ./.github/actions/build-python-package + with: + upload-artifact: true + artifact-name: python-package-distributions-pypi + + publish-to-pypi: + name: >- + Publish Python 🐍 distribution 📦 to PyPI + if: ${{ github.repository == 'containers/podman-py' }} + needs: build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/podman + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download artifacts from build job + uses: actions/download-artifact@v5 + with: + name: python-package-distributions-pypi + path: dist/ + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/.github/workflows/publish-to-test-pypi.yml new/podman-py-5.7.0/.github/workflows/publish-to-test-pypi.yml --- old/podman-py-5.6.0/.github/workflows/publish-to-test-pypi.yml 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/.github/workflows/publish-to-test-pypi.yml 2026-01-21 11:39:13.000000000 +0100 @@ -1,108 +1,28 @@ -name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI +name: Publish Python 🐍 distribution 📦 to TestPyPI -on: push +on: + push: + branches: + - main jobs: build: name: Build distribution 📦 - # ensure the workflow is never executed on forked branches - # it would fail anyway, so we just avoid to see an error - if: ${{ github.repository == 'containers/podman-py' }} runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v5 - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: "3.x" - - - name: Install pypa/build - run: >- - python3 -m - pip install - build - --user - - name: Build a binary wheel and a source tarball - run: python3 -m build - - name: Store the distribution packages - uses: actions/upload-artifact@v4 - with: - name: python-package-distributions - path: dist/ - - publish-to-pypi: - name: >- - Publish Python 🐍 distribution 📦 to PyPI - if: startsWith(github.ref, 'refs/tags/') && github.repository == 'containers/podman-py' - needs: - - build - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/podman - permissions: - id-token: write # IMPORTANT: mandatory for trusted publishing - steps: - - name: Download all the dists - uses: actions/download-artifact@v5 - with: - name: python-package-distributions - path: dist/ - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - name: Checkout code + uses: actions/checkout@v5 - github-release: - name: >- - Sign the Python 🐍 distribution 📦 with Sigstore - and upload them to GitHub Release - if: github.repository == 'containers/podman-py' - needs: - - publish-to-pypi - runs-on: ubuntu-latest - - permissions: - contents: write # IMPORTANT: mandatory for making GitHub Releases - id-token: write # IMPORTANT: mandatory for sigstore - - steps: - - name: Download all the dists - uses: actions/download-artifact@v5 + - name: Build Python package + uses: ./.github/actions/build-python-package with: - name: python-package-distributions - path: dist/ - - name: Sign the dists with Sigstore - uses: sigstore/[email protected] - with: - inputs: >- - ./dist/*.tar.gz - ./dist/*.whl - - - name: Create GitHub Release - env: - GITHUB_TOKEN: ${{ github.token }} - run: >- - gh release create - '${{ github.ref_name }}' - --repo '${{ github.repository }}' - --generate-notes - - name: Upload artifact signatures to GitHub Release - env: - GITHUB_TOKEN: ${{ github.token }} - # Upload to GitHub Release using the `gh` CLI. - # `dist/` contains the built packages, and the - # sigstore-produced signatures and certificates. - run: >- - gh release upload - '${{ github.ref_name }}' dist/** - --repo '${{ github.repository }}' + upload-artifact: true + artifact-name: python-package-distributions-testpypi publish-to-testpypi: name: Publish Python 🐍 distribution 📦 to TestPyPI - if: github.repository == 'containers/podman-py' - needs: - - build + if: ${{ github.repository == 'containers/podman-py' }} + needs: build runs-on: ubuntu-latest environment: @@ -113,14 +33,14 @@ id-token: write # IMPORTANT: mandatory for trusted publishing steps: - - name: Download all the dists + - name: Download artifacts from build job uses: actions/download-artifact@v5 with: - name: python-package-distributions + name: python-package-distributions-testpypi path: dist/ - name: Publish distribution 📦 to TestPyPI uses: pypa/gh-action-pypi-publish@release/v1 with: repository-url: https://test.pypi.org/legacy/ - skip_existing: true + skip-existing: true verbose: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/.packit.yaml new/podman-py-5.7.0/.packit.yaml --- old/podman-py-5.6.0/.packit.yaml 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/.packit.yaml 2026-01-21 11:39:13.000000000 +0100 @@ -15,10 +15,6 @@ dest: plans/ delete: true mkpath: true - - src: tests/ - dest: tests/ - delete: true - mkpath: true - src: .fmf/ dest: .fmf/ delete: true @@ -36,6 +32,10 @@ python-podman-rhel: specfile_path: rpm/python-podman.spec +actions: + prepare-files: >- + bash -c "sed -i 's/^\(\s*\)ref: .*/\1ref: \"${PACKIT_PROJECT_TAG}\"/' ${PACKIT_DOWNSTREAM_REPO}/plans/main.fmf" + srpm_build_deps: - make diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/.pre-commit-config.yaml new/podman-py-5.7.0/.pre-commit-config.yaml --- old/podman-py-5.6.0/.pre-commit-config.yaml 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/.pre-commit-config.yaml 2026-01-21 11:39:13.000000000 +0100 @@ -16,7 +16,7 @@ # Run the formatter. - id: ruff-format - repo: https://github.com/teemtee/tmt.git - rev: 1.39.0 + rev: 1.62.1 hooks: - id: tmt-lint - repo: https://github.com/pre-commit/mirrors-mypy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/CONTRIBUTING.md new/podman-py-5.7.0/CONTRIBUTING.md --- old/podman-py-5.6.0/CONTRIBUTING.md 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/CONTRIBUTING.md 2026-01-21 11:39:13.000000000 +0100 @@ -40,7 +40,7 @@ Run unit tests and get coverage report: -``` +```bash pip install tox tox -e coverage ``` @@ -48,21 +48,33 @@ #### Advanced testing Always prefer to run `tox` directly, even when you want to run a specific test or scenario. -Instead of running `pytest` directly, you should run: +Instead of running `pytest` directly, you should run it through tox: -``` +```bash tox -e py -- podman/tests/integration/test_container_create.py -k test_container_directory_volume_mount ``` -If you'd like to test against a specific `tox` environment you can do: +Pass pytest options after `--`. + +##### Test against a specific python version -``` +If you'd like to test against a specific `tox` environment (like a specific python version) you can do: + +```bash tox -e py12 -- podman/tests/integration/test_container_create.py -k test_container_directory_volume_mount ``` -Pass pytest options after `--`. +Avaliable environments are specified under `tox.envlist` in `tox.ini` + +##### Test against a specific podman version + +By default, our test suite uses the podman version that's found in the path, i.e., the installed binary. If you want to use a specific version, or a custom build, pass it via the env variable. + +```bash +PODMAN_BINARY=/path/to/podman/bin tox -e py -- ... +``` -#### Testing future features +##### Testing future features Since `podman-py` follows stable releases of `podman`, tests are thought to be run against libpod's versions that are commonly installed in the distributions. Tests can be versioned, @@ -84,6 +96,41 @@ The option `--pnext` **enables** the tests with the `pnext` pytest marker, and `-m pnext` will run the marked tests **only**. +##### Testing version-specific or distro-specific scenarios + +Tests can be conditionally skipped based on `podman`'s version or on the host +that's running the test suite. + +```python +from podman.tests.utils import OS_RELEASE, PODMAN_VERSION + + [email protected]( + PODMAN_VERSION < (5, 6, 0), + reason="Feature introduced in Podman 5.6.0 https://github.com/...", +) [email protected]( + OS_RELEASE["ID"] == "fedora" and int(OS_RELEASE["VERSION_ID"]) < 42, + reason="Feature patched in F42 or later https://github.com/...", +) +``` + +#### Common test issues + +Tests work by establishing a ssh connection with localhost. If you see +this line repeating after launching tox you might need to fix your ssh to be +able to connect locally. + +```log +... +2025-09-25 13:20:44 [ DEBUG] Waiting on /run/user/1000/podman/podman-forward-a65fd89525a248608d4c.sock (ssh.py:89) +2025-09-25 13:20:44 [ DEBUG] Waiting on /run/user/1000/podman/podman-forward-a65fd89525a248608d4c.sock (ssh.py:89) +... +``` + +Most of the times, if you can run `ssh localhost exit` successfully your tests + will run. + ## Submitting changes - Create a github pull request (PR) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/MANIFEST.in new/podman-py-5.7.0/MANIFEST.in --- old/podman-py-5.6.0/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100 +++ new/podman-py-5.7.0/MANIFEST.in 2026-01-21 11:39:13.000000000 +0100 @@ -0,0 +1,2 @@ +recursive-include podman/tests *.py *.md +include tox.ini diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/Makefile new/podman-py-5.7.0/Makefile --- old/podman-py-5.6.0/Makefile 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/Makefile 2026-01-21 11:39:13.000000000 +0100 @@ -8,7 +8,7 @@ EPOCH_TEST_COMMIT ?= $(shell git merge-base $${DEST_BRANCH:-main} HEAD) HEAD ?= HEAD -export PODMAN_VERSION ?= "5.6.0" +export PODMAN_VERSION ?= "5.7.0" .PHONY: podman podman: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/api/__init__.py new/podman-py-5.7.0/podman/api/__init__.py --- old/podman-py-5.6.0/podman/api/__init__.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/api/__init__.py 2026-01-21 11:39:13.000000000 +0100 @@ -1,6 +1,5 @@ """Tools for connecting to a Podman service.""" -from podman.api.cached_property import cached_property from podman.api.client import APIClient from podman.api.api_versions import VERSION, COMPATIBLE_VERSION from podman.api.http_utils import encode_auth_header, prepare_body, prepare_filters @@ -24,7 +23,6 @@ 'COMPATIBLE_VERSION', 'DEFAULT_CHUNK_SIZE', 'VERSION', - 'cached_property', 'create_tar', 'decode_header', 'encode_auth_header', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/api/cached_property.py new/podman-py-5.7.0/podman/api/cached_property.py --- old/podman-py-5.6.0/podman/api/cached_property.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/api/cached_property.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,10 +0,0 @@ -"""Provide cached_property for Python <=3.8 programs.""" - -import functools - -try: - from functools import cached_property # pylint: disable=unused-import -except ImportError: - - def cached_property(fn): # type: ignore[no-redef] - return property(functools.lru_cache()(fn)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/api/client.py new/podman-py-5.7.0/podman/api/client.py --- old/podman-py-5.6.0/podman/api/client.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/api/client.py 2026-01-21 11:39:13.000000000 +0100 @@ -32,6 +32,9 @@ ] """Type alias for request data parameter.""" +_Params = Union[None, bytes, Mapping[str, Any]] +"""Type alias for request query parameter.""" + _Timeout = Union[None, float, tuple[float, float], tuple[float, None]] """Type alias for request timeout parameter.""" @@ -207,7 +210,7 @@ self, path: Union[str, bytes], *, - params: Union[None, bytes, Mapping[str, str]] = None, + params: _Params = None, headers: Optional[Mapping[str, str]] = None, timeout: _Timeout = None, stream: Optional[bool] = False, @@ -242,7 +245,7 @@ self, path: Union[str, bytes], *, - params: Union[None, bytes, Mapping[str, list[str]]] = None, + params: _Params = None, headers: Optional[Mapping[str, str]] = None, timeout: _Timeout = None, stream: Optional[bool] = False, @@ -277,7 +280,7 @@ self, path: Union[str, bytes], *, - params: Union[None, bytes, Mapping[str, str]] = None, + params: _Params = None, headers: Optional[Mapping[str, str]] = None, timeout: _Timeout = None, stream: Optional[bool] = False, @@ -312,7 +315,7 @@ self, path: Union[str, bytes], *, - params: Union[None, bytes, Mapping[str, str]] = None, + params: _Params = None, data: _Data = None, headers: Optional[Mapping[str, str]] = None, timeout: _Timeout = None, @@ -351,7 +354,7 @@ self, path: Union[str, bytes], *, - params: Union[None, bytes, Mapping[str, str]] = None, + params: _Params = None, data: _Data = None, headers: Optional[Mapping[str, str]] = None, timeout: _Timeout = None, @@ -391,7 +394,7 @@ path: Union[str, bytes], *, data: _Data = None, - params: Union[None, bytes, Mapping[str, str]] = None, + params: _Params = None, headers: Optional[Mapping[str, str]] = None, timeout: _Timeout = None, stream: Optional[bool] = None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/api/http_utils.py new/podman-py-5.7.0/podman/api/http_utils.py --- old/podman-py-5.6.0/podman/api/http_utils.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/api/http_utils.py 2026-01-21 11:39:13.000000000 +0100 @@ -84,6 +84,12 @@ ): continue + # networks can actually be a dictionary with an empty value like this: + # networks={network.name: {}} + if key == "networks": + canonical[key] = value + continue + # depending on type we need details... proposal: Any if isinstance(value, collections.abc.Mapping): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/client.py new/podman-py-5.7.0/podman/client.py --- old/podman-py-5.6.0/podman/client.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/client.py 2026-01-21 11:39:13.000000000 +0100 @@ -3,10 +3,10 @@ import logging import os from contextlib import AbstractContextManager +from functools import cached_property from pathlib import Path from typing import Any, Optional -from podman.api import cached_property from podman.api.client import APIClient from podman.api.path_utils import get_runtime_dir from podman.domain.config import PodmanConfig @@ -69,8 +69,15 @@ # Override configured identity, if provided in arguments api_kwargs["identity"] = kwargs.get("identity", str(connection.identity)) elif "base_url" not in api_kwargs: - path = str(Path(get_runtime_dir()) / "podman" / "podman.sock") - api_kwargs["base_url"] = "http+unix://" + path + # Check if there's an active service configured and is a podman machine + active_service = config.active_service + if active_service and active_service.is_machine: + api_kwargs["base_url"] = active_service.url.geturl() + api_kwargs["identity"] = kwargs.get("identity", str(active_service.identity)) + else: + # Fall back to local Unix socket + path = str(Path(get_runtime_dir()) / "podman" / "podman.sock") + api_kwargs["base_url"] = "http+unix://" + path self.api = APIClient(**api_kwargs) def __enter__(self) -> "PodmanClient": diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/domain/config.py new/podman-py-5.7.0/podman/domain/config.py --- old/podman-py-5.6.0/podman/domain/config.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/domain/config.py 2026-01-21 11:39:13.000000000 +0100 @@ -2,11 +2,11 @@ import sys import urllib +from functools import cached_property from pathlib import Path from typing import Optional import json -from podman.api import cached_property from podman.api.path_utils import get_xdg_config_home if sys.version_info >= (3, 11): @@ -59,6 +59,11 @@ return Path(self.attrs.get("identity")) return Path(self.attrs.get("Identity")) + @cached_property + def is_machine(self) -> bool: + """bool: Returns True if connection is to a Podman machine.""" + return self.attrs.get("IsMachine", False) + class PodmanConfig: """PodmanConfig provides a representation of the containers.conf file.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/domain/containers_run.py new/podman-py-5.7.0/podman/domain/containers_run.py --- old/podman-py-5.6.0/podman/domain/containers_run.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/domain/containers_run.py 2026-01-21 11:39:13.000000000 +0100 @@ -25,7 +25,7 @@ stderr=False, remove: bool = False, **kwargs, - ) -> Union[Container, Union[Generator[str, None, None], Iterator[str]]]: + ) -> Union[Container, Union[Generator[bytes, None, None], Iterator[bytes]]]: """Run a container. By default, run() will wait for the container to finish and return its logs. @@ -66,20 +66,22 @@ APIError: when Podman service reports an error """ if isinstance(image, Image): - image = image.id + image_id = image.id + else: + image_id = image if isinstance(command, str): command = [command] try: - container = self.create(image=image, command=command, **kwargs) + container = self.create(image=image_id, command=command, **kwargs) # type: ignore[attr-defined] except ImageNotFound: - self.podman_client.images.pull( - image, + self.podman_client.images.pull( # type: ignore[attr-defined] + image_id, auth_config=kwargs.get("auth_config"), platform=kwargs.get("platform"), policy=kwargs.get("policy", "missing"), ) - container = self.create(image=image, command=command, **kwargs) + container = self.create(image=image_id, command=command, **kwargs) # type: ignore[attr-defined] container.start() container.reload() @@ -116,6 +118,6 @@ container.remove() if exit_status != 0: - raise ContainerError(container, exit_status, command, image, log_iter) + raise ContainerError(container, exit_status, command, image_id, log_iter) - return log_iter if kwargs.get("stream", False) or log_iter is None else b"".join(log_iter) + return log_iter if kwargs.get("stream", False) or log_iter is None else b"".join(log_iter) # type: ignore[return-value] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/domain/images_build.py new/podman-py-5.7.0/podman/domain/images_build.py --- old/podman-py-5.6.0/podman/domain/images_build.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/domain/images_build.py 2026-01-21 11:39:13.000000000 +0100 @@ -63,9 +63,13 @@ isolation (str) – Isolation technology used during build. (ignored) use_config_proxy (bool) – (ignored) http_proxy (bool) - Inject http proxy environment variables into container (Podman only) - layers (bool) - Cache intermediate layers during build. + layers (bool) - Cache intermediate layers during build. Default True. output (str) - specifies if any custom build output is selected for following build. outputformat (str) - The format of the output image's manifest and configuration data. + Default to "application/vnd.oci.image.manifest.v1+json" (OCI format). + manifest (str) - add the image to the specified manifest list. + Creates manifest list if it does not exist. + secrets (list[str]) - Secret files/envs to expose to the build Returns: first item is the podman.domain.images.Image built @@ -119,7 +123,7 @@ if kwargs.get("timeout"): post_kwargs["timeout"] = float(kwargs.get("timeout")) - response = self.client.post( + response = self.client.post( # type: ignore[attr-defined] "/build", params=params, data=body, @@ -152,7 +156,7 @@ unknown = line if image_id: - return self.get(image_id), report_stream + return self.get(image_id), report_stream # type: ignore[attr-defined] raise BuildError(unknown or "Unknown", report_stream) @@ -169,10 +173,11 @@ raise PodmanError("Custom encoding not supported when gzip enabled.") params = { - "dockerfile": kwargs.get("dockerfile"), + "dockerfile": kwargs.get("dockerfile", f".containerfile.{random.getrandbits(160):x}"), "forcerm": kwargs.get("forcerm"), "httpproxy": kwargs.get("http_proxy"), "networkmode": kwargs.get("network_mode"), + "manifest": kwargs.get("manifest"), "nocache": kwargs.get("nocache"), "platform": kwargs.get("platform"), "pull": kwargs.get("pull"), @@ -183,9 +188,11 @@ "squash": kwargs.get("squash"), "t": kwargs.get("tag"), "target": kwargs.get("target"), - "layers": kwargs.get("layers"), + "layers": kwargs.get("layers", True), "output": kwargs.get("output"), - "outputformat": kwargs.get("outputformat"), + "outputformat": kwargs.get( + "outputformat", "application/vnd.oci.image.manifest.v1+json" + ), } if "buildargs" in kwargs: @@ -206,8 +213,8 @@ if "labels" in kwargs: params["labels"] = json.dumps(kwargs.get("labels")) - if params["dockerfile"] is None: - params["dockerfile"] = f".containerfile.{random.getrandbits(160):x}" + if "secrets" in kwargs: + params["secrets"] = json.dumps(kwargs.get("secrets")) # Remove any unset parameters return dict(filter(lambda i: i[1] is not None, params.items())) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/domain/networks.py new/podman-py-5.7.0/podman/domain/networks.py --- old/podman-py-5.6.0/podman/domain/networks.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/domain/networks.py 2026-01-21 11:39:13.000000000 +0100 @@ -11,12 +11,15 @@ import json import logging from contextlib import suppress -from typing import Optional, Union +from typing import Optional, Union, TYPE_CHECKING from podman.domain.containers import Container from podman.domain.containers_manager import ContainersManager from podman.domain.manager import PodmanResource +if TYPE_CHECKING: + from podman.domain.networks_manager import NetworksManager + logger = logging.getLogger("podman.networks") @@ -27,6 +30,8 @@ attrs (dict[str, Any]): Attributes of Network reported from Podman service """ + manager: "NetworksManager" + @property def id(self): # pylint: disable=invalid-name """str: Returns the identifier of the network.""" @@ -44,7 +49,7 @@ """list[Container]: Returns list of Containers connected to network.""" with suppress(KeyError): container_manager = ContainersManager(client=self.client) - return [container_manager.get(ident) for ident in self.attrs["Containers"].keys()] + return [container_manager.get(ident) for ident in self.attrs["containers"].keys()] return [] @property @@ -105,7 +110,7 @@ } data = {"Container": container, "EndpointConfig": endpoint_config} - data = {k: v for (k, v) in data.items() if not (v is None or len(v) == 0)} + data = {k: v for (k, v) in data.items() if not (v is None or len(v) == 0)} # type: ignore[arg-type] response = self.client.post( f"/networks/{self.name}/connect", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/domain/networks_manager.py new/podman-py-5.7.0/podman/domain/networks_manager.py --- old/podman-py-5.6.0/podman/domain/networks_manager.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/domain/networks_manager.py 2026-01-21 11:39:13.000000000 +0100 @@ -39,7 +39,8 @@ Keyword Args: attachable (bool): Ignored, always False. check_duplicate (bool): Ignored, always False. - dns_enabled (bool): When True, do not provision DNS for this network. + dns_enabled (bool): DNSEnabled is whether name resolution is active for container on + this Network. Only supported with the bridge driver. driver (str): Which network driver to use when creating network. enable_ipv6 (bool): Enable IPv6 on the network. ingress (bool): Ignored, always False. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/domain/secrets.py new/podman-py-5.7.0/podman/domain/secrets.py --- old/podman-py-5.6.0/podman/domain/secrets.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/domain/secrets.py 2026-01-21 11:39:13.000000000 +0100 @@ -11,6 +11,8 @@ class Secret(PodmanResource): """Details and configuration for a secret registered with the Podman service.""" + manager: "SecretsManager" + def __repr__(self): return f"<{self.__class__.__name__}: {self.name}>" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/domain/volumes.py new/podman-py-5.7.0/podman/domain/volumes.py --- old/podman-py-5.6.0/podman/domain/volumes.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/domain/volumes.py 2026-01-21 11:39:13.000000000 +0100 @@ -4,6 +4,7 @@ from typing import Any, Literal, Optional, Union import requests +import pathlib from podman import api from podman.domain.manager import Manager, PodmanResource @@ -15,6 +16,8 @@ class Volume(PodmanResource): """Details and configuration for an image managed by the Podman service.""" + manager: "VolumesManager" + @property def id(self): return self.name @@ -173,3 +176,49 @@ name = name.name response = self.client.delete(f"/volumes/{name}", params={"force": force}) response.raise_for_status() + + def export_archive(self, name: Union[Volume, str]) -> bytes: + """Export a podman volume, returns the exported archive as bytes. + + Args: + name: Identifier for Volume to be exported. + + Raises: + APIError: when service reports an error + """ + if isinstance(name, Volume): + name = name.name + response = self.client.get(f"/volumes/{name}/export") + response.raise_for_status() + return response._content + + def import_archive( + self, name: Union[Volume, str], data: Optional[bytes] = None, path: Optional[str] = None + ): + """Import a podman volume from tar. + The podman volume archive must be provided either as bytes or as a path to the archive. + + Args: + name: Identifier for Volume to be imported. + data: Uncompressed tar archive as bytes. + path: Path to uncompressed tar archive. + + Raises: + APIError: when service reports an error + """ + if isinstance(name, Volume): + name = name.name + + if data is None and path is None: + raise RuntimeError("Either data or path must be provided !") + elif data is not None and path is not None: + raise RuntimeError("Data and path must not be set at the same time !") + + if data is None: + file = pathlib.Path(path) + if not file.exists(): + raise RuntimeError(f"Archive {path} does not exist !") + data = file.read_bytes() + + response = self.client.post(f"/volumes/{name}/import", data=data) + response.raise_for_status() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/errors/exceptions.py new/podman-py-5.7.0/podman/errors/exceptions.py --- old/podman-py-5.6.0/podman/errors/exceptions.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/errors/exceptions.py 2026-01-21 11:39:13.000000000 +0100 @@ -115,7 +115,7 @@ exit_status: int, command: Union[str, list[str]], image: str, - stderr: Optional[Iterable[str]] = None, + stderr: Optional[Iterable[bytes]] = None, ): # pylint: disable=too-many-positional-arguments """Initialize ContainerError. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/tests/__init__.py new/podman-py-5.7.0/podman/tests/__init__.py --- old/podman-py-5.6.0/podman/tests/__init__.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/tests/__init__.py 2026-01-21 11:39:13.000000000 +0100 @@ -3,5 +3,5 @@ # Do not auto-update these from version.py, # as test code should be changed to reflect changes in Podman API versions BASE_SOCK = "unix:///run/api.sock" -LIBPOD_URL = "http://%2Frun%2Fapi.sock/v5.6.0/libpod" +LIBPOD_URL = "http://%2Frun%2Fapi.sock/v5.7.0/libpod" COMPATIBLE_URL = "http://%2Frun%2Fapi.sock/v1.40" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/tests/integration/test_containers.py new/podman-py-5.7.0/podman/tests/integration/test_containers.py --- old/podman-py-5.6.0/podman/tests/integration/test_containers.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/tests/integration/test_containers.py 2026-01-21 11:39:13.000000000 +0100 @@ -1,7 +1,6 @@ import io import random import tarfile -import tempfile import unittest try: @@ -86,7 +85,7 @@ with self.subTest("Archive /root/unittest"): self.assertTrue(container.put_archive("/root", data=tarball)) - actual, stats = container.get_archive("/root") + actual, _ = container.get_archive("/root") with io.BytesIO() as fd: for chunk in actual: @@ -184,16 +183,8 @@ def test_container_rm_anonymous_volume(self): with self.subTest("Check anonymous volume is removed"): - container_file = """ -FROM alpine -VOLUME myvol -ENV foo=bar -""" - tmp_file = tempfile.mktemp() - file = open(tmp_file, 'w') - file.write(container_file) - file.close() - self.client.images.build(dockerfile=tmp_file, tag="test-img", path=".") + container_file = io.StringIO("\n".join(["FROM alpine", "VOLUME myvol", "ENV foo=bar"])) + test_img, _ = self.client.images.build(fileobj=container_file, tag="test-img", path=".") # get existing number of containers and volumes existing_containers = self.client.containers.list(all=True) @@ -211,6 +202,8 @@ self.assertEqual(len(container_list), len(existing_containers)) volume_list = self.client.volumes.list() self.assertEqual(len(volume_list), len(existing_volumes)) + # clean up + self.client.images.remove(test_img) def test_container_labels(self): labels = {'label1': 'value1', 'label2': 'value2'} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/tests/integration/test_images.py new/podman-py-5.7.0/podman/tests/integration/test_images.py --- old/podman-py-5.6.0/podman/tests/integration/test_images.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/tests/integration/test_images.py 2026-01-21 11:39:13.000000000 +0100 @@ -15,16 +15,19 @@ """Images integration tests.""" import io +import os +import json import platform import tarfile +import tempfile import types import unittest +import random import podman.tests.integration.base as base from podman import PodmanClient from podman.domain.images import Image -from podman.errors import APIError, ImageNotFound, PodmanError - +from podman.errors import APIError, ContainerError, ImageNotFound, PodmanError # @unittest.skipIf(os.geteuid() != 0, 'Skipping, not running as root') @@ -38,6 +41,12 @@ self.client = PodmanClient(base_url=self.socket_uri) self.addCleanup(self.client.close) + self.test_manifest_name = "dummy:v1.2.3" + + def tearDown(self) -> None: + if self.client.manifests.exists(self.test_manifest_name): + self.client.manifests.remove(self.test_manifest_name) + def test_image_crud(self): """Test Image CRUD. @@ -138,12 +147,59 @@ self.assertIn("payload does not match", e.exception.explanation) def test_build(self): + buffer = io.StringIO("""FROM scratch""") + image, _ = self.client.images.build(fileobj=buffer) + self.assertIsNotNone(image) + self.assertIsNotNone(image.id) + + def test_build_cache(self): + """Check build caching when enabled + + Build twice with caching enabled (default), then again with nocache + """ + + def look_for_cache(stream) -> bool: + # Search for a line with contents "-> Using cache <image id>" + uses_cache = False + for line in stream: + parsed = json.loads(line)['stream'] + if "Using cache" in parsed: + uses_cache = True + break + return uses_cache + + label = str(random.getrandbits(32)) + buffer = io.StringIO(f"""FROM scratch\nLABEL test={label}""") + image, _ = self.client.images.build(fileobj=buffer) + buffer.seek(0) + cached_image, stream = self.client.images.build(fileobj=buffer) + self.assertTrue(look_for_cache(stream)) + self.assertEqual( + cached_image.id, + image.id, + msg="Building twice with cache does not produce the same image id", + ) + # Build again with disabled cache + buffer.seek(0) + uncached_image, stream = self.client.images.build(fileobj=buffer, nocache=True) + self.assertFalse(look_for_cache(stream)) + self.assertNotEqual( + uncached_image.id, + image.id, + msg="Building twice without cache produces the same image id", + ) + + def test_build_with_manifest(self): buffer = io.StringIO("""FROM quay.io/libpod/alpine_labels:latest""") - image, stream = self.client.images.build(fileobj=buffer) + self.assertFalse(self.client.manifests.exists(self.test_manifest_name)) + + image, _ = self.client.images.build(fileobj=buffer, manifest=self.test_manifest_name) self.assertIsNotNone(image) self.assertIsNotNone(image.id) + self.assertTrue(self.client.manifests.exists(self.test_manifest_name)) + def test_build_with_context(self): context = io.BytesIO() with tarfile.open(fileobj=context, mode="w") as tar: @@ -172,7 +228,7 @@ # If requesting a custom context, currently must specify the dockerfile name self.client.images.build(custom_context=True, fileobj=context) - image, stream = self.client.images.build( + image, _ = self.client.images.build( fileobj=context, dockerfile="MyDockerfile", custom_context=True, @@ -180,6 +236,41 @@ self.assertIsNotNone(image) self.assertIsNotNone(image.id) + def test_build_with_secret(self): + with tempfile.TemporaryDirectory() as context_dir: + dockerfile_path = os.path.join(context_dir, "Dockerfile") + with open(dockerfile_path, "w") as f: + f.write(""" + FROM quay.io/libpod/alpine_labels:latest + RUN --mount=type=secret,id=example cat /run/secrets/example > /output.txt + """) + + secret_path = os.path.join(context_dir, "build-secret.txt") + with open(secret_path, "w") as f: + f.write("secret123") + + image, _ = self.client.images.build( + path=context_dir, + secrets=["id=example,src=build-secret.txt"], + dockerfile="Dockerfile", + ) + + self.assertIsNotNone(image) + self.assertIsNotNone(image.id) + + # Verify secret was passed and stored in file (NOT RECOMMENDED for real use cases) + container_out = self.client.containers.run( + image.id, command=["cat", "/output.txt"], remove=True, log_config={"Type": "json-file"} + ) + self.assertIn(b"secret123", container_out) + + # Verify mounted secret file is not present in image + with self.assertRaises(ContainerError) as exc: + self.client.containers.run( + image.id, command=["cat", "/run/secrets/example"], remove=True + ) + self.assertIn("No such file or directory", b"".join(exc.exception.stderr).decode("utf-8")) + @unittest.skipIf(platform.architecture()[0] == "32bit", "no 32-bit image available") def test_pull_stream(self): generator = self.client.images.pull("ubi8", tag="latest", stream=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/tests/integration/test_volumes.py new/podman-py-5.7.0/podman/tests/integration/test_volumes.py --- old/podman-py-5.6.0/podman/tests/integration/test_volumes.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/tests/integration/test_volumes.py 2026-01-21 11:39:13.000000000 +0100 @@ -4,6 +4,7 @@ from podman import PodmanClient from podman.errors import NotFound from podman.tests.integration import base +from podman.tests.utils import PODMAN_VERSION class VolumesIntegrationTest(base.IntegrationTest): @@ -35,6 +36,13 @@ names = [i.name for i in report] self.assertIn(volume_name, names) + if PODMAN_VERSION >= (5, 6, 0): + with self.subTest("Export"): + archive_bytes = self.client.volumes.export_archive(volume_name) + + with self.subTest("Import"): + self.client.volumes.import_archive(volume_name, archive_bytes) + with self.subTest("Remove"): self.client.volumes.remove(volume_name, force=True) with self.assertRaises(NotFound): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/tests/unit/test_build.py new/podman-py-5.7.0/podman/tests/unit/test_build.py --- old/podman-py-5.6.0/podman/tests/unit/test_build.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/tests/unit/test_build.py 2026-01-21 11:39:13.000000000 +0100 @@ -1,4 +1,5 @@ import io +import requests import json import unittest @@ -16,6 +17,20 @@ from podman.domain.images import Image from podman.errors import BuildError, DockerException +good_image_id = "032b8b2855fc" +good_stream = [ + {"stream": " ---\u003e a9eb17255234"}, + {"stream": "Step 1 : VOLUME /data"}, + {"stream": " ---\u003e Running in abdc1e6896c6"}, + {"stream": " ---\u003e 713bca62012e"}, + {"stream": "Removing intermediate container abdc1e6896c6"}, + {"stream": "Step 2 : CMD [\"/bin/sh\"]"}, + {"stream": " ---\u003e Running in dba30f2a1a7e"}, + {"stream": " ---\u003e 032b8b2855fc"}, + {"stream": "Removing intermediate container dba30f2a1a7e"}, + {"stream": f"{good_image_id}\n"}, +] + class TestBuildCase(unittest.TestCase): """Test ImagesManager build(). @@ -41,19 +56,7 @@ mock_prepare_containerfile.return_value = "Containerfile" mock_create_tar.return_value = b"This is a mocked tarball." - stream = [ - {"stream": " ---\u003e a9eb17255234"}, - {"stream": "Step 1 : VOLUME /data"}, - {"stream": " ---\u003e Running in abdc1e6896c6"}, - {"stream": " ---\u003e 713bca62012e"}, - {"stream": "Removing intermediate container abdc1e6896c6"}, - {"stream": "Step 2 : CMD [\"/bin/sh\"]"}, - {"stream": " ---\u003e Running in dba30f2a1a7e"}, - {"stream": " ---\u003e 032b8b2855fc"}, - {"stream": "Removing intermediate container dba30f2a1a7e"}, - {"stream": "032b8b2855fc\n"}, - ] - + stream = good_stream buffer = io.StringIO() for entry in stream: buffer.write(json.JSONEncoder().encode(entry)) @@ -66,13 +69,15 @@ "&buildargs=%7B%22BUILD_DATE%22%3A+%22January+1%2C+1970%22%7D" "&cpuperiod=10" "&extrahosts=%7B%22database%22%3A+%22127.0.0.1%22%7D" - "&labels=%7B%22Unittest%22%3A+%22true%22%7D", + "&labels=%7B%22Unittest%22%3A+%22true%22%7D" + "&manifest=example%3Av1.2.3" + "&secrets=%5B%22id%3Dexample%2Csrc%3Dpodman-build-secret123%22%5D", text=buffer.getvalue(), ) mock.get( - tests.LIBPOD_URL + "/images/032b8b2855fc/json", + tests.LIBPOD_URL + f"/images/{good_image_id}/json", json={ - "Id": "032b8b2855fc", + "Id": good_image_id, "ParentId": "", "RepoTags": ["fedora:latest", "fedora:33", "<none>:<none>"], "RepoDigests": [ @@ -98,9 +103,11 @@ }, extra_hosts={"database": "127.0.0.1"}, labels={"Unittest": "true"}, + manifest="example:v1.2.3", + secrets=["id=example,src=podman-build-secret123"], ) self.assertIsInstance(image, Image) - self.assertEqual(image.id, "032b8b2855fc") + self.assertEqual(image.id, good_image_id) self.assertIsInstance(logs, Iterable) @patch.object(api, "create_tar") @@ -130,16 +137,47 @@ @requests_mock.Mocker() def test_build_no_context(self, mock): - mock.post(tests.LIBPOD_URL + "/images/build") + mock.post(tests.LIBPOD_URL + "/build") with self.assertRaises(TypeError): self.client.images.build() @requests_mock.Mocker() def test_build_encoding(self, mock): - mock.post(tests.LIBPOD_URL + "/images/build") + mock.post(tests.LIBPOD_URL + "/build") with self.assertRaises(DockerException): self.client.images.build(path="/root", gzip=True, encoding="utf-8") + @patch.object(api, "create_tar") + @patch.object(api, "prepare_containerfile") + def test_build_defaults(self, mock_prepare_containerfile, mock_create_tar): + """Check the defaults used by images.build""" + mock_prepare_containerfile.return_value = "Containerfile" + mock_create_tar.return_value = b"This is a mocked tarball." + + stream = good_stream + buffer = io.StringIO() + for entry in stream: + buffer.write(json.dumps(entry)) + buffer.write("\n") + + with requests_mock.Mocker() as mock: + query = "?outputformat=" + ( + requests.utils.quote("application/vnd.oci.image.manifest.v1+json", safe='') + + "&layers=True" + ) + mock.post( + tests.LIBPOD_URL + "/build" + query, + text=buffer.getvalue(), + ) + mock.get( + tests.LIBPOD_URL + f"/images/{good_image_id}/json", + json={ + "Id": "unittest", + }, + ) + img, _ = self.client.images.build(path="/tmp/context_dir") + assert img.id == "unittest" + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/tests/unit/test_podmanclient.py new/podman-py-5.7.0/podman/tests/unit/test_podmanclient.py --- old/podman-py-5.6.0/podman/tests/unit/test_podmanclient.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/tests/unit/test_podmanclient.py 2026-01-21 11:39:13.000000000 +0100 @@ -6,6 +6,7 @@ import requests_mock +from podman.domain.config import PodmanConfig, ServiceConnection from podman import PodmanClient, tests from podman.api.path_utils import get_runtime_dir, get_xdg_config_home @@ -108,6 +109,23 @@ expected = Path(get_xdg_config_home()) / "containers" / "containers.conf" PodmanClientTestCase.opener.assert_called_with(expected, encoding="utf-8") + def test_connect_with_connection_file(self): + mock_config = MagicMock(spec=PodmanConfig) + mock_service = MagicMock(spec=ServiceConnection) + mock_service.url.geturl.return_value = ( + "http+ssh://[email protected]:58468/run/user/501/podman/podman.sock" + ) + mock_service.identity = "/Users/test/.local/share/containers/podman/machine/machine" + mock_service.is_machine = True + mock_config.active_service = mock_service + + with mock.patch('podman.client.PodmanConfig', return_value=mock_config): + # Mock pathlib.Path.exists to return True for the identity file + with mock.patch('pathlib.Path.exists', return_value=True): + with PodmanClient() as client: + expected = "http+ssh://[email protected]:58468/run/user/501/podman/podman.sock" + self.assertEqual(client.api.base_url.geturl(), expected) + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/tests/unit/test_volumesmanager.py new/podman-py-5.7.0/podman/tests/unit/test_volumesmanager.py --- old/podman-py-5.6.0/podman/tests/unit/test_volumesmanager.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/tests/unit/test_volumesmanager.py 2026-01-21 11:39:13.000000000 +0100 @@ -137,6 +137,28 @@ actual, {"VolumesDeleted": ["dbase", "source"], "SpaceReclaimed": 2048} ) + @requests_mock.Mocker() + def test_import(self, mock): + mock.post( + tests.LIBPOD_URL + "/volumes/dbase/import", + status_code=requests.codes.no_content, + ) + actual = self.client.volumes.import_archive("dbase", data=b'mocked_archive') + self.assertIsInstance(actual, type(None)) + + with self.assertRaises(RuntimeError): + # The archive does not exist + self.client.volumes.import_archive("dbase", path="/path/to/archive.tar") + + @requests_mock.Mocker() + def test_export(self, mock): + mock.get( + tests.LIBPOD_URL + "/volumes/dbase/export", + content=b'exported_archive', + ) + actual = self.client.volumes.export_archive("dbase") + self.assertIsInstance(actual, bytes) + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/podman/version.py new/podman-py-5.7.0/podman/version.py --- old/podman-py-5.6.0/podman/version.py 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/podman/version.py 2026-01-21 11:39:13.000000000 +0100 @@ -1,4 +1,4 @@ """Version of PodmanPy.""" -__version__ = "5.6.0" +__version__ = "5.7.0" __compatible_version__ = "1.40" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/pyproject.toml new/podman-py-5.7.0/pyproject.toml --- old/podman-py-5.6.0/pyproject.toml 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/pyproject.toml 2026-01-21 11:39:13.000000000 +0100 @@ -6,7 +6,7 @@ name = "podman" # TODO: remove the line version = ... on podman-py > 5.4.0 releases # dynamic = ["version"] -version = "5.6.0" +version = "5.7.0" description = "Bindings for Podman RESTful API" readme = "README.md" license = {file = "LICENSE"} @@ -141,19 +141,13 @@ "podman.domain.config", "podman.domain.containers", "podman.domain.containers_create", - "podman.domain.containers_run", "podman.domain.events", "podman.domain.images_build", "podman.domain.images_manager", "podman.domain.manager", "podman.domain.manifests", - "podman.domain.networks", - "podman.domain.networks_manager", - "podman.domain.pods", "podman.domain.pods_manager", "podman.domain.registry_data", - "podman.domain.secrets", - "podman.domain.volumes", "podman.errors.exceptions" ] ignore_errors = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podman-py-5.6.0/setup.cfg new/podman-py-5.7.0/setup.cfg --- old/podman-py-5.6.0/setup.cfg 2025-09-05 11:38:28.000000000 +0200 +++ new/podman-py-5.7.0/setup.cfg 2026-01-21 11:39:13.000000000 +0100 @@ -1,6 +1,6 @@ [metadata] name = podman -version = 5.6.0 +version = 5.7.0 author = Brent Baude, Jhon Honce, Urvashi Mohnani, Nicola Sella author_email = [email protected] description = Bindings for Podman RESTful API
