diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index 121cb5dc0e..365a0d5293 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -1207,10 +1207,22 @@ def _configure(options): return config +def _ensure_db_directory_exists(path): + if path == b':memory:': # in memory db + return + newpath = os.path.dirname(path) + if not os.path.isdir(newpath): + if input_yn("The database directory {} does not \ + exist. Create it (Y/n)?" + .format(util.displayable_path(newpath))): + os.makedirs(newpath) + + def _open_library(config): """Create a new library instance from the configuration. """ dbpath = util.bytestring_path(config['library'].as_filename()) + _ensure_db_directory_exists(dbpath) try: lib = library.Library( dbpath, diff --git a/docs/changelog.rst b/docs/changelog.rst index 002b96c5dc..4126a8cce3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,6 +8,8 @@ Changelog goes here! New features: +* Create the parental directories for database if they do not exist. + :bug:`3808` :bug:`4327` * :ref:`musicbrainz-config`: a new :ref:`musicbrainz.enabled` option allows disabling the MusicBrainz metadata source during the autotagging process * :doc:`/plugins/kodiupdate`: Now supports multiple kodi instances diff --git a/test/test_dbcore.py b/test/test_dbcore.py index 603d85bad5..80d85c3bb4 100644 --- a/test/test_dbcore.py +++ b/test/test_dbcore.py @@ -763,5 +763,6 @@ def test_no_results(self): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) + if __name__ == '__main__': unittest.main(defaultTest='suite') diff --git a/test/test_ui_init.py b/test/test_ui_init.py index bb9a922a5d..9f9487a6a7 100644 --- a/test/test_ui_init.py +++ b/test/test_ui_init.py @@ -15,11 +15,16 @@ """Test module for file ui/__init__.py """ - +import os +import shutil import unittest -from test import _common +from random import random +from copy import deepcopy from beets import ui +from test import _common +from test.helper import control_stdin +from beets import config class InputMethodsTest(_common.TestCase): @@ -121,8 +126,39 @@ def test_human_seconds(self): self.assertEqual(h, ui.human_seconds(i)) +class ParentalDirCreation(_common.TestCase): + def test_create_yes(self): + non_exist_path = _common.util.py3_path(os.path.join( + self.temp_dir, b'nonexist', str(random()).encode())) + # Deepcopy instead of recovering because exceptions might + # occcur; wish I can use a golang defer here. + test_config = deepcopy(config) + test_config['library'] = non_exist_path + with control_stdin('y'): + ui._open_library(test_config) + + def test_create_no(self): + non_exist_path_parent = _common.util.py3_path( + os.path.join(self.temp_dir, b'nonexist')) + non_exist_path = _common.util.py3_path(os.path.join( + non_exist_path_parent.encode(), str(random()).encode())) + test_config = deepcopy(config) + test_config['library'] = non_exist_path + + with control_stdin('n'): + try: + ui._open_library(test_config) + except ui.UserError: + if os.path.exists(non_exist_path_parent): + shutil.rmtree(non_exist_path_parent) + raise OSError("Parent directories should not be created.") + else: + raise OSError("Parent directories should not be created.") + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) + if __name__ == '__main__': unittest.main(defaultTest='suite')