From 10758c487ffca2af3701dee3e9b95e2db21f60f5 Mon Sep 17 00:00:00 2001 From: steini Date: Mon, 4 Feb 2013 23:41:38 +0000 Subject: [PATCH] Configurable list of patterns which will be ignored when pruning empty directories. * util.prune_dirs modified to accept glob patterns as clutter to determine emptiness. * config option, 'clutter' (a list of filenames/glob patterns) * ImportTask.prune passes this option's value to prune_dirs. --- beets/config_default.yaml | 1 + beets/importer.py | 6 +++++- beets/util/__init__.py | 22 ++++++++++++++++++---- test/test_importer.py | 8 ++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 9dfb8ea0f..100267ece 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -17,6 +17,7 @@ import: singletons: no default_action: apply +clutter: ["Thumbs.DB", ".DS_Store"] ignore: [".*", "*~"] replace: '[\\/]': _ diff --git a/beets/importer.py b/beets/importer.py index 61e490400..8ad8eca70 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -513,7 +513,11 @@ class ImportTask(object): call when the file in question may not have been removed. """ if self.toppath and not os.path.exists(filename): - util.prune_dirs(os.path.dirname(filename), self.toppath) + util.prune_dirs( + os.path.dirname(filename), + self.toppath, + clutter=config['clutter'].get(list) + ) # Full-album pipeline stages. diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 7bcd7eab4..096cc319c 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -193,11 +193,26 @@ def mkdirall(path): raise FilesystemError(exc, 'create', (ancestor,), traceback.format_exc()) +def fnmatch_all(names, patterns): + """ + `names` and `patterns` should be iterables. + Returns True if all names match any of the patterns. + """ + for name in names: + matches = False + for pattern in patterns: + matches = fnmatch.fnmatch(name, pattern) + if matches: + break + if not matches: + return False + return True + def prune_dirs(path, root=None, clutter=('.DS_Store', 'Thumbs.db')): """If path is an empty directory, then remove it. Recursively remove path's ancestry up to root (which is never removed) where there are empty directories. If path is not contained in root, then nothing is - removed. Filenames in clutter are ignored when determining + removed. Glob patterns in clutter are ignored when determining emptiness. If root is not provided, then only path may be removed (i.e., no recursive removal). """ @@ -224,13 +239,12 @@ def prune_dirs(path, root=None, clutter=('.DS_Store', 'Thumbs.db')): if not os.path.exists(directory): # Directory gone already. continue - - if all(fn in clutter for fn in os.listdir(directory)): + if fnmatch_all(os.listdir(directory), clutter): # Directory contains only clutter (or nothing). try: shutil.rmtree(directory) except OSError: - break + break else: break diff --git a/test/test_importer.py b/test/test_importer.py index 2bad42e4c..c00662328 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -332,6 +332,14 @@ class ImportApplyTest(_common.TestCase): _call_stages(self.session, [self.i], self.info, toppath=self.srcdir) self.assertNotExists(os.path.dirname(self.srcpath)) + def test_apply_with_move_prunes_with_extra_clutter(self): + f = open(os.path.join(self.srcdir, 'testalbum', 'alog.log'), 'w') + f.close() + config['clutter'] = ['*.log'] + config['import']['move'] = True + _call_stages(self.session, [self.i], self.info, toppath=self.srcdir) + self.assertNotExists(os.path.dirname(self.srcpath)) + def test_manipulate_files_with_null_move(self): """It should be possible to "move" a file even when the file is already at the destination.