From 90c57f99e88c5caf4e4959b3740df7235a0e1220 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Tue, 27 Mar 2018 12:37:21 -0700 Subject: [PATCH] On load error, retry reading the file with UTF-8 encoding Signed-off-by: Joffrey F --- compose/config/config.py | 12 +++++++++--- tests/unit/config/config_test.py | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/compose/config/config.py b/compose/config/config.py index 7bc220e5f6..147c1d31d2 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import functools +import io import logging import os import string @@ -1431,10 +1432,15 @@ def has_uppercase(name): return any(char in string.ascii_uppercase for char in name) -def load_yaml(filename): +def load_yaml(filename, encoding=None): try: - with open(filename, 'r') as fh: + with io.open(filename, 'r', encoding=encoding) as fh: return yaml.safe_load(fh) - except (IOError, yaml.YAMLError) as e: + except (IOError, yaml.YAMLError, UnicodeDecodeError) as e: + if encoding is None: + # Sometimes the user's locale sets an encoding that doesn't match + # the YAML files. Im such cases, retry once with the "default" + # UTF-8 encoding + return load_yaml(filename, encoding='utf-8') error_name = getattr(e, '__module__', '') + '.' + e.__class__.__name__ raise ConfigurationError(u"{}: {}".format(error_name, e)) diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 7a9bb944dc..0574b215c9 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -3,6 +3,7 @@ from __future__ import print_function from __future__ import unicode_literals +import codecs import os import shutil import tempfile @@ -1623,6 +1624,21 @@ def test_load_yaml_with_yaml_error(self): assert 'line 3, column 32' in exc.exconly() + def test_load_yaml_with_bom(self): + tmpdir = py.test.ensuretemp('bom_yaml') + self.addCleanup(tmpdir.remove) + bom_yaml = tmpdir.join('docker-compose.yml') + with codecs.open(str(bom_yaml), 'w', encoding='utf-8') as f: + f.write('''\ufeff + version: '2.3' + volumes: + park_bom: + ''') + assert config.load_yaml(str(bom_yaml)) == { + 'version': '2.3', + 'volumes': {'park_bom': None} + } + def test_validate_extra_hosts_invalid(self): with pytest.raises(ConfigurationError) as exc: config.load(build_config_details({