diff --git a/docs/changelog.rst b/docs/changelog.rst index b4f8d019..204efb84 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -75,6 +75,12 @@ v0.19.5 (UNRELEASED) Bug fix release. +- config: Support UTF-8 in default config. If an extension with non-ASCII + characters in its default config was installed, and Mopidy didn't already + have a config file, Mopidy would crashed when trying to create the initial + config file based on the default config of all available extensions. + (Fixes: :discuss:`428`) + - Models: Hide empty collections from :func:`repr()` representations. - Models: Field values are no longer stored on the model instance when the diff --git a/docs/conf.py b/docs/conf.py index 0715f326..c300aa62 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -157,6 +157,7 @@ extlinks = { 'commit': ('https://github.com/mopidy/mopidy/commit/%s', 'commit '), 'mpris': ( 'https://github.com/mopidy/mopidy-mpris/issues/%s', 'mopidy-mpris#'), + 'discuss': ('https://discuss.mopidy.com/t/%s', 'discuss.mopidy.com/t/'), } diff --git a/mopidy/config/__init__.py b/mopidy/config/__init__.py index 6edca51c..db451cef 100644 --- a/mopidy/config/__init__.py +++ b/mopidy/config/__init__.py @@ -98,8 +98,12 @@ def format_initial(extensions): versions = ['Mopidy %s' % versioning.get_version()] for extension in sorted(extensions, key=lambda ext: ext.dist_name): versions.append('%s %s' % (extension.dist_name, extension.version)) - description = _INITIAL_HELP.strip() % {'versions': '\n# '.join(versions)} - return description + '\n\n' + _format(config, {}, schemas, False, True) + + header = _INITIAL_HELP.strip() % {'versions': '\n# '.join(versions)} + formatted_config = _format( + config=config, comments={}, schemas=schemas, + display=False, disable=True).decode('utf-8') + return header + '\n\n' + formatted_config def _load(files, defaults, overrides): diff --git a/mopidy/utils/path.py b/mopidy/utils/path.py index 4c546ec3..c72d3b18 100644 --- a/mopidy/utils/path.py +++ b/mopidy/utils/path.py @@ -46,12 +46,14 @@ def get_or_create_file(file_path, mkdir=True, content=None): if not isinstance(file_path, bytes): raise ValueError('Path is not a bytestring.') file_path = expand_path(file_path) + if isinstance(content, compat.text_type): + content = content.encode('utf-8') if mkdir: get_or_create_dir(os.path.dirname(file_path)) if not os.path.isfile(file_path): logger.info('Creating file %s', file_path) - with open(file_path, 'w') as fh: - if content: + with open(file_path, 'wb') as fh: + if content is not None: fh.write(content) return file_path diff --git a/tests/utils/test_path.py b/tests/utils/test_path.py index 4467e07e..36d1f7db 100644 --- a/tests/utils/test_path.py +++ b/tests/utils/test_path.py @@ -107,12 +107,12 @@ class GetOrCreateFileTest(unittest.TestCase): with self.assertRaises(IOError): path.get_or_create_file(conflicting_dir) - def test_create_dir_with_unicode(self): + def test_create_dir_with_unicode_filename_throws_value_error(self): with self.assertRaises(ValueError): file_path = compat.text_type(os.path.join(self.parent, b'test')) path.get_or_create_file(file_path) - def test_create_file_with_none(self): + def test_create_file_with_none_filename_throws_value_error(self): with self.assertRaises(ValueError): path.get_or_create_file(None) @@ -121,12 +121,18 @@ class GetOrCreateFileTest(unittest.TestCase): with self.assertRaises(IOError): path.get_or_create_file(file_path, mkdir=False) - def test_create_dir_with_default_content(self): + def test_create_dir_with_bytes_content(self): file_path = os.path.join(self.parent, b'test') created = path.get_or_create_file(file_path, content=b'foobar') with open(created) as fh: self.assertEqual(fh.read(), b'foobar') + def test_create_dir_with_unicode_content(self): + file_path = os.path.join(self.parent, b'test') + created = path.get_or_create_file(file_path, content='foobaræøå') + with open(created) as fh: + self.assertEqual(fh.read(), b'foobaræøå') + class PathToFileURITest(unittest.TestCase): def test_simple_path(self):