diff --git a/compose/cli/errors.py b/compose/cli/errors.py index eefa4ebe4..82768970b 100644 --- a/compose/cli/errors.py +++ b/compose/cli/errors.py @@ -7,7 +7,6 @@ import socket from distutils.spawn import find_executable from textwrap import dedent -import six from docker.errors import APIError from requests.exceptions import ConnectionError as RequestsConnectionError from requests.exceptions import ReadTimeout @@ -15,6 +14,7 @@ from requests.exceptions import SSLError from requests.packages.urllib3.exceptions import ReadTimeoutError from ..const import API_VERSION_TO_ENGINE_VERSION +from .utils import binarystr_to_unicode from .utils import is_docker_for_mac_installed from .utils import is_mac from .utils import is_ubuntu @@ -75,7 +75,9 @@ def log_windows_pipe_error(exc): ) else: log.error( - "Windows named pipe error: {} (code: {})".format(exc.strerror, exc.winerror) + "Windows named pipe error: {} (code: {})".format( + binarystr_to_unicode(exc.strerror), exc.winerror + ) ) @@ -89,9 +91,7 @@ def log_timeout_error(timeout): def log_api_error(e, client_version): - explanation = e.explanation - if isinstance(explanation, six.binary_type): - explanation = explanation.decode('utf-8') + explanation = binarystr_to_unicode(e.explanation) if 'client is newer than server' not in explanation: log.error(explanation) diff --git a/compose/cli/utils.py b/compose/cli/utils.py index 4d4fc4c18..a171d6678 100644 --- a/compose/cli/utils.py +++ b/compose/cli/utils.py @@ -10,6 +10,7 @@ import subprocess import sys import docker +import six import compose from ..const import IS_WINDOWS_PLATFORM @@ -148,3 +149,15 @@ def human_readable_file_size(size): size / float(1 << (order * 10)), suffixes[order] ) + + +def binarystr_to_unicode(s): + if not isinstance(s, six.binary_type): + return s + + if IS_WINDOWS_PLATFORM: + try: + return s.decode('windows-1250') + except UnicodeDecodeError: + pass + return s.decode('utf-8', 'replace') diff --git a/tests/unit/cli/errors_test.py b/tests/unit/cli/errors_test.py index 68326d1c7..7b53ed2b1 100644 --- a/tests/unit/cli/errors_test.py +++ b/tests/unit/cli/errors_test.py @@ -86,3 +86,13 @@ class TestHandleConnectionErrors(object): _, args, _ = mock_logging.error.mock_calls[0] assert "Windows named pipe error: The pipe is busy. (code: 231)" == args[0] + + @pytest.mark.skipif(not IS_WINDOWS_PLATFORM, reason='Needs pywin32') + def test_windows_pipe_error_encoding_issue(self, mock_logging): + import pywintypes + with pytest.raises(errors.ConnectionError): + with handle_connection_errors(mock.Mock(api_version='1.22')): + raise pywintypes.error(9999, 'WriteFile', 'I use weird characters \xe9') + + _, args, _ = mock_logging.error.mock_calls[0] + assert 'Windows named pipe error: I use weird characters \xe9 (code: 9999)' == args[0]