Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions docker/models/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from ..api import APIClient
from ..errors import BuildError, ImageLoadError
from ..utils import parse_repository_tag
from ..utils.json_stream import json_stream
from .resource import Collection, Model

Expand Down Expand Up @@ -269,6 +270,8 @@ def pull(self, name, tag=None, **kwargs):
"""
Pull an image of the given name and return it. Similar to the
``docker pull`` command.
If no tag is specified, all tags from that repository will be
pulled.

If you want to get the raw pull output, use the
:py:meth:`~docker.api.image.ImageApiMixin.pull` method in the
Expand All @@ -285,18 +288,29 @@ def pull(self, name, tag=None, **kwargs):
platform (str): Platform in the format ``os[/arch[/variant]]``

Returns:
(:py:class:`Image`): The image that has been pulled.
(:py:class:`Image` or list): The image that has been pulled.
If no ``tag`` was specified, the method will return a list
of :py:class:`Image` objects belonging to this repository.

Raises:
:py:class:`docker.errors.APIError`
If the server returns an error.

Example:

>>> image = client.images.pull('busybox')
>>> # Pull the image tagged `latest` in the busybox repo
>>> image = client.images.pull('busybox:latest')

>>> # Pull all tags in the busybox repo
>>> images = client.images.pull('busybox')
"""
if not tag:
name, tag = parse_repository_tag(name)

self.client.api.pull(name, tag=tag, **kwargs)
return self.get('{0}:{1}'.format(name, tag) if tag else name)
if tag:
return self.get('{0}:{1}'.format(name, tag))
return self.list(name)

def push(self, repository, tag=None, **kwargs):
return self.client.api.push(repository, tag=tag, **kwargs)
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/models_images_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ def test_pull_with_tag(self):
image = client.images.pull('alpine', tag='3.3')
assert 'alpine:3.3' in image.attrs['RepoTags']

def test_pull_multiple(self):
client = docker.from_env(version=TEST_API_VERSION)
images = client.images.pull('hello-world')
assert len(images) == 1
assert 'hello-world:latest' in images[0].attrs['RepoTags']

def test_load_error(self):
client = docker.from_env(version=TEST_API_VERSION)
with pytest.raises(docker.errors.ImageLoadError):
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/models_services_test.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import unittest

import docker
import pytest

from .. import helpers
from .base import TEST_API_VERSION
from docker.errors import InvalidArgument
from docker.types.services import ServiceMode
import pytest


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

@pytest.mark.xfail(reason='Flaky test')
def test_update_retains_networks(self):
client = docker.from_env(version=TEST_API_VERSION)
network_name = helpers.random_name()
Expand Down
17 changes: 15 additions & 2 deletions tests/unit/models_images_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,22 @@ def test_load(self):

def test_pull(self):
client = make_fake_client()
image = client.images.pull('test_image')
image = client.images.pull('test_image:latest')
client.api.pull.assert_called_with('test_image', tag='latest')
client.api.inspect_image.assert_called_with('test_image:latest')
assert isinstance(image, Image)
assert image.id == FAKE_IMAGE_ID

def test_pull_multiple(self):
client = make_fake_client()
images = client.images.pull('test_image')
client.api.pull.assert_called_with('test_image', tag=None)
client.api.inspect_image.assert_called_with('test_image')
client.api.images.assert_called_with(
all=False, name='test_image', filters=None
)
client.api.inspect_image.assert_called_with(FAKE_IMAGE_ID)
assert len(images) == 1
image = images[0]
assert isinstance(image, Image)
assert image.id == FAKE_IMAGE_ID

Expand Down