Repository: docker/docker-py

Issue:
image build error on master with docker 1.12.6
Hello,
ImageCollection.build regex error check is wrong with docker 1.12.6 API, you can fix it this way:
________________________________________________________
        if 'stream' in event:
            match = re.search(r'(Successfully built |sha256:)([0-9a-f]+)',
                              event.get('stream', ''))
            if match:
                image_id = match.group(2)
                return self.get(image_id)
____________________________________________________________
Regards,
Sébastien Dorgan



Discussion:

Resolving patch:
diff --git a/docker/api/build.py b/docker/api/build.py
--- a/docker/api/build.py
+++ b/docker/api/build.py
@@ -1,11 +1,11 @@
+import json
 import logging
 import os
 import re
-import json
 
+from .. import auth
 from .. import constants
 from .. import errors
-from .. import auth
 from .. import utils
 
 
@@ -18,7 +18,7 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
               custom_context=False, encoding=None, pull=False,
               forcerm=False, dockerfile=None, container_limits=None,
               decode=False, buildargs=None, gzip=False, shmsize=None,
-              labels=None, cachefrom=None):
+              labels=None, cache_from=None):
         """
         Similar to the ``docker build`` command. Either ``path`` or ``fileobj``
         needs to be set. ``path`` can be a local path (to a directory
@@ -92,7 +92,8 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
             shmsize (int): Size of `/dev/shm` in bytes. The size must be
                 greater than 0. If omitted the system uses 64MB.
             labels (dict): A dictionary of labels to set on the image.
-            cachefrom (list): A list of images used for build cache resolution.
+            cache_from (list): A list of images used for build cache
+                resolution.
 
         Returns:
             A generator for the build output.
@@ -189,12 +190,12 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
                     'labels was only introduced in API version 1.23'
                 )
 
-        if cachefrom:
+        if cache_from:
             if utils.version_gte(self._version, '1.25'):
-                params.update({'cachefrom': json.dumps(cachefrom)})
+                params.update({'cachefrom': json.dumps(cache_from)})
             else:
                 raise errors.InvalidVersion(
-                    'cachefrom was only introduced in API version 1.25'
+                    'cache_from was only introduced in API version 1.25'
                 )
 
         if context is not None:
diff --git a/docker/models/images.py b/docker/models/images.py
--- a/docker/models/images.py
+++ b/docker/models/images.py
@@ -141,7 +141,8 @@ def build(self, **kwargs):
                     ``"0-3"``, ``"0,1"``
             decode (bool): If set to ``True``, the returned stream will be
                 decoded into dicts on the fly. Default ``False``.
-            cachefrom (list): A list of images used for build cache resolution.
+            cache_from (list): A list of images used for build cache
+                resolution.
 
         Returns:
             (:py:class:`Image`): The built image.
@@ -162,10 +163,10 @@ def build(self, **kwargs):
             return BuildError('Unknown')
         event = events[-1]
         if 'stream' in event:
-            match = re.search(r'Successfully built ([0-9a-f]+)',
+            match = re.search(r'(Successfully built |sha256:)([0-9a-f]+)',
                               event.get('stream', ''))
             if match:
-                image_id = match.group(1)
+                image_id = match.group(2)
                 return self.get(image_id)
 
         raise BuildError(event.get('error') or event)


Test patch:
diff --git a/tests/integration/api_build_test.py b/tests/integration/api_build_test.py
--- a/tests/integration/api_build_test.py
+++ b/tests/integration/api_build_test.py
@@ -3,13 +3,12 @@
 import shutil
 import tempfile
 
-import pytest
-import six
-
 from docker import errors
 
-from ..helpers import requires_api_version
+import six
+
 from .base import BaseAPIIntegrationTest
+from ..helpers import requires_api_version
 
 
 class BuildTest(BaseAPIIntegrationTest):
@@ -155,25 +154,40 @@ def test_build_labels(self):
         self.assertEqual(info['Config']['Labels'], labels)
 
     @requires_api_version('1.25')
-    @pytest.mark.xfail(reason='Bad test')
-    def test_build_cachefrom(self):
+    def test_build_with_cache_from(self):
         script = io.BytesIO('\n'.join([
-            'FROM scratch',
-            'CMD sh -c "echo \'Hello, World!\'"',
+            'FROM busybox',
+            'ENV FOO=bar',
+            'RUN touch baz',
+            'RUN touch bax',
         ]).encode('ascii'))
 
-        cachefrom = ['build1']
+        stream = self.client.build(fileobj=script, tag='build1')
+        self.tmp_imgs.append('build1')
+        for chunk in stream:
+            pass
 
         stream = self.client.build(
-            fileobj=script, tag='cachefrom', cachefrom=cachefrom
+            fileobj=script, tag='build2', cache_from=['build1'],
+            decode=True
         )
-        self.tmp_imgs.append('cachefrom')
+        self.tmp_imgs.append('build2')
+        counter = 0
         for chunk in stream:
-            pass
+            if 'Using cache' in chunk.get('stream', ''):
+                counter += 1
+        assert counter == 3
+        self.client.remove_image('build2')
 
-        info = self.client.inspect_image('cachefrom')
-        # FIXME: Config.CacheFrom is not a real thing
-        self.assertEqual(info['Config']['CacheFrom'], cachefrom)
+        counter = 0
+        stream = self.client.build(
+            fileobj=script, tag='build2', cache_from=['nosuchtag'],
+            decode=True
+        )
+        for chunk in stream:
+            if 'Using cache' in chunk.get('stream', ''):
+                counter += 1
+        assert counter == 0
 
     def test_build_stderr_data(self):
         control_chars = ['\x1b[91m', '\x1b[0m']