Skip to content

Commit 2e8f1f7

Browse files
authored
Merge pull request #1883 from docker/1761-dockerclient-pull
Properly support pulling all tags in DockerClient.images.pull
2 parents a05922e + 17aa314 commit 2e8f1f7

File tree

4 files changed

+40
-6
lines changed

4 files changed

+40
-6
lines changed

docker/models/images.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from ..api import APIClient
77
from ..errors import BuildError, ImageLoadError
8+
from ..utils import parse_repository_tag
89
from ..utils.json_stream import json_stream
910
from .resource import Collection, Model
1011

@@ -269,6 +270,8 @@ def pull(self, name, tag=None, **kwargs):
269270
"""
270271
Pull an image of the given name and return it. Similar to the
271272
``docker pull`` command.
273+
If no tag is specified, all tags from that repository will be
274+
pulled.
272275
273276
If you want to get the raw pull output, use the
274277
:py:meth:`~docker.api.image.ImageApiMixin.pull` method in the
@@ -285,18 +288,29 @@ def pull(self, name, tag=None, **kwargs):
285288
platform (str): Platform in the format ``os[/arch[/variant]]``
286289
287290
Returns:
288-
(:py:class:`Image`): The image that has been pulled.
291+
(:py:class:`Image` or list): The image that has been pulled.
292+
If no ``tag`` was specified, the method will return a list
293+
of :py:class:`Image` objects belonging to this repository.
289294
290295
Raises:
291296
:py:class:`docker.errors.APIError`
292297
If the server returns an error.
293298
294299
Example:
295300
296-
>>> image = client.images.pull('busybox')
301+
>>> # Pull the image tagged `latest` in the busybox repo
302+
>>> image = client.images.pull('busybox:latest')
303+
304+
>>> # Pull all tags in the busybox repo
305+
>>> images = client.images.pull('busybox')
297306
"""
307+
if not tag:
308+
name, tag = parse_repository_tag(name)
309+
298310
self.client.api.pull(name, tag=tag, **kwargs)
299-
return self.get('{0}:{1}'.format(name, tag) if tag else name)
311+
if tag:
312+
return self.get('{0}:{1}'.format(name, tag))
313+
return self.list(name)
300314

301315
def push(self, repository, tag=None, **kwargs):
302316
return self.client.api.push(repository, tag=tag, **kwargs)

tests/integration/models_images_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ def test_pull_with_tag(self):
7474
image = client.images.pull('alpine', tag='3.3')
7575
assert 'alpine:3.3' in image.attrs['RepoTags']
7676

77+
def test_pull_multiple(self):
78+
client = docker.from_env(version=TEST_API_VERSION)
79+
images = client.images.pull('hello-world')
80+
assert len(images) == 1
81+
assert 'hello-world:latest' in images[0].attrs['RepoTags']
82+
7783
def test_load_error(self):
7884
client = docker.from_env(version=TEST_API_VERSION)
7985
with pytest.raises(docker.errors.ImageLoadError):

tests/integration/models_services_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import unittest
22

33
import docker
4+
import pytest
45

56
from .. import helpers
67
from .base import TEST_API_VERSION
78
from docker.errors import InvalidArgument
89
from docker.types.services import ServiceMode
9-
import pytest
1010

1111

1212
class ServiceTest(unittest.TestCase):
@@ -182,6 +182,7 @@ def test_update_remove_service_labels(self):
182182
service.reload()
183183
assert not service.attrs['Spec'].get('Labels')
184184

185+
@pytest.mark.xfail(reason='Flaky test')
185186
def test_update_retains_networks(self):
186187
client = docker.from_env(version=TEST_API_VERSION)
187188
network_name = helpers.random_name()

tests/unit/models_images_test.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,22 @@ def test_load(self):
4141

4242
def test_pull(self):
4343
client = make_fake_client()
44-
image = client.images.pull('test_image')
44+
image = client.images.pull('test_image:latest')
45+
client.api.pull.assert_called_with('test_image', tag='latest')
46+
client.api.inspect_image.assert_called_with('test_image:latest')
47+
assert isinstance(image, Image)
48+
assert image.id == FAKE_IMAGE_ID
49+
50+
def test_pull_multiple(self):
51+
client = make_fake_client()
52+
images = client.images.pull('test_image')
4553
client.api.pull.assert_called_with('test_image', tag=None)
46-
client.api.inspect_image.assert_called_with('test_image')
54+
client.api.images.assert_called_with(
55+
all=False, name='test_image', filters=None
56+
)
57+
client.api.inspect_image.assert_called_with(FAKE_IMAGE_ID)
58+
assert len(images) == 1
59+
image = images[0]
4760
assert isinstance(image, Image)
4861
assert image.id == FAKE_IMAGE_ID
4962

0 commit comments

Comments
 (0)