diff --git a/compose/config/interpolation.py b/compose/config/interpolation.py index 0f2630383..5a5f349db 100644 --- a/compose/config/interpolation.py +++ b/compose/config/interpolation.py @@ -137,6 +137,8 @@ class TemplateWithDefaults(Template): if named is not None: val = mapping[named] + if isinstance(val, six.binary_type): + val = val.decode('utf-8') return '%s' % (val,) if mo.group('escaped') is not None: return self.delimiter diff --git a/compose/config/serialize.py b/compose/config/serialize.py index 3ab43fc59..7fb9360a2 100644 --- a/compose/config/serialize.py +++ b/compose/config/serialize.py @@ -27,6 +27,9 @@ def serialize_string(dumper, data): """ Ensure boolean-like strings are quoted in the output and escape $ characters """ representer = dumper.represent_str if six.PY3 else dumper.represent_unicode + if isinstance(data, six.binary_type): + data = data.decode('utf-8') + data = data.replace('$', '$$') if data.lower() in ('y', 'n', 'yes', 'no', 'on', 'off', 'true', 'false'): @@ -90,7 +93,8 @@ def serialize_config(config, image_digests=None): denormalize_config(config, image_digests), default_flow_style=False, indent=2, - width=80 + width=80, + allow_unicode=True ) diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 03626ad36..cfbd4cf33 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -4750,3 +4750,20 @@ class SerializeTest(unittest.TestCase): assert serialized_service['environment']['CURRENCY'] == '$$' assert serialized_service['command'] == 'echo $$FOO' assert serialized_service['entrypoint'][0] == '$$SHELL' + + def test_serialize_unicode_values(self): + cfg = { + 'version': '2.3', + 'services': { + 'web': { + 'image': 'busybox', + 'command': 'echo 十六夜 咲夜' + } + } + } + + config_dict = config.load(build_config_details(cfg)) + + serialized_config = yaml.load(serialize_config(config_dict)) + serialized_service = serialized_config['services']['web'] + assert serialized_service['command'] == 'echo 十六夜 咲夜' diff --git a/tests/unit/config/interpolation_test.py b/tests/unit/config/interpolation_test.py index dfeba96d0..62f4ac774 100644 --- a/tests/unit/config/interpolation_test.py +++ b/tests/unit/config/interpolation_test.py @@ -1,3 +1,5 @@ +# encoding: utf-8 + from __future__ import absolute_import from __future__ import unicode_literals @@ -401,3 +403,14 @@ def test_interpolate_mixed_separators(defaults_interpolator): def test_unbraced_separators(defaults_interpolator): assert defaults_interpolator("ok $FOO:-bar") == "ok first:-bar" assert defaults_interpolator("ok $BAZ?error") == "ok ?error" + + +def test_interpolate_unicode_values(): + variable_mapping = { + 'FOO': '十六夜 咲夜'.encode('utf-8'), + 'BAR': '十六夜 咲夜' + } + interpol = Interpolator(TemplateWithDefaults, variable_mapping).interpolate + + interpol("$FOO") == '十六夜 咲夜' + interpol("${BAR}") == '十六夜 咲夜'