From 21aedeb51acede828d88fe3fe88bf9c4d1aee295 Mon Sep 17 00:00:00 2001 From: Marvin Steadfast Date: Wed, 11 Feb 2015 12:58:57 +0100 Subject: [PATCH 1/5] Updated permissions plugin to change directory permissions too. --- beetsplug/permissions.py | 76 +++++++++++++++++++++++++++++++++++----- test/test_permissions.py | 41 +++++++++++++++++----- 2 files changed, 101 insertions(+), 16 deletions(-) diff --git a/beetsplug/permissions.py b/beetsplug/permissions.py index 256f09e52..afff3ce38 100644 --- a/beetsplug/permissions.py +++ b/beetsplug/permissions.py @@ -6,10 +6,13 @@ like the following in your config.yaml to configure: permissions: file: 644 + dir: 755 """ import os +from collections import OrderedDict from beets import config, util from beets.plugins import BeetsPlugin +from beets.util import displayable_path def convert_perm(perm): @@ -28,13 +31,43 @@ def check_permissions(path, permission): return oct(os.stat(path).st_mode & 0o777) == oct(permission) +def get_music_directories(music_directory, imported_item): + """Creates a list of directories the imported item is in. + """ + # Checks for the directory in config and if it has a tilde in it. + # If its that way it will be expanded to the full path. + if '~' in music_directory: + music_directory = os.path.expanduser(music_directory) + + # Getting the absolute path of the directory. + music_directory = os.path.abspath(music_directory) + + # Creates a differential path list of the directory config path and + # the path of the imported item. + differential_path_list = os.path.split( + displayable_path(imported_item).split( + music_directory)[1])[0].split('/')[1:] + + # Creating a list with full paths of all directories in the music library + # we need to look at for chaning permissions. + directory_list = [] + for path in differential_path_list: + if len(directory_list) > 0: + directory_list.append(os.path.join(directory_list[-1], path)) + else: + directory_list.append(os.path.join(music_directory, path)) + + return directory_list + + class Permissions(BeetsPlugin): def __init__(self): super(Permissions, self).__init__() # Adding defaults. self.config.add({ - u'file': 644 + u'file': 644, + u'dir': 755 }) @@ -45,21 +78,25 @@ def permissions(lib, item=None, album=None): """ # Getting the config. file_perm = config['permissions']['file'].get() + dir_perm = config['permissions']['dir'].get() - # Converts file permissions to oct. + # Converts permissions to oct. file_perm = convert_perm(file_perm) + dir_perm = convert_perm(dir_perm) # Create chmod_queue. - chmod_queue = [] + file_chmod_queue = [] if item: - chmod_queue.append(item.path) + file_chmod_queue.append(item.path) elif album: for album_item in album.items(): - chmod_queue.append(album_item.path) + file_chmod_queue.append(album_item.path) - # Setting permissions for every path in the queue. - for path in chmod_queue: - # Changing permissions on the destination path. + # A list of directories to set permissions for. + dir_chmod_queue = [] + + for path in file_chmod_queue: + # Changing permissions on the destination file. os.chmod(util.bytestring_path(path), file_perm) # Checks if the destination path has the permissions configured. @@ -67,3 +104,26 @@ def permissions(lib, item=None, album=None): message = 'There was a problem setting permission on {}'.format( path) print(message) + + # Adding directories to the chmod queue. + dir_chmod_queue.append( + get_music_directories(config['directory'].get(), path)) + + # Unpack sublists. + dir_chmod_queue = [directory + for dir_list in dir_chmod_queue + for directory in dir_list] + + # Get rid of the duplicates. + dir_chmod_queue = list(OrderedDict.fromkeys(dir_chmod_queue)) + + # Change permissions for the directories. + for path in dir_chmod_queue: + # Chaning permissions on the destination directory. + os.chmod(util.bytestring_path(path), dir_perm) + # Checks if the destination path has the permissions configured. + + if not check_permissions(util.bytestring_path(path), dir_perm): + message = 'There was a problem setting permission on {}'.format( + path) + print(message) diff --git a/test/test_permissions.py b/test/test_permissions.py index 1e16a1c34..9f24820d6 100644 --- a/test/test_permissions.py +++ b/test/test_permissions.py @@ -5,7 +5,9 @@ from __future__ import (division, absolute_import, print_function, from test._common import unittest from test.helper import TestHelper -from beetsplug.permissions import check_permissions, convert_perm +from beetsplug.permissions import (check_permissions, + convert_perm, + get_music_directories) class PermissionsPluginTest(unittest.TestCase, TestHelper): @@ -14,7 +16,8 @@ class PermissionsPluginTest(unittest.TestCase, TestHelper): self.load_plugins('permissions') self.config['permissions'] = { - 'file': 777} + 'file': 777, + 'dir': 777} def tearDown(self): self.teardown_beets() @@ -24,23 +27,45 @@ class PermissionsPluginTest(unittest.TestCase, TestHelper): self.importer = self.create_importer() self.importer.run() item = self.lib.items().get() - config_perm = self.config['permissions']['file'].get() - config_perm = convert_perm(config_perm) - self.assertTrue(check_permissions(item.path, config_perm)) + file_perm = self.config['permissions']['file'].get() + file_perm = convert_perm(file_perm) + + dir_perm = self.config['permissions']['dir'].get() + dir_perm = convert_perm(dir_perm) + + music_dirs = get_music_directories(self.config['directory'].get(), + item.path) + + self.assertTrue(check_permissions(item.path, file_perm)) self.assertFalse(check_permissions(item.path, convert_perm(644))) + for path in music_dirs: + self.assertTrue(check_permissions(path, dir_perm)) + self.assertFalse(check_permissions(path, convert_perm(644))) + def test_permissions_on_item_imported(self): self.config['import']['singletons'] = True self.importer = self.create_importer() self.importer.run() item = self.lib.items().get() - config_perm = self.config['permissions']['file'].get() - config_perm = convert_perm(config_perm) - self.assertTrue(check_permissions(item.path, config_perm)) + file_perm = self.config['permissions']['file'].get() + file_perm = convert_perm(file_perm) + + dir_perm = self.config['permissions']['dir'].get() + dir_perm = convert_perm(dir_perm) + + music_dirs = get_music_directories(self.config['directory'].get(), + item.path) + + self.assertTrue(check_permissions(item.path, file_perm)) self.assertFalse(check_permissions(item.path, convert_perm(644))) + for path in music_dirs: + self.assertTrue(check_permissions(path, dir_perm)) + self.assertFalse(check_permissions(path, convert_perm(644))) + def suite(): return unittest.TestLoader().loadTestsFromName(__name__) From 8b08ec568c33a76edf78e69a58dc1f450b3c8f95 Mon Sep 17 00:00:00 2001 From: Marvin Steadfast Date: Wed, 11 Feb 2015 13:27:18 +0100 Subject: [PATCH 2/5] permissions plugin now uses a set instead of a OrderedDict to find duplicates in a list --- beetsplug/permissions.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/beetsplug/permissions.py b/beetsplug/permissions.py index afff3ce38..7d50c34ab 100644 --- a/beetsplug/permissions.py +++ b/beetsplug/permissions.py @@ -9,7 +9,6 @@ like the following in your config.yaml to configure: dir: 755 """ import os -from collections import OrderedDict from beets import config, util from beets.plugins import BeetsPlugin from beets.util import displayable_path @@ -114,15 +113,15 @@ def permissions(lib, item=None, album=None): for dir_list in dir_chmod_queue for directory in dir_list] - # Get rid of the duplicates. - dir_chmod_queue = list(OrderedDict.fromkeys(dir_chmod_queue)) + # Get rid of duplicates. + dir_chmod_queue = list(set(dir_chmod_queue)) # Change permissions for the directories. for path in dir_chmod_queue: # Chaning permissions on the destination directory. os.chmod(util.bytestring_path(path), dir_perm) - # Checks if the destination path has the permissions configured. + # Checks if the destination path has the permissions configured. if not check_permissions(util.bytestring_path(path), dir_perm): message = 'There was a problem setting permission on {}'.format( path) From 27f4732d3d93fb7c9d2d1232df35e002535008ba Mon Sep 17 00:00:00 2001 From: Marvin Steadfast Date: Wed, 11 Feb 2015 13:48:53 +0100 Subject: [PATCH 3/5] Updated permissions plugin docs --- docs/plugins/permissions.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/plugins/permissions.rst b/docs/plugins/permissions.rst index 06b034b51..9c4cdc0aa 100644 --- a/docs/plugins/permissions.rst +++ b/docs/plugins/permissions.rst @@ -2,7 +2,7 @@ Permissions Plugin ================== The ``permissions`` plugin allows you to set file permissions for imported -music files. +music files and its directories. To use the ``permissions`` plugin, enable it in your configuration (see :ref:`using-plugins`). Permissions will be adjusted automatically on import. @@ -12,9 +12,10 @@ Configuration To configure the plugin, make an ``permissions:`` section in your configuration file. The ``file`` config value therein uses **octal modes** to specify the -desired permissions. The default flags are octal 644. +desired permissions. The default flags for files are octal 644 and 755 for directories. Here's an example:: permissions: file: 644 + dir: 755 From dd0de2f04b6e61253cac72ab0b8fd0e2c4da6d10 Mon Sep 17 00:00:00 2001 From: Marvin Steadfast Date: Mon, 16 Feb 2015 13:26:38 +0100 Subject: [PATCH 4/5] Made the permissions plugin simpler. Got rid of some non-needed code and use the ancestors function instead of writing something new. --- beetsplug/permissions.py | 52 ++++++++++------------------------------ test/test_permissions.py | 10 ++++---- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/beetsplug/permissions.py b/beetsplug/permissions.py index 7d50c34ab..224819658 100644 --- a/beetsplug/permissions.py +++ b/beetsplug/permissions.py @@ -11,7 +11,7 @@ like the following in your config.yaml to configure: import os from beets import config, util from beets.plugins import BeetsPlugin -from beets.util import displayable_path +from beets.util import ancestry def convert_perm(perm): @@ -30,33 +30,12 @@ def check_permissions(path, permission): return oct(os.stat(path).st_mode & 0o777) == oct(permission) -def get_music_directories(music_directory, imported_item): - """Creates a list of directories the imported item is in. +def dirs_in_library(library, item): + """Creates a list of ancestor directories in the beets library path. """ - # Checks for the directory in config and if it has a tilde in it. - # If its that way it will be expanded to the full path. - if '~' in music_directory: - music_directory = os.path.expanduser(music_directory) - - # Getting the absolute path of the directory. - music_directory = os.path.abspath(music_directory) - - # Creates a differential path list of the directory config path and - # the path of the imported item. - differential_path_list = os.path.split( - displayable_path(imported_item).split( - music_directory)[1])[0].split('/')[1:] - - # Creating a list with full paths of all directories in the music library - # we need to look at for chaning permissions. - directory_list = [] - for path in differential_path_list: - if len(directory_list) > 0: - directory_list.append(os.path.join(directory_list[-1], path)) - else: - directory_list.append(os.path.join(music_directory, path)) - - return directory_list + return [ancestor + for ancestor in ancestry(item) + if library in ancestor][1:] class Permissions(BeetsPlugin): @@ -91,8 +70,8 @@ def permissions(lib, item=None, album=None): for album_item in album.items(): file_chmod_queue.append(album_item.path) - # A list of directories to set permissions for. - dir_chmod_queue = [] + # A set of directories to change permissions for. + dir_chmod_queue = set() for path in file_chmod_queue: # Changing permissions on the destination file. @@ -104,17 +83,10 @@ def permissions(lib, item=None, album=None): path) print(message) - # Adding directories to the chmod queue. - dir_chmod_queue.append( - get_music_directories(config['directory'].get(), path)) - - # Unpack sublists. - dir_chmod_queue = [directory - for dir_list in dir_chmod_queue - for directory in dir_list] - - # Get rid of duplicates. - dir_chmod_queue = list(set(dir_chmod_queue)) + # Adding directories to the directory chmod queue. + dir_chmod_queue.update( + dirs_in_library(config['directory'].get(), + path)) # Change permissions for the directories. for path in dir_chmod_queue: diff --git a/test/test_permissions.py b/test/test_permissions.py index 9f24820d6..20e33b7d2 100644 --- a/test/test_permissions.py +++ b/test/test_permissions.py @@ -7,7 +7,7 @@ from test._common import unittest from test.helper import TestHelper from beetsplug.permissions import (check_permissions, convert_perm, - get_music_directories) + dirs_in_library) class PermissionsPluginTest(unittest.TestCase, TestHelper): @@ -34,8 +34,8 @@ class PermissionsPluginTest(unittest.TestCase, TestHelper): dir_perm = self.config['permissions']['dir'].get() dir_perm = convert_perm(dir_perm) - music_dirs = get_music_directories(self.config['directory'].get(), - item.path) + music_dirs = dirs_in_library(self.config['directory'].get(), + item.path) self.assertTrue(check_permissions(item.path, file_perm)) self.assertFalse(check_permissions(item.path, convert_perm(644))) @@ -56,8 +56,8 @@ class PermissionsPluginTest(unittest.TestCase, TestHelper): dir_perm = self.config['permissions']['dir'].get() dir_perm = convert_perm(dir_perm) - music_dirs = get_music_directories(self.config['directory'].get(), - item.path) + music_dirs = dirs_in_library(self.config['directory'].get(), + item.path) self.assertTrue(check_permissions(item.path, file_perm)) self.assertFalse(check_permissions(item.path, convert_perm(644))) From b9174d176f96232b184998faf49a7b64b0696d5b Mon Sep 17 00:00:00 2001 From: Marvin Steadfast Date: Tue, 17 Feb 2015 11:43:56 +0100 Subject: [PATCH 5/5] The permissions plugin now uses startswith for finding ancestors in the library path. --- beetsplug/permissions.py | 2 +- docs/changelog.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/beetsplug/permissions.py b/beetsplug/permissions.py index 224819658..7039f491a 100644 --- a/beetsplug/permissions.py +++ b/beetsplug/permissions.py @@ -35,7 +35,7 @@ def dirs_in_library(library, item): """ return [ancestor for ancestor in ancestry(item) - if library in ancestor][1:] + if ancestor.startswith(library)][1:] class Permissions(BeetsPlugin): diff --git a/docs/changelog.rst b/docs/changelog.rst index 329770102..ca96f1d3f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -44,6 +44,8 @@ Features: * The number of missing/unmatched tracks is shown during import. :bug:`1088` * The data source used during import (e.g., MusicBrainz) is now saved as a flexible attribute `data_source` of an Item/Album. :bug:`1311` +* :doc:`/plugins/permissions`: Now handles also the permissions of the + directories. :bug:`1308` bug:`1324` Core changes: