Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-docker for openSUSE:Factory 
checked in at 2026-08-21 17:01:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-docker (Old)
 and      /work/SRC/openSUSE:Factory/.python-docker.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-docker"

Fri Aug 21 17:01:21 2026 rev:30 rq:1372815 version:7.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-docker/python-docker.changes      
2024-08-28 21:34:37.930609627 +0200
+++ /work/SRC/openSUSE:Factory/.python-docker.new.1258/python-docker.changes    
2026-08-21 17:02:41.837975359 +0200
@@ -1,0 +2,59 @@
+Fri Aug 21 10:26:21 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 7.2.0:
+  * ## Upgrade Notes
+  * `docker.from_env()` now honors the active Docker CLI context
+    when `DOCKER_HOST` is not set.
+  * This means the client may connect to the daemon selected by
+    `DOCKER_CONTEXT` or the current context in
+    `~/.docker/config.json`, matching Docker CLI behavior more
+    closely.
+  * If your application relied on the previous default connection
+    behavior, set `DOCKER_HOST` explicitly, set
+    `DOCKER_CONTEXT=default`, or pass `use_context=False` to
+    `DockerClient.from_env()`.
+  * Added `docker.from_context()` / `DockerClient.from_context()`
+    for explicitly creating a client from a Docker CLI context.
+  * ## Features
+  * Added support for Docker contexts when creating the default
+    client
+  * Added subpath support for volumes
+  * ## Bugfixes
+  * Fixed `exec_run` documentation for the `stream` parameter
+  * Fixed image loading to avoid depending on the deprecated
+    `JSONMessage.error` field
+  * Preserved the rotated unlock key in swarm integration tests
+  * Fixed SSL certificate generation in tests
+  * Fixed IPv6 integration tests by explicitly enabling IPv6
+    where required
+  * ## Miscellaneous
+  * Updated tests for newer Docker Engine behavior
+  * CI and build updates
+  * tests/exec: expect 127 exit code for missing executable
+  * fixing doc for stream param in exec_run
+  * Bump default API version to 1.45 (Moby 26.0/26.1)
+  * Set a dummy-version if none set, and remove unused APT_MIRROR
+    build-arg
+  * test_service_logs: stop testing experimental versions
+  * Makefile: fix circular reference for integration-dind
+  * image load: don't depend on deprecated JSONMessage.error
+    field
+  * integration: test_create_volume_invalid_driver allow either
+    400 or 404
+  * integration: adjust tests for omitted "OnBuild"
+  * Implement Subpath Support for Volumes in Docker-Py
+  * tests: fix ssl generation
+  * test/integration: don't check for deprecated Networks field
+  * test: Skip from_env_unix tests if DOCKER_HOST is network
+    socket
+  * test_connect_with_ipv6_address: enable IPv6
+  * test_create_with_ipv6_address: enable IPv6
+  * tests: Migrate off gpg2 and regenerate key ed25519
+  * gha: Pin to digests
+  * Fix integration tests on non-amd64 hosts and add ARM64 CI
+  * integration/swarm: Preserve rotated unlock key
+  * [DKP-2535] Honour context if present for default client, add
+    contexts support
+  * docs: 7.2.0 changelog
+
+-------------------------------------------------------------------

Old:
----
  docker-7.1.0.tar.gz

New:
----
  docker-7.2.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-docker.spec ++++++
--- /var/tmp/diff_new_pack.sY6lxg/_old  2026-08-21 17:02:42.768008287 +0200
+++ /var/tmp/diff_new_pack.sY6lxg/_new  2026-08-21 17:02:42.770008358 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-docker
 #
-# Copyright (c) 2024 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
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-docker
-Version:        7.1.0
+Version:        7.2.0
 Release:        0
 Summary:        A Python library for the Docker Engine API
 License:        Apache-2.0

++++++ docker-7.1.0.tar.gz -> docker-7.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/.github/workflows/ci.yml 
new/docker-py-7.2.0/.github/workflows/ci.yml
--- old/docker-py-7.1.0/.github/workflows/ci.yml        2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/.github/workflows/ci.yml        2026-07-09 
15:51:20.000000000 +0200
@@ -10,8 +10,8 @@
     lint:
         runs-on: ubuntu-latest
         steps:
-            - uses: actions/checkout@v4
-            - uses: actions/setup-python@v5
+            - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 
# v4.3.1
+            - uses: 
actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
               with:
                   python-version: '3.x'
             - run: pip install -U ruff==0.1.8
@@ -21,12 +21,12 @@
     build:
       runs-on: ubuntu-22.04
       steps:
-        - uses: actions/checkout@v4
-        - uses: actions/setup-python@v5
+        - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # 
v4.3.1
+        - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 
# v5.6.0
           with:
             python-version: '3.x'
         - run: pip3 install build && python -m build .
-        - uses: actions/upload-artifact@v4
+        - uses: 
actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
           with:
             name: dist
             path: dist
@@ -38,9 +38,9 @@
                 python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
 
         steps:
-            - uses: actions/checkout@v4
+            - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 
# v4.3.1
             - name: Set up Python ${{ matrix.python-version }}
-              uses: actions/setup-python@v5
+              uses: 
actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
               with:
                 python-version: ${{ matrix.python-version }}
                 allow-prereleases: true
@@ -61,7 +61,7 @@
                 variant: [ "integration-dind", "integration-dind-ssl", 
"integration-dind-ssh" ]
 
         steps:
-            - uses: actions/checkout@v4
+            - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 
# v4.3.1
               with:
                 fetch-depth: 0
                 fetch-tags: true
@@ -70,3 +70,16 @@
                   docker logout
                   rm -rf ~/.docker
                   make ${{ matrix.variant }}
+
+    arm64-integration-tests:
+        runs-on: ubuntu-24.04-arm
+        steps:
+            - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 
# v4.3.1
+              with:
+                fetch-depth: 0
+                fetch-tags: true
+            - name: make integration-dind
+              run: |
+                  docker logout
+                  rm -rf ~/.docker
+                  make integration-dind
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/.github/workflows/release.yml 
new/docker-py-7.2.0/.github/workflows/release.yml
--- old/docker-py-7.1.0/.github/workflows/release.yml   2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/.github/workflows/release.yml   2026-07-09 
15:51:20.000000000 +0200
@@ -20,9 +20,9 @@
   publish:
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # 
v4.3.1
 
-      - uses: actions/setup-python@v5
+      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # 
v5.6.0
         with:
           python-version: '3.x'
 
@@ -36,13 +36,13 @@
           SETUPTOOLS_SCM_PRETEND_VERSION: ${{ inputs.tag }}
 
       - name: Publish to PyPI
-        uses: pypa/gh-action-pypi-publish@release/v1
+        uses: 
pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
         if: '! inputs.dry-run'
         with:
           password: ${{ secrets.PYPI_API_TOKEN }}
 
       - name: Create GitHub release
-        uses: ncipollo/release-action@v1
+        uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd 
# v1.21.0
         if: '! inputs.dry-run'
         with:
           artifacts: "dist/*"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/Dockerfile 
new/docker-py-7.2.0/Dockerfile
--- old/docker-py-7.1.0/Dockerfile      2024-05-23 13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/Dockerfile      2026-07-09 15:51:20.000000000 +0200
@@ -6,7 +6,7 @@
 WORKDIR /src
 COPY . .
 
-ARG VERSION
+ARG VERSION=0.0.0.dev0
 RUN --mount=type=cache,target=/cache/pip \
     PIP_CACHE_DIR=/cache/pip \
     SETUPTOOLS_SCM_PRETEND_VERSION=${VERSION} \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/Dockerfile-docs 
new/docker-py-7.2.0/Dockerfile-docs
--- old/docker-py-7.1.0/Dockerfile-docs 2024-05-23 13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/Dockerfile-docs 2026-07-09 15:51:20.000000000 +0200
@@ -13,7 +13,7 @@
 WORKDIR /src
 COPY . .
 
-ARG VERSION
+ARG VERSION=0.0.0.dev0
 RUN --mount=type=cache,target=/cache/pip \
     PIP_CACHE_DIR=/cache/pip \
     SETUPTOOLS_SCM_PRETEND_VERSION=${VERSION} \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/Makefile new/docker-py-7.2.0/Makefile
--- old/docker-py-7.1.0/Makefile        2024-05-23 13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/Makefile        2026-07-09 15:51:20.000000000 +0200
@@ -1,5 +1,5 @@
-TEST_API_VERSION ?= 1.44
-TEST_ENGINE_VERSION ?= 25.0
+TEST_API_VERSION ?= 1.45
+TEST_ENGINE_VERSION ?= 26.1
 
 ifeq ($(OS),Windows_NT)
     PLATFORM := Windows
@@ -13,7 +13,7 @@
 
 SETUPTOOLS_SCM_PRETEND_VERSION_DOCKER ?= $(shell git describe --match '[0-9]*' 
--dirty='.m' --always --tags 2>/dev/null | sed -r 's/-([0-9]+)/.dev\1/' | sed 
's/-/+/')
 ifeq ($(SETUPTOOLS_SCM_PRETEND_VERSION_DOCKER),)
-       SETUPTOOLS_SCM_PRETEND_VERSION_DOCKER = "dev"
+       SETUPTOOLS_SCM_PRETEND_VERSION_DOCKER = "0.0.0.dev0"
 endif
 
 .PHONY: all
@@ -33,7 +33,7 @@
                --build-arg VERSION=${SETUPTOOLS_SCM_PRETEND_VERSION_DOCKER} \
                --build-arg ENGINE_VERSION=${TEST_ENGINE_VERSION} \
                --build-arg API_VERSION=${TEST_API_VERSION} \
-               --build-arg APT_MIRROR .
+               .
 
 .PHONY: build
 build:
@@ -42,7 +42,7 @@
                -t docker-sdk-python3 \
                -f tests/Dockerfile \
                --build-arg VERSION=${SETUPTOOLS_SCM_PRETEND_VERSION_DOCKER} \
-               --build-arg APT_MIRROR .
+               .
 
 .PHONY: build-docs
 build-docs:
@@ -77,9 +77,6 @@
        docker network inspect dpy-tests || docker network create dpy-tests
 
 .PHONY: integration-dind
-integration-dind: integration-dind
-
-.PHONY: integration-dind
 integration-dind: build setup-network
        docker rm -vf dpy-dind || :
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docker/__init__.py 
new/docker-py-7.2.0/docker/__init__.py
--- old/docker-py-7.1.0/docker/__init__.py      2024-05-23 13:09:32.000000000 
+0200
+++ new/docker-py-7.2.0/docker/__init__.py      2026-07-09 15:51:20.000000000 
+0200
@@ -1,5 +1,5 @@
 from .api import APIClient
-from .client import DockerClient, from_env
+from .client import DockerClient, from_context, from_env
 from .context import Context, ContextAPI
 from .tls import TLSConfig
 from .version import __version__
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docker/client.py 
new/docker-py-7.2.0/docker/client.py
--- old/docker-py-7.1.0/docker/client.py        2024-05-23 13:09:32.000000000 
+0200
+++ new/docker-py-7.2.0/docker/client.py        2026-07-09 15:51:20.000000000 
+0200
@@ -1,5 +1,8 @@
+import os
+
 from .api.client import APIClient
 from .constants import DEFAULT_MAX_POOL_SIZE, DEFAULT_TIMEOUT_SECONDS
+from .context import ContextAPI
 from .models.configs import ConfigCollection
 from .models.containers import ContainerCollection
 from .models.images import ImageCollection
@@ -78,6 +81,10 @@
             use_ssh_client (bool): If set to `True`, an ssh connection is
                 made via shelling out to the ssh client. Ensure the ssh
                 client is installed and configured on the host.
+            use_context (bool): If ``True`` (the default), fall back to the
+                current Docker CLI context (``~/.docker/config.json`` /
+                ``DOCKER_CONTEXT``) when ``DOCKER_HOST`` is not set. This
+                allows the client to talk to Docker Desktop out of the box.
 
         Example:
 
@@ -91,12 +98,66 @@
         max_pool_size = kwargs.pop('max_pool_size', DEFAULT_MAX_POOL_SIZE)
         version = kwargs.pop('version', None)
         use_ssh_client = kwargs.pop('use_ssh_client', False)
