Repository: beetbox/beets

Issue:
Improve documentation of lastgenre.prefer_specific's dependency on lastgenre.canonical?
### Problem

When `lastgenre.prefer_specific` is enabled but `lastgenre.canonical` is not `beet lastgenre` will fail to apply any genres. This could be better documented.

#### Error message
It would be nice if there would be a error message when `prefer_specific` is enabled but `canonical` is not. 
The current behaviour:
```sh
$ # with prefer_specific but without canonical:
$ beet lastgenre LITE Cubic
lastgenre: genre for album LITE - Cubic (None):
$ # compare: with canonical:
$ beet lastgenre LITE Cubic
lastgenre: genre for album LITE - Cubic (artist): Post-Rock
```
Suggested behaviour:
```sh
$ # with prefer_specific but without canonical:
$ beet lastgenre LITE Cubic
lastgenre: ERROR: `lastgenre.prefer_specific` is enabled but `lastgenre.canonical` is not. Please either disable `prefer_specific` or enable `canonical`!
```

#### Documentation
Currently the [documentation](https://beets.readthedocs.io/en/latest/plugins/lastgenre.html#specific-vs-popular-genres) only hints at this requirement. One could possibly make this clearer.

#### Alternatives
Alternatively the default canonicalisation tree could just be loaded. Care must be taken to only use it for sorting, not canonicalisation.



Discussion:

Resolving patch:
diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py
--- a/beetsplug/lastgenre/__init__.py
+++ b/beetsplug/lastgenre/__init__.py
@@ -138,9 +138,18 @@ def setup(self):
         # Read the genres tree for canonicalization if enabled.
         self.c14n_branches = []
         c14n_filename = self.config['canonical'].get()
-        if c14n_filename in (True, ''):  # Default tree.
+        self.canonicalize = c14n_filename is not False
+
+        # Default tree
+        if c14n_filename in (True, ''):
+            c14n_filename = C14N_TREE
+        elif not self.canonicalize and self.config['prefer_specific'].get():
+            # prefer_specific requires a tree, load default tree
             c14n_filename = C14N_TREE
+
+        # Read the tree
         if c14n_filename:
+            self._log.debug('Loading canonicalization tree {0}', c14n_filename)
             c14n_filename = normpath(c14n_filename)
             with codecs.open(c14n_filename, 'r', encoding='utf-8') as f:
                 genres_tree = yaml.load(f)
@@ -186,7 +195,7 @@ def _resolve_genres(self, tags):
             return None
 
         count = self.config['count'].get(int)
-        if self.c14n_branches:
+        if self.canonicalize:
             # Extend the list to consider tags parents in the c14n tree
             tags_all = []
             for tag in tags:


Test patch:
diff --git a/test/test_lastgenre.py b/test/test_lastgenre.py
--- a/test/test_lastgenre.py
+++ b/test/test_lastgenre.py
@@ -36,9 +36,11 @@ def setUp(self):
     def tearDown(self):
         self.teardown_beets()
 
-    def _setup_config(self, whitelist=False, canonical=False, count=1):
+    def _setup_config(self, whitelist=False, canonical=False, count=1,
+                      prefer_specific=False):
         config['lastgenre']['canonical'] = canonical
         config['lastgenre']['count'] = count
+        config['lastgenre']['prefer_specific'] = prefer_specific
         if isinstance(whitelist, (bool, six.string_types)):
             # Filename, default, or disabled.
             config['lastgenre']['whitelist'] = whitelist
@@ -136,6 +138,21 @@ def test_empty_string_enables_whitelist(self):
         self.assertEqual(self.plugin._resolve_genres(['iota blues']),
                          u'')
 
+    def test_prefer_specific_loads_tree(self):
+        """When prefer_specific is enabled but canonical is not the
+        tree still has to be loaded.
+        """
+        self._setup_config(prefer_specific=True, canonical=False)
+        self.assertNotEqual(self.plugin.c14n_branches, [])
+
+    def test_prefer_specific_without_canonical(self):
+        """Prefer_specific works without canonical.
+        """
+        self._setup_config(prefer_specific=True, canonical=False, count=4)
+        self.assertEqual(self.plugin._resolve_genres(
+                         ['math rock', 'post-rock']),
+                         u'Post-Rock, Math Rock')
+
     def test_no_duplicate(self):
         """Remove duplicated genres.
         """