Browse Source

Make sure error messages are unicode strings before combining

Signed-off-by: Joffrey F <[email protected]>
Joffrey F 7 years ago
parent
commit
8356576a9a
3 changed files with 40 additions and 6 deletions
  1. 6 5
      compose/parallel.py
  2. 5 1
      compose/project.py
  3. 29 0
      tests/unit/project_test.py

+ 6 - 5
compose/parallel.py

@@ -279,9 +279,7 @@ class ParallelStreamWriter(object):
     def write_initial(self, msg, obj_index):
         if msg is None:
             return
-        self.stream.write("{:<{width}} ... \r\n".format(
-            msg + ' ' + obj_index, width=self.width))
-        self.stream.flush()
+        return self._write_noansi(msg, obj_index, '')
 
     def _write_ansi(self, msg, obj_index, status):
         self.lock.acquire()
@@ -299,8 +297,11 @@ class ParallelStreamWriter(object):
         self.lock.release()
 
     def _write_noansi(self, msg, obj_index, status):
-        self.stream.write("{:<{width}} ... {}\r\n".format(msg + ' ' + obj_index,
-                          status, width=self.width))
+        self.stream.write(
+            "{:<{width}} ... {}\r\n".format(
+                msg + ' ' + obj_index, status, width=self.width
+            )
+        )
         self.stream.flush()
 
     def write(self, msg, obj_index, status, color_func):

+ 5 - 1
compose/project.py

@@ -556,7 +556,11 @@ class Project(object):
                 limit=5,
             )
             if len(errors):
-                raise ProjectError(b"\n".join(errors.values()))
+                combined_errors = '\n'.join([
+                    e.decode('utf-8') if isinstance(e, six.binary_type) else e for e in errors.values()
+                ])
+                raise ProjectError(combined_errors)
+
         else:
             for service in services:
                 service.pull(ignore_pull_failures, silent=silent)

+ 29 - 0
tests/unit/project_test.py

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
 import datetime
 
 import docker
+import pytest
 from docker.errors import NotFound
 
 from .. import mock
@@ -16,8 +17,10 @@ from compose.const import COMPOSEFILE_V2_0 as V2_0
 from compose.const import COMPOSEFILE_V2_4 as V2_4
 from compose.const import LABEL_SERVICE
 from compose.container import Container
+from compose.errors import OperationFailedError
 from compose.project import NoSuchService
 from compose.project import Project
+from compose.project import ProjectError
 from compose.service import ImageType
 from compose.service import Service
 
@@ -588,3 +591,29 @@ class ProjectTest(unittest.TestCase):
             name='test', client=self.mock_client, config_data=config_data, default_platform='windows'
         )
         assert project.get_service('web').options.get('platform') == 'linux/s390x'
+
+    @mock.patch('compose.parallel.ParallelStreamWriter._write_noansi')
+    def test_error_parallel_pull(self, mock_write):
+        project = Project.from_config(
+            name='test',
+            client=self.mock_client,
+            config_data=Config(
+                version=V2_0,
+                services=[{
+                    'name': 'web',
+                    'image': 'busybox:latest',
+                }],
+                networks=None,
+                volumes=None,
+                secrets=None,
+                configs=None,
+            ),
+        )
+
+        self.mock_client.pull.side_effect = OperationFailedError('pull error')
+        with pytest.raises(ProjectError):
+            project.pull(parallel_pull=True)
+
+        self.mock_client.pull.side_effect = OperationFailedError(b'pull error')
+        with pytest.raises(ProjectError):
+            project.pull(parallel_pull=True)