+        use_context = kwargs.pop('use_context', True)
+        environment = kwargs.get('environment') or os.environ
+
+        params = kwargs_from_env(**kwargs)
+        if use_context and 'base_url' not in params:
+            for k, v in ContextAPI.kwargs_from_context(
+                    environment=environment).items():
+                params.setdefault(k, v)
+
+        return cls(
+            timeout=timeout,
+            max_pool_size=max_pool_size,
+            version=version,
+            use_ssh_client=use_ssh_client,
+            **params,
+        )
+
+    @classmethod
+    def from_context(cls, name=None, **kwargs):
+        """
+        Return a client configured from a Docker CLI context.
+
+        With no ``name``, resolves the current context the same way the
+        Docker CLI does: ``DOCKER_CONTEXT`` env var, then the
+        ``currentContext`` field in ``~/.docker/config.json``, falling back
+        to the built-in ``default`` context. On a machine with Docker
+        Desktop installed this typically resolves to ``desktop-linux``.
+
+        Args:
+            name (str): Name of the context to load. ``None`` (the default)
+                means "use the current context".
+            version (str): The version of the API to use. Set to ``auto`` to
+                automatically detect the server's version.
+            timeout (int): Default timeout for API calls, in seconds.
+            max_pool_size (int): The maximum number of connections to save in
+                the pool.
+            use_ssh_client (bool): If ``True``, shell out to the ssh client
+                for ssh:// contexts.
+
+        Example:
+
+            >>> import docker
+            >>> client = docker.DockerClient.from_context()
+            >>> # or, pick a specific context:
+            >>> client = docker.DockerClient.from_context('desktop-linux')
+        """
+        timeout = kwargs.pop('timeout', DEFAULT_TIMEOUT_SECONDS)
+        max_pool_size = kwargs.pop('max_pool_size', DEFAULT_MAX_POOL_SIZE)
+        version = kwargs.pop('version', None)
+        use_ssh_client = kwargs.pop('use_ssh_client', False)
+
+        params = ContextAPI.kwargs_from_context(name=name)
+        params.update(kwargs)
+
         return cls(
             timeout=timeout,
             max_pool_size=max_pool_size,
             version=version,
             use_ssh_client=use_ssh_client,
-            **kwargs_from_env(**kwargs)
+            **params,
         )
 
     # Resources
@@ -220,3 +281,4 @@
 
 
 from_env = DockerClient.from_env
+from_context = DockerClient.from_context
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docker/constants.py 
new/docker-py-7.2.0/docker/constants.py
--- old/docker-py-7.1.0/docker/constants.py     2024-05-23 13:09:32.000000000 
+0200
+++ new/docker-py-7.2.0/docker/constants.py     2026-07-09 15:51:20.000000000 
+0200
@@ -2,7 +2,7 @@
 
 from .version import __version__
 
-DEFAULT_DOCKER_API_VERSION = '1.44'
+DEFAULT_DOCKER_API_VERSION = '1.45'
 MINIMUM_DOCKER_API_VERSION = '1.24'
 DEFAULT_TIMEOUT_SECONDS = 60
 STREAM_HEADER_SIZE_BYTES = 8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docker/context/api.py 
new/docker-py-7.2.0/docker/context/api.py
--- old/docker-py-7.1.0/docker/context/api.py   2024-05-23 13:09:32.000000000 
+0200
+++ new/docker-py-7.2.0/docker/context/api.py   2026-07-09 15:51:20.000000000 
+0200
@@ -133,6 +133,33 @@
         return cls.get_context()
 
     @classmethod
+    def kwargs_from_context(cls, name=None, environment=None):
+        """Build ``base_url`` / ``tls`` kwargs from a Docker CLI context.
+
+        Mirrors the Docker CLI: if ``name`` is not given, honours the
+        ``DOCKER_CONTEXT`` env var, then the ``currentContext`` field in
+        ``~/.docker/config.json``, defaulting to the built-in ``default``
+        context (local socket / named pipe). On a host with Docker Desktop
+        this resolves to the ``desktop-linux`` (or equivalent) context, so
+        client construction targets Docker Desktop out of the box.
+        """
+        if environment is None:
+            environment = os.environ
+        if name is None:
+            name = environment.get("DOCKER_CONTEXT")
+        ctx = cls.get_context(name)
+        if ctx is None:
+            return {}
+        host = ctx.Host
+        if not host:
+            return {}
+        params = {"base_url": host}
+        tls_cfg = ctx.TLSConfig
+        if tls_cfg is not None:
+            params["tls"] = tls_cfg
+        return params
+
+    @classmethod
     def set_current_context(cls, name="default"):
         ctx = cls.get_context(name)
         if not ctx:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docker/models/containers.py 
new/docker-py-7.2.0/docker/models/containers.py
--- old/docker-py-7.1.0/docker/models/containers.py     2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/docker/models/containers.py     2026-07-09 
15:51:20.000000000 +0200
@@ -181,7 +181,8 @@
             user (str): User to execute command as. Default: root
             detach (bool): If true, detach from the exec command.
                 Default: False
-            stream (bool): Stream response data. Default: False
+            stream (bool): Stream response data. Ignored if ``detach`` is true.
+                Default: False
             socket (bool): Return the connection socket to allow custom
                 read/write operations. Default: False
             environment (dict or list): A dictionary or a list of strings in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docker/models/images.py 
new/docker-py-7.2.0/docker/models/images.py
--- old/docker-py-7.1.0/docker/models/images.py 2024-05-23 13:09:32.000000000 
+0200
+++ new/docker-py-7.2.0/docker/models/images.py 2026-07-09 15:51:20.000000000 
+0200
@@ -407,8 +407,8 @@
                 if match:
                     image_id = match.group(2)
                     images.append(image_id)
-            if 'error' in chunk:
-                raise ImageLoadError(chunk['error'])
+            if 'errorDetail' in chunk:
+                raise ImageLoadError(chunk['errorDetail']['message'])
 
         return [self.get(i) for i in images]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docker/types/services.py 
new/docker-py-7.2.0/docker/types/services.py
--- old/docker-py-7.1.0/docker/types/services.py        2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/docker/types/services.py        2026-07-09 
15:51:20.000000000 +0200
@@ -242,6 +242,7 @@
           for the ``volume`` type.
         driver_config (DriverConfig): Volume driver configuration. Only valid
           for the ``volume`` type.
+        subpath (str): Path inside a volume to mount instead of the volume 
root.
         tmpfs_size (int or string): The size for the tmpfs mount in bytes.
         tmpfs_mode (int): The permission mode for the tmpfs mount.
     """
@@ -249,7 +250,7 @@
     def __init__(self, target, source, type='volume', read_only=False,
                  consistency=None, propagation=None, no_copy=False,
                  labels=None, driver_config=None, tmpfs_size=None,
-                 tmpfs_mode=None):
+                 tmpfs_mode=None, subpath=None):
         self['Target'] = target
         self['Source'] = source
         if type not in ('bind', 'volume', 'tmpfs', 'npipe'):
@@ -267,7 +268,7 @@
                 self['BindOptions'] = {
                     'Propagation': propagation
                 }
-            if any([labels, driver_config, no_copy, tmpfs_size, tmpfs_mode]):
+            if any([labels, driver_config, no_copy, tmpfs_size, tmpfs_mode, 
subpath]):
                 raise errors.InvalidArgument(
                     'Incompatible options have been provided for the bind '
                     'type mount.'
@@ -280,6 +281,8 @@
                 volume_opts['Labels'] = labels
             if driver_config:
                 volume_opts['DriverConfig'] = driver_config
+            if subpath:
+                volume_opts['Subpath'] = subpath
             if volume_opts:
                 self['VolumeOptions'] = volume_opts
             if any([propagation, tmpfs_size, tmpfs_mode]):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/docs/change-log.md 
new/docker-py-7.2.0/docs/change-log.md
--- old/docker-py-7.1.0/docs/change-log.md      2024-05-23 13:09:32.000000000 
+0200
+++ new/docker-py-7.2.0/docs/change-log.md      2026-07-09 15:51:20.000000000 
+0200
@@ -1,6 +1,29 @@
 Changelog
 ==========
 
+7.2.0
+-----
+### Upgrade Notes
+- `docker.from_env()` now honors the active Docker CLI context when 
`DOCKER_HOST` is not set.
+  - This means the client may connect to the daemon selected by 
`DOCKER_CONTEXT` or the current context in `~/.docker/config.json`, matching 
Docker CLI behavior more closely.
+  - If your application relied on the previous default connection behavior, 
set `DOCKER_HOST` explicitly, set `DOCKER_CONTEXT=default`, or pass 
`use_context=False` to `DockerClient.from_env()`.
+- Added `docker.from_context()` / `DockerClient.from_context()` for explicitly 
creating a client from a Docker CLI context.
+
+### Features
+- Added support for Docker contexts when creating the default client
+- Added subpath support for volumes
+
+### Bugfixes
+- Fixed `exec_run` documentation for the `stream` parameter
+- Fixed image loading to avoid depending on the deprecated `JSONMessage.error` 
field
+- Preserved the rotated unlock key in swarm integration tests
+- Fixed SSL certificate generation in tests
+- Fixed IPv6 integration tests by explicitly enabling IPv6 where required
+
+## Miscellaneous
+- Updated tests for newer Docker Engine behavior
+- CI and build updates
+
 7.1.0
 -----
 ### Upgrade Notes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/Dockerfile 
new/docker-py-7.2.0/tests/Dockerfile
--- old/docker-py-7.1.0/tests/Dockerfile        2024-05-23 13:09:32.000000000 
+0200
+++ new/docker-py-7.2.0/tests/Dockerfile        2026-07-09 15:51:20.000000000 
+0200
@@ -4,7 +4,7 @@
 FROM python:${PYTHON_VERSION}
 
 RUN apt-get update && apt-get -y install --no-install-recommends \
-    gnupg2 \
+    gnupg \
     pass
 
 # Add SSH keys and set permissions
@@ -14,10 +14,10 @@
 RUN chmod -R 600 /root/.ssh
 
 COPY ./tests/gpg-keys /gpg-keys
-RUN gpg2 --import gpg-keys/secret
-RUN gpg2 --import-ownertrust gpg-keys/ownertrust
-RUN yes | pass init $(gpg2 --no-auto-check-trustdb --list-secret-key | awk 
'/^sec/{getline; $1=$1; print}')
-RUN gpg2 --check-trustdb
+RUN gpg --batch --pinentry-mode loopback --passphrase '' --import 
gpg-keys/secret
+RUN gpg --import-ownertrust gpg-keys/ownertrust
+RUN yes | pass init $(gpg --no-auto-check-trustdb --list-secret-key | awk 
'/^sec/{getline; $1=$1; print}')
+RUN gpg --check-trustdb
 ARG CREDSTORE_VERSION=v0.6.3
 RUN curl -sSL -o /opt/docker-credential-pass.tar.gz \
     
https://github.com/docker/docker-credential-helpers/releases/download/$CREDSTORE_VERSION/docker-credential-pass-$CREDSTORE_VERSION-amd64.tar.gz
 && \
@@ -28,7 +28,7 @@
 WORKDIR /src
 COPY . .
 
-ARG VERSION
+ARG VERSION=0.0.0.dev0
 RUN --mount=type=cache,target=/cache/pip \
     PIP_CACHE_DIR=/cache/pip \
     SETUPTOOLS_SCM_PRETEND_VERSION=${VERSION} \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/Dockerfile-dind-certs 
new/docker-py-7.2.0/tests/Dockerfile-dind-certs
--- old/docker-py-7.1.0/tests/Dockerfile-dind-certs     2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/Dockerfile-dind-certs     2026-07-09 
15:51:20.000000000 +0200
@@ -7,17 +7,46 @@
 VOLUME /certs
 
 WORKDIR /tmp/certs
+
+# ---- CA (with proper v3_ca) ----
 RUN openssl genrsa -aes256 -passout pass:foobar -out ca-key.pem 4096
-RUN echo "[req]\nprompt=no\ndistinguished_name = 
req_distinguished_name\n[req_distinguished_name]\ncountryName=AU" > /tmp/config
-RUN openssl req -new -x509 -passin pass:foobar -config /tmp/config -days 365 
-key ca-key.pem -sha256 -out ca.pem
-RUN openssl genrsa -out server-key.pem -passout pass:foobar 4096
+COPY <<'EOF' /tmp/ca.cnf
+[req]
+prompt = no
+distinguished_name = req_distinguished_name
+x509_extensions = v3_ca
+
+[req_distinguished_name]
+countryName = AU
+
+[v3_ca]
+basicConstraints = critical, CA:TRUE
+keyUsage = critical, keyCertSign, cRLSign
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer
+EOF
+RUN openssl req -new -x509 -passin pass:foobar -config /tmp/ca.cnf -days 365 
-key ca-key.pem -sha256 -out ca.pem
+
+# ---- Server cert (SAN + KU/EKU) ----
+RUN openssl genrsa -out server-key.pem 4096
 RUN openssl req -subj "/CN=docker" -sha256 -new -key server-key.pem -out 
server.csr
-RUN echo subjectAltName = DNS:docker,DNS:localhost > extfile.cnf
-RUN openssl x509 -req -days 365 -passin pass:foobar -sha256 -in server.csr -CA 
ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile 
extfile.cnf
+COPY <<'EOF' /tmp/server-ext.cnf
+basicConstraints = CA:FALSE
+keyUsage = critical, digitalSignature, keyEncipherment
+extendedKeyUsage = serverAuth
+subjectAltName = DNS:docker, DNS:localhost
+EOF
+RUN openssl x509 -req -days 365 -passin pass:foobar -sha256 -in server.csr -CA 
ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile 
/tmp/server-ext.cnf
+
+# ---- Client cert (KU/EKU) ----
 RUN openssl genrsa -out key.pem 4096
 RUN openssl req -passin pass:foobar -subj '/CN=client' -new -key key.pem -out 
client.csr
-RUN echo extendedKeyUsage = clientAuth > extfile.cnf
-RUN openssl x509 -req -passin pass:foobar -days 365 -sha256 -in client.csr -CA 
ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf
+COPY <<'EOF' /tmp/client-ext.cnf
+basicConstraints = CA:FALSE
+keyUsage = critical, digitalSignature
+extendedKeyUsage = clientAuth
+EOF
+RUN openssl x509 -req -passin pass:foobar -days 365 -sha256 -in client.csr -CA 
ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile 
/tmp/client-ext.cnf
 RUN chmod -v 0400 ca-key.pem key.pem server-key.pem
 RUN chmod -v 0444 ca.pem server-cert.pem cert.pem
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/Dockerfile-ssh-dind 
new/docker-py-7.2.0/tests/Dockerfile-ssh-dind
--- old/docker-py-7.1.0/tests/Dockerfile-ssh-dind       2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/Dockerfile-ssh-dind       2026-07-09 
15:51:20.000000000 +0200
@@ -1,7 +1,7 @@
 # syntax=docker/dockerfile:1
 
-ARG API_VERSION=1.44
-ARG ENGINE_VERSION=25.0
+ARG API_VERSION=1.45
+ARG ENGINE_VERSION=26.1
 
 FROM docker:${ENGINE_VERSION}-dind
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/gpg-keys/ownertrust 
new/docker-py-7.2.0/tests/gpg-keys/ownertrust
--- old/docker-py-7.1.0/tests/gpg-keys/ownertrust       2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/gpg-keys/ownertrust       2026-07-09 
15:51:20.000000000 +0200
@@ -1,3 +1,3 @@
-# List of assigned trustvalues, created Wed 25 Apr 2018 01:28:17 PM PDT
+# List of assigned trustvalues, created Wed May 20 14:53:47 2026 UTC
 # (Use "gpg --import-ownertrust" to restore them)
-9781B87DAB042E6FD51388A5464ED987A7B21401:6:
+713D05DCAFD74767847E4BBBE596CB09C46DE21F:6:
Binary files old/docker-py-7.1.0/tests/gpg-keys/secret and 
new/docker-py-7.2.0/tests/gpg-keys/secret differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/integration/api_build_test.py 
new/docker-py-7.2.0/tests/integration/api_build_test.py
--- old/docker-py-7.1.0/tests/integration/api_build_test.py     2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/api_build_test.py     2026-07-09 
15:51:20.000000000 +0200
@@ -275,7 +275,7 @@
             pass
 
         info = self.client.inspect_image('build1')
-        assert not info['Config']['OnBuild']
+        assert 'OnBuild' not in info['Config'] or not info['Config']['OnBuild']
 
     @requires_api_version('1.25')
     def test_build_with_network_mode(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/docker-py-7.1.0/tests/integration/api_container_test.py 
new/docker-py-7.2.0/tests/integration/api_container_test.py
--- old/docker-py-7.1.0/tests/integration/api_container_test.py 2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/api_container_test.py 2026-07-09 
15:51:20.000000000 +0200
@@ -620,6 +620,56 @@
         assert mount['Source'] == mount_data['Name']
         assert mount_data['RW'] is True
 
+    @requires_api_version('1.45')
+    def test_create_with_subpath_volume_mount(self):
+        source_volume = helpers.random_name()
+        self.client.create_volume(name=source_volume)
+
+        setup_container = None
+        test_container = None
+
+
+        # Create a file structure in the volume to test with
+        setup_container = self.client.create_container(
+            TEST_IMG,
+            [
+                "sh",
+                "-c",
+                'mkdir -p /vol/subdir && echo "test content" > 
/vol/subdir/testfile.txt',
+            ],
+            host_config=self.client.create_host_config(
+                binds=[f"{source_volume}:/vol"]
+            ),
+        )
+        self.client.start(setup_container)
+        self.client.wait(setup_container)
+
+        # Now test with subpath
+        mount = docker.types.Mount(
+            type="volume",
+            source=source_volume,
+            target=self.mount_dest,
+            read_only=True,
+            subpath="subdir",
+        )
+
+
+        host_config = self.client.create_host_config(mounts=[mount])
+        test_container = self.client.create_container(
+            TEST_IMG,
+            ["cat", os.path.join(self.mount_dest, "testfile.txt")],
+            host_config=host_config,
+        )
+
+        self.client.start(test_container)
+        self.client.wait(test_container)  # Wait for container to finish
+        output = self.client.logs(test_container).decode("utf-8").strip()
+
+        # If the subpath feature is working, we should be able to see the 
content
+        # of the file in the subdir
+        assert output == "test content"
+
+
     def check_container_data(self, inspect_data, rw, propagation='rprivate'):
         assert 'Mounts' in inspect_data
         filtered = list(filter(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/docker-py-7.1.0/tests/integration/api_network_test.py 
new/docker-py-7.2.0/tests/integration/api_network_test.py
--- old/docker-py-7.1.0/tests/integration/api_network_test.py   2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/api_network_test.py   2026-07-09 
15:51:20.000000000 +0200
@@ -232,6 +232,7 @@
     @requires_api_version('1.22')
     def test_create_with_ipv6_address(self):
         net_name, net_id = self.create_network(
+            enable_ipv6=True,
             ipam=IPAMConfig(
                 driver='default',
                 pool_configs=[IPAMPool(subnet="2001:389::/64")],
@@ -384,6 +385,7 @@
     @requires_api_version('1.22')
     def test_connect_with_ipv6_address(self):
         net_name, net_id = self.create_network(
+            enable_ipv6=True,
             ipam=IPAMConfig(
                 driver='default',
                 pool_configs=[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/integration/api_plugin_test.py 
new/docker-py-7.2.0/tests/integration/api_plugin_test.py
--- old/docker-py-7.1.0/tests/integration/api_plugin_test.py    2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/api_plugin_test.py    2026-07-09 
15:51:20.000000000 +0200
@@ -1,4 +1,5 @@
 import os
+import platform
 
 import pytest
 
@@ -11,6 +12,10 @@
 
 
 @requires_api_version('1.25')
[email protected](
+    platform.machine().lower() not in ('amd64', 'x86_64'),
+    reason='vieux/sshfs plugin is only supported on amd64',
+)
 class PluginTest(BaseAPIIntegrationTest):
     @classmethod
     def teardown_class(cls):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/docker-py-7.1.0/tests/integration/api_service_test.py 
new/docker-py-7.2.0/tests/integration/api_service_test.py
--- old/docker-py-7.1.0/tests/integration/api_service_test.py   2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/api_service_test.py   2026-07-09 
15:51:20.000000000 +0200
@@ -5,7 +5,7 @@
 
 import docker
 
-from ..helpers import force_leave_swarm, requires_api_version, 
requires_experimental
+from ..helpers import force_leave_swarm, requires_api_version
 from .base import TEST_IMG, BaseAPIIntegrationTest
 
 
@@ -140,8 +140,7 @@
         assert len(services) == 1
         assert services[0]['ID'] == svc_id['ID']
 
-    @requires_api_version('1.25')
-    @requires_experimental(until='1.29')
+    @requires_api_version('1.29')
     def test_service_logs(self):
         name, svc_id = self.create_simple_service()
         assert self.get_service_container(name, include_stopped=True)
@@ -368,16 +367,18 @@
         )
         self.tmp_networks.append(net2['Id'])
         container_spec = docker.types.ContainerSpec(TEST_IMG, ['true'])
-        task_tmpl = docker.types.TaskTemplate(container_spec)
-        name = self.get_service_name()
-        svc_id = self.client.create_service(
-            task_tmpl, name=name, networks=[
+        task_tmpl = docker.types.TaskTemplate(
+            container_spec, networks=[
                 'dockerpytest_1', {'Target': 'dockerpytest_2'}
             ]
         )
+        name = self.get_service_name()
+        svc_id = self.client.create_service(
+            task_tmpl, name=name
+        )
         svc_info = self.client.inspect_service(svc_id)
-        assert 'Networks' in svc_info['Spec']
-        assert svc_info['Spec']['Networks'] == [
+        assert 'Networks' in svc_info['Spec']['TaskTemplate']
+        assert svc_info['Spec']['TaskTemplate']['Networks'] == [
             {'Target': net1['Id']}, {'Target': net2['Id']}
         ]
 
@@ -1117,16 +1118,18 @@
         )
         self.tmp_networks.append(net2['Id'])
         container_spec = docker.types.ContainerSpec(TEST_IMG, ['true'])
-        task_tmpl = docker.types.TaskTemplate(container_spec)
-        name = self.get_service_name()
-        svc_id = self.client.create_service(
-            task_tmpl, name=name, networks=[
+        task_tmpl = docker.types.TaskTemplate(
+            container_spec, networks=[
                 'dockerpytest_1', {'Target': 'dockerpytest_2'}
             ]
         )
+        name = self.get_service_name()
+        svc_id = self.client.create_service(
+            task_tmpl, name=name
+        )
         svc_info = self.client.inspect_service(svc_id)
-        assert 'Networks' in svc_info['Spec']
-        assert svc_info['Spec']['Networks'] == [
+        assert 'Networks' in svc_info['Spec']['TaskTemplate']
+        assert svc_info['Spec']['TaskTemplate']['Networks'] == [
             {'Target': net1['Id']}, {'Target': net2['Id']}
         ]
 
@@ -1144,8 +1147,11 @@
             {'Target': net1['Id']}, {'Target': net2['Id']}
         ]
 
+        task_tmpl = docker.types.TaskTemplate(
+            container_spec, networks=[net1['Id']]
+        )
         self._update_service(
-            svc_id, name, new_index, networks=[net1['Id']],
+            svc_id, name, new_index, task_tmpl,
             fetch_current_spec=True
         )
         svc_info = self.client.inspect_service(svc_id)
@@ -1314,7 +1320,6 @@
         container_spec = docker.types.ContainerSpec(
             'busybox', ['echo', 'hello']
         )
-        task_tmpl = docker.types.TaskTemplate(container_spec)
         net1 = self.client.create_network(
             self.get_service_name(), driver='overlay',
             ipam={'Driver': 'default'}
@@ -1325,22 +1330,27 @@
             ipam={'Driver': 'default'}
         )
         self.tmp_networks.append(net2['Id'])
+        task_tmpl = docker.types.TaskTemplate(
+            container_spec, networks=[net1['Id']]
+        )
         name = self.get_service_name()
         svc_id = self.client.create_service(
-            task_tmpl, name=name, networks=[net1['Id']]
+            task_tmpl, name=name
         )
         svc_info = self.client.inspect_service(svc_id)
-        assert 'Networks' in svc_info['Spec']
-        assert len(svc_info['Spec']['Networks']) > 0
-        assert svc_info['Spec']['Networks'][0]['Target'] == net1['Id']
+        assert 'Networks' in svc_info['Spec']['TaskTemplate']
+        assert len(svc_info['Spec']['TaskTemplate']['Networks']) > 0
+        assert svc_info['Spec']['TaskTemplate']['Networks'][0]['Target'] == 
net1['Id']
 
         svc_info = self.client.inspect_service(svc_id)
         version_index = svc_info['Version']['Index']
 
-        task_tmpl = docker.types.TaskTemplate(container_spec)
+        task_tmpl = docker.types.TaskTemplate(
+            container_spec, networks=[net2['Id']]
+        )
         self._update_service(
             svc_id, name, version_index, task_tmpl, name=name,
-            networks=[net2['Id']], fetch_current_spec=True
+            fetch_current_spec=True
         )
         svc_info = self.client.inspect_service(svc_id)
         task_template = svc_info['Spec']['TaskTemplate']
@@ -1352,8 +1362,11 @@
         new_index = svc_info['Version']['Index']
         assert new_index > version_index
 
+        task_tmpl = docker.types.TaskTemplate(
+            container_spec, networks=[net1['Id']]
+        )
         self._update_service(
-            svc_id, name, new_index, name=name, networks=[net1['Id']],
+            svc_id, name, new_index, task_tmpl, name=name,
             fetch_current_spec=True
         )
         svc_info = self.client.inspect_service(svc_id)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/integration/api_swarm_test.py 
new/docker-py-7.2.0/tests/integration/api_swarm_test.py
--- old/docker-py-7.1.0/tests/integration/api_swarm_test.py     2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/api_swarm_test.py     2026-07-09 
15:51:20.000000000 +0200
@@ -249,6 +249,7 @@
             rotate_manager_unlock_key=True
         )
         key_2 = self.client.get_unlock_key()
+        self._unlock_key = key_2
         assert key_1['UnlockKey'] != key_2['UnlockKey']
 
     @requires_api_version('1.30')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/integration/api_volume_test.py 
new/docker-py-7.2.0/tests/integration/api_volume_test.py
--- old/docker-py-7.1.0/tests/integration/api_volume_test.py    2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/api_volume_test.py    2026-07-09 
15:51:20.000000000 +0200
@@ -17,10 +17,16 @@
         assert result['Driver'] == 'local'
 
     def test_create_volume_invalid_driver(self):
-        driver_name = 'invalid.driver'
+        # special name to avoid exponential timeout loop
+        # 
https://github.com/moby/moby/blob/9e00a63d65434cdedc444e79a2b33a7c202b10d8/pkg/plugins/client.go#L253-L254
+        driver_name = 'this-plugin-does-not-exist'
 
-        with pytest.raises(docker.errors.NotFound):
+        with pytest.raises(docker.errors.APIError) as cm:
             self.client.create_volume('perfectcherryblossom', driver_name)
+            assert (
+                cm.value.response.status_code == 404 or
+                cm.value.response.status_code == 400
+            )
 
     def test_list_volumes(self):
         name = 'imperishablenight'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/docker-py-7.1.0/tests/integration/credentials/create_gpg_key.sh 
new/docker-py-7.2.0/tests/integration/credentials/create_gpg_key.sh
--- old/docker-py-7.1.0/tests/integration/credentials/create_gpg_key.sh 
2024-05-23 13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/credentials/create_gpg_key.sh 
2026-07-09 15:51:20.000000000 +0200
@@ -1,12 +1,4 @@
 #!/usr/bin/sh
-haveged
-gpg --batch --gen-key <<-EOF
-%echo Generating a standard key
-Key-Type: DSA
-Key-Length: 1024
-Subkey-Type: ELG-E
-Subkey-Length: 1024
-Name-Real: Sakuya Izayoi
-Name-Email: [email protected]
-Expire-Date: 0
-EOF
\ No newline at end of file
+gpg --batch --passphrase '' --quick-gen-key 'Example User <[email protected]>' 
ed25519 cert 0
+FINGERPRINT=$(gpg --no-auto-check-trustdb --list-secret-keys --with-colons | 
awk -F: '/^fpr/{print $10; exit}')
+gpg --batch --passphrase '' --quick-add-key "$FINGERPRINT" cv25519 encr 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/docker-py-7.1.0/tests/integration/credentials/store_test.py 
new/docker-py-7.2.0/tests/integration/credentials/store_test.py
--- old/docker-py-7.1.0/tests/integration/credentials/store_test.py     
2024-05-23 13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/credentials/store_test.py     
2026-07-09 15:51:20.000000000 +0200
@@ -1,4 +1,5 @@
 import os
+import platform
 import random
 import shutil
 import sys
@@ -14,6 +15,10 @@
 )
 
 
[email protected](
+    platform.machine().lower() not in ('amd64', 'x86_64'),
+    reason='docker-credential-pass test helper is only supported on amd64',
+)
 class TestStore:
     def teardown_method(self):
         for server in self.tmp_keys:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/docker-py-7.1.0/tests/integration/models_containers_test.py 
new/docker-py-7.2.0/tests/integration/models_containers_test.py
--- old/docker-py-7.1.0/tests/integration/models_containers_test.py     
2024-05-23 13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/integration/models_containers_test.py     
2026-07-09 15:51:20.000000000 +0200
@@ -131,10 +131,9 @@
         assert 'NetworkSettings' in attrs
         assert 'Networks' in attrs['NetworkSettings']
         assert list(attrs['NetworkSettings']['Networks'].keys()) == [net_name]
-        # Expect Aliases to list 'test_alias' and the container's short-id.
-        # In API version 1.45, the short-id will be removed.
+        # Aliases no longer include the container's short-id in API v1.45.
         assert attrs['NetworkSettings']['Networks'][net_name]['Aliases'] \
-               == [test_alias, attrs['Id'][:12]]
+               == [test_alias]
         assert attrs['NetworkSettings']['Networks'][net_name]['DriverOpts'] \
                == test_driver_opt
 
@@ -191,9 +190,9 @@
         assert 'NetworkSettings' in attrs
         assert 'Networks' in attrs['NetworkSettings']
         assert list(attrs['NetworkSettings']['Networks'].keys()) == [net_name]
-        # Aliases should include the container's short-id (but it will be 
removed
-        # in API v1.45).
-        assert attrs['NetworkSettings']['Networks'][net_name]['Aliases'] == 
[attrs["Id"][:12]]
+        # Aliases no longer include the container's short-id in API v1.45.
+        assert (attrs['NetworkSettings']['Networks'][net_name]['Aliases']
+                is None)
         assert (attrs['NetworkSettings']['Networks'][net_name]['DriverOpts']
                 is None)
 
@@ -353,14 +352,26 @@
         assert exec_output[0] == 0
         assert exec_output[1] == b"hello\n"
 
+    def test_exec_run_error_code_from_exec(self):
+        client = docker.from_env(version=TEST_API_VERSION)
+        container = client.containers.run(
+            "alpine", "sh -c 'sleep 20'", detach=True
+        )
+        self.tmp_containers.append(container.id)
+        exec_output = container.exec_run("sh -c 'exit 42'")
+        assert exec_output[0] == 42
+
     def test_exec_run_failed(self):
         client = docker.from_env(version=TEST_API_VERSION)
         container = client.containers.run(
             "alpine", "sh -c 'sleep 60'", detach=True
         )
         self.tmp_containers.append(container.id)
-        exec_output = container.exec_run("docker ps")
-        assert exec_output[0] == 126
+        exec_output = container.exec_run("non-existent")
+        # older versions of docker return `126` in the case that an exec cannot
+        # be started due to a missing executable. We're fixing this for the
+        # future, so accept both for now.
+        assert exec_output[0] == 127 or exec_output[0] == 126
 
     def test_kill(self):
         client = docker.from_env(version=TEST_API_VERSION)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/ssh/api_build_test.py 
new/docker-py-7.2.0/tests/ssh/api_build_test.py
--- old/docker-py-7.1.0/tests/ssh/api_build_test.py     2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/ssh/api_build_test.py     2026-07-09 
15:51:20.000000000 +0200
@@ -266,7 +266,7 @@
             pass
 
         info = self.client.inspect_image('build1')
-        assert not info['Config']['OnBuild']
+        assert 'OnBuild' not in info['Config'] or not info['Config']['OnBuild']
 
     @requires_api_version('1.25')
     def test_build_with_network_mode(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/docker-py-7.1.0/tests/unit/client_test.py 
new/docker-py-7.2.0/tests/unit/client_test.py
--- old/docker-py-7.1.0/tests/unit/client_test.py       2024-05-23 
13:09:32.000000000 +0200
+++ new/docker-py-7.2.0/tests/unit/client_test.py       2026-07-09 
15:51:20.000000000 +0200
@@ -9,7 +9,9 @@
 from docker.constants import (
     DEFAULT_DOCKER_API_VERSION,
     DEFAULT_MAX_POOL_SIZE,
+    DEFAULT_NPIPE,
     DEFAULT_TIMEOUT_SECONDS,
+    DEFAULT_UNIX_SOCKET,
     IS_WINDOWS_PLATFORM,
 )
 from docker.utils import kwargs_from_env
@@ -188,11 +190,14 @@
         assert client.api.timeout == DEFAULT_TIMEOUT_SECONDS
 
     @pytest.mark.skipif(
-        IS_WINDOWS_PLATFORM, reason='Unix Connection Pool only on Linux'
+        os.environ.get('DOCKER_HOST', '').startswith('tcp://') or 
IS_WINDOWS_PLATFORM,
+        reason='Requires a Unix socket'
     )
     @mock.patch("docker.transport.unixconn.UnixHTTPConnectionPool")
     def test_default_pool_size_from_env_unix(self, mock_obj):
-        client = docker.from_env(version=DEFAULT_DOCKER_API_VERSION)
+        client = docker.from_env(
+            version=DEFAULT_DOCKER_API_VERSION, use_context=False,
+        )
         mock_obj.return_value.urlopen.return_value.status = 200
         client.ping()
 
@@ -219,13 +224,15 @@
                                          )
 
     @pytest.mark.skipif(
-        IS_WINDOWS_PLATFORM, reason='Unix Connection Pool only on Linux'
+        os.environ.get('DOCKER_HOST', '').startswith('tcp://') or 
IS_WINDOWS_PLATFORM,
+        reason='Requires a Unix socket'
     )
     @mock.patch("docker.transport.unixconn.UnixHTTPConnectionPool")
     def test_pool_size_from_env_unix(self, mock_obj):
         client = docker.from_env(
             version=DEFAULT_DOCKER_API_VERSION,
-            max_pool_size=POOL_SIZE
+            max_pool_size=POOL_SIZE,
+            use_context=False,
         )
         mock_obj.return_value.urlopen.return_value.status = 200
         client.ping()
@@ -254,3 +261,87 @@
                                          60,
                                          maxsize=POOL_SIZE
                                          )
+
+
+class FromContextTest(unittest.TestCase):
+    """from_env / from_context honour Docker CLI contexts so the SDK
+    talks to Docker Desktop (or any other current context) by default."""
+
+    def setUp(self):
+        self.os_environ = os.environ.copy()
+        # Make sure DOCKER_HOST does not short-circuit the context path
+        os.environ.pop('DOCKER_HOST', None)
+        os.environ.pop('DOCKER_CONTEXT', None)
+
+    def tearDown(self):
+        os.environ.clear()
+        os.environ.update(self.os_environ)
+
+    @mock.patch('docker.client.ContextAPI.kwargs_from_context')
+    def test_from_env_uses_current_context_when_no_docker_host(
+            self, mock_ctx):
+        mock_ctx.return_value = {
+            'base_url': 'unix:///Users/me/.docker/run/docker.sock',
+        }
+        client = docker.from_env(version=DEFAULT_DOCKER_API_VERSION)
+        assert mock_ctx.called
+        # Unix socket base_urls are rewritten internally; confirm the
+        # underlying APIClient picked up the context-supplied socket.
+        assert (
+            client.api._custom_adapter.socket_path
+            == '/Users/me/.docker/run/docker.sock'
+        )
+
+    @mock.patch('docker.client.ContextAPI.kwargs_from_context')
+    def test_from_env_docker_host_overrides_context(self, mock_ctx):
+        mock_ctx.return_value = {
+            'base_url': 'unix:///Users/me/.docker/run/docker.sock',
+        }
+        os.environ['DOCKER_HOST'] = 'tcp://192.168.59.103:2375'
+        client = docker.from_env(version=DEFAULT_DOCKER_API_VERSION)
+        assert client.api.base_url == 'http://192.168.59.103:2375'
+        # When DOCKER_HOST is set we don't need the context fallback.
+        assert not mock_ctx.called
+
+    @mock.patch('docker.client.ContextAPI.kwargs_from_context')
+    def test_from_env_use_context_false_skips_context(self, mock_ctx):
+        client = docker.from_env(
+            version=DEFAULT_DOCKER_API_VERSION, use_context=False,
+        )
+        assert not mock_ctx.called
+        # Falls back to APIClient's own platform default.
+        assert client.api.base_url in (
+            'http+docker://localhost', 'http+docker://localnpipe',
+        )
+
+    @mock.patch('docker.client.ContextAPI.kwargs_from_context')
+    def test_from_context_uses_named_context(self, mock_ctx):
+        mock_ctx.return_value = {
+            'base_url': 'unix:///Users/me/.docker/run/docker.sock',
+        }
+        client = docker.from_context(
+            'desktop-linux', version=DEFAULT_DOCKER_API_VERSION,
+        )
+        mock_ctx.assert_called_once_with(name='desktop-linux')
+        assert (
+            client.api._custom_adapter.socket_path
+            == '/Users/me/.docker/run/docker.sock'
+        )
+
+    def test_kwargs_from_context_honours_docker_context_env(self):
+        from docker.context import ContextAPI
+        # No context with this name exists; helper should return {} rather
+        # than raise, leaving APIClient to use its own default.
+        params = ContextAPI.kwargs_from_context(
+            environment={'DOCKER_CONTEXT': 'does-not-exist'},
+        )
+        assert params == {}
+
+    def test_kwargs_from_context_default(self):
+        from docker.context import ContextAPI
+        params = ContextAPI.kwargs_from_context(name='default')
+        # The default context always resolves to the local socket / pipe.
+        assert 'base_url' in params
+        assert params['base_url'] in (
+            DEFAULT_UNIX_SOCKET[len('http+'):], DEFAULT_NPIPE,
+        )

Reply via email to