Merge pull request #1346 from brunal/subcommand-auto-format-path

Generalize --path, --format and --album options
This commit is contained in:
Bruno 2015-03-07 14:05:05 +01:00
commit f6c6c35614
21 changed files with 341 additions and 120 deletions

View file

@ -61,8 +61,8 @@ ui:
action_default: turquoise
action: blue
list_format_item: $artist - $album - $title
list_format_album: $albumartist - $album
format_item: $artist - $album - $title
format_album: $albumartist - $album
time_format: '%Y-%m-%d %H:%M:%S'
sort_album: albumartist+ album+

View file

@ -444,7 +444,7 @@ class Item(LibModel):
_sorts = {'artist': SmartArtistSort}
_format_config_key = 'list_format_item'
_format_config_key = 'format_item'
@classmethod
def _getters(cls):
@ -877,7 +877,7 @@ class Album(LibModel):
"""List of keys that are set on an album's items.
"""
_format_config_key = 'list_format_album'
_format_config_key = 'format_album'
@classmethod
def _getters(cls):

View file

@ -592,6 +592,119 @@ def show_model_changes(new, old=None, fields=None, always=False):
return bool(changes)
class CommonOptionsParser(optparse.OptionParser, object):
"""Offers a simple way to add common formatting options.
Options available include:
- matching albums instead of tracks: add_album_option()
- showing paths instead of items/albums: add_path_option()
- changing the format of displayed items/albums: add_format_option()
The last one can have several behaviors:
- against a special target
- with a certain format
- autodetected target with the album option
Each method is fully documented in the related method.
"""
def __init__(self, *args, **kwargs):
super(CommonOptionsParser, self).__init__(*args, **kwargs)
self._album_flags = False
# this serves both as an indicator that we offer the feature AND allows
# us to check whether it has been specified on the CLI - bypassing the
# fact that arguments may be in any order
def add_album_option(self, flags=('-a', '--album')):
"""Add a -a/--album option to match albums instead of tracks.
If used then the format option can auto-detect whether we're setting
the format for items or albums.
Sets the album property on the options extracted from the CLI.
"""
album = optparse.Option(*flags, action='store_true',
help='match albums instead of tracks')
self.add_option(album)
self._album_flags = set(flags)
def _set_format(self, option, opt_str, value, parser, target=None,
fmt=None, store_true=False):
"""Internal callback that sets the correct format while parsing CLI
arguments.
"""
if store_true:
setattr(parser.values, option.dest, True)
value = fmt or value and unicode(value) or ''
parser.values.format = value
if target:
config[target._format_config_key].set(value)
else:
if self._album_flags:
if parser.values.album:
target = library.Album
else:
# the option is either missing either not parsed yet
if self._album_flags & set(parser.rargs):
target = library.Album
else:
target = library.Item
config[target._format_config_key].set(value)
else:
config[library.Item._format_config_key].set(value)
config[library.Album._format_config_key].set(value)
def add_path_option(self, flags=('-p', '--path')):
"""Add a -p/--path option to display the path instead of the default
format.
By default this affects both items and albums. If add_album_option()
is used then the target will be autodetected.
Sets the format property to u'$path' on the options extracted from the
CLI.
"""
path = optparse.Option(*flags, nargs=0, action='callback',
callback=self._set_format,
callback_kwargs={'fmt': '$path',
'store_true': True},
help='print paths for matched items or albums')
self.add_option(path)
def add_format_option(self, flags=('-f', '--format'), target=None):
"""Add -f/--format option to print some LibModel instances with a
custom format.
`target` is optional and can be one of ``library.Item``, 'item',
``library.Album`` and 'album'.
Several behaviors are available:
- if `target` is given then the format is only applied to that
LibModel
- if the album option is used then the target will be autodetected
- otherwise the format is applied to both items and albums.
Sets the format property on the options extracted from the CLI.
"""
kwargs = {}
if target:
if isinstance(target, basestring):
target = {'item': library.Item,
'album': library.Album}[target]
kwargs['target'] = target
opt = optparse.Option(*flags, action='callback',
callback=self._set_format,
callback_kwargs=kwargs,
help='print with custom format')
self.add_option(opt)
def add_all_common_options(self):
"""Add album, path and format options.
"""
self.add_album_option()
self.add_path_option()
self.add_format_option()
# Subcommand parsing infrastructure.
#
# This is a fairly generic subcommand parser for optparse. It is
@ -600,6 +713,7 @@ def show_model_changes(new, old=None, fields=None, always=False):
# There you will also find a better description of the code and a more
# succinct example program.
class Subcommand(object):
"""A subcommand of a root command-line application that may be
invoked by a SubcommandOptionParser.
@ -609,10 +723,10 @@ class Subcommand(object):
the subcommand; aliases are alternate names. parser is an
OptionParser responsible for parsing the subcommand's options.
help is a short description of the command. If no parser is
given, it defaults to a new, empty OptionParser.
given, it defaults to a new, empty CommonOptionsParser.
"""
self.name = name
self.parser = parser or optparse.OptionParser()
self.parser = parser or CommonOptionsParser()
self.aliases = aliases
self.help = help
self.hide = hide
@ -635,7 +749,7 @@ class Subcommand(object):
root_parser.get_prog_name().decode('utf8'), self.name)
class SubcommandsOptionParser(optparse.OptionParser):
class SubcommandsOptionParser(CommonOptionsParser):
"""A variant of OptionParser that parses subcommands and their
arguments.
"""
@ -653,7 +767,7 @@ class SubcommandsOptionParser(optparse.OptionParser):
kwargs['add_help_option'] = False
# Super constructor.
optparse.OptionParser.__init__(self, *args, **kwargs)
super(SubcommandsOptionParser, self).__init__(*args, **kwargs)
# Our root parser needs to stop on the first unrecognized argument.
self.disable_interspersed_args()
@ -670,7 +784,7 @@ class SubcommandsOptionParser(optparse.OptionParser):
# Add the list of subcommands to the help message.
def format_help(self, formatter=None):
# Get the original help message, to which we will append.
out = optparse.OptionParser.format_help(self, formatter)
out = super(SubcommandsOptionParser, self).format_help(formatter)
if formatter is None:
formatter = self.formatter
@ -871,6 +985,17 @@ def _configure(options):
u'See documentation for more info.')
config['ui']['color'].set(config['color'].get(bool))
# Compatibility from list_format_{item,album} to format_{item,album}
for elem in ('item', 'album'):
old_key = 'list_format_{0}'.format(elem)
if config[old_key].exists():
new_key = 'format_{0}'.format(elem)
log.warning('Warning: configuration uses "{0}" which is deprecated'
' in favor of "{1}" now that it affects all commands. '
'See changelog & documentation.'.format(old_key,
new_key))
config[new_key].set(config[old_key])
config_path = config.user_config_path()
if os.path.isfile(config_path):
log.debug(u'user configuration: {0}',
@ -913,6 +1038,8 @@ def _raw_main(args, lib=None):
handling.
"""
parser = SubcommandsOptionParser()
parser.add_format_option(flags=('--format-item',), target=library.Item)
parser.add_format_option(flags=('--format-album',), target=library.Album)
parser.add_option('-l', '--library', dest='library',
help='library database file to use')
parser.add_option('-d', '--directory', dest='directory',

View file

@ -957,7 +957,7 @@ default_commands.append(import_cmd)
# list: Query and show library contents.
def list_items(lib, query, album, fmt):
def list_items(lib, query, album, fmt=''):
"""Print out items in lib matching query. If album, then search for
albums instead of single items.
"""
@ -970,23 +970,11 @@ def list_items(lib, query, album, fmt):
def list_func(lib, opts, args):
fmt = '$path' if opts.path else opts.format
list_items(lib, decargs(args), opts.album, fmt)
list_items(lib, decargs(args), opts.album)
list_cmd = ui.Subcommand('list', help='query the library', aliases=('ls',))
list_cmd.parser.add_option(
'-a', '--album', action='store_true',
help='show matching albums instead of tracks'
)
list_cmd.parser.add_option(
'-p', '--path', action='store_true',
help='print paths for matched items or albums'
)
list_cmd.parser.add_option(
'-f', '--format', action='store',
help='print with custom format', default=''
)
list_cmd.parser.add_all_common_options()
list_cmd.func = list_func
default_commands.append(list_cmd)
@ -1087,10 +1075,8 @@ def update_func(lib, opts, args):
update_cmd = ui.Subcommand(
'update', help='update the library', aliases=('upd', 'up',)
)
update_cmd.parser.add_option(
'-a', '--album', action='store_true',
help='match albums instead of tracks'
)
update_cmd.parser.add_album_option()
update_cmd.parser.add_format_option()
update_cmd.parser.add_option(
'-M', '--nomove', action='store_false', default=True, dest='move',
help="don't move files in library"
@ -1099,10 +1085,6 @@ update_cmd.parser.add_option(
'-p', '--pretend', action='store_true',
help="show all changes but do nothing"
)
update_cmd.parser.add_option(
'-f', '--format', action='store',
help='print with custom format', default=''
)
update_cmd.func = update_func
default_commands.append(update_cmd)
@ -1151,10 +1133,7 @@ remove_cmd.parser.add_option(
"-d", "--delete", action="store_true",
help="also remove files from disk"
)
remove_cmd.parser.add_option(
'-a', '--album', action='store_true',
help='match albums instead of tracks'
)
remove_cmd.parser.add_album_option()
remove_cmd.func = remove_func
default_commands.append(remove_cmd)
@ -1348,18 +1327,12 @@ modify_cmd.parser.add_option(
'-W', '--nowrite', action='store_false', dest='write',
help="don't write metadata (opposite of -w)"
)
modify_cmd.parser.add_option(
'-a', '--album', action='store_true',
help='modify whole albums instead of tracks'
)
modify_cmd.parser.add_album_option()
modify_cmd.parser.add_format_option(target='item')
modify_cmd.parser.add_option(
'-y', '--yes', action='store_true',
help='skip confirmation'
)
modify_cmd.parser.add_option(
'-f', '--format', action='store',
help='print with custom format', default=''
)
modify_cmd.func = modify_func
default_commands.append(modify_cmd)
@ -1405,10 +1378,7 @@ move_cmd.parser.add_option(
'-c', '--copy', default=False, action='store_true',
help='copy instead of moving'
)
move_cmd.parser.add_option(
'-a', '--album', default=False, action='store_true',
help='match whole albums instead of tracks'
)
move_cmd.parser.add_album_option()
move_cmd.func = move_func
default_commands.append(move_cmd)

View file

@ -139,8 +139,6 @@ class ConvertPlugin(BeetsPlugin):
cmd = ui.Subcommand('convert', help='convert to external location')
cmd.parser.add_option('-p', '--pretend', action='store_true',
help='show actions but do nothing')
cmd.parser.add_option('-a', '--album', action='store_true',
help='choose albums instead of tracks')
cmd.parser.add_option('-t', '--threads', action='store', type='int',
help='change the number of threads, \
defaults to maximum available processors')
@ -148,11 +146,12 @@ class ConvertPlugin(BeetsPlugin):
dest='keep_new', help='keep only the converted \
and move the old files')
cmd.parser.add_option('-d', '--dest', action='store',
help='set the destination directory')
help='set the target format of the tracks')
cmd.parser.add_option('-f', '--format', action='store', dest='format',
help='set the destination directory')
cmd.parser.add_option('-y', '--yes', action='store_true', dest='yes',
help='do not ask for confirmation')
cmd.parser.add_album_option()
cmd.func = self.convert_func
return [cmd]
@ -359,7 +358,7 @@ class ConvertPlugin(BeetsPlugin):
self.config['pretend'].get(bool)
if not pretend:
ui.commands.list_items(lib, ui.decargs(args), opts.album, '')
ui.commands.list_items(lib, ui.decargs(args), opts.album)
if not (opts.yes or ui.input_yn("Convert? (Y/n)")):
return

View file

@ -125,17 +125,6 @@ class DuplicatesPlugin(BeetsPlugin):
self._command = Subcommand('duplicates',
help=__doc__,
aliases=['dup'])
self._command.parser.add_option('-f', '--format', dest='format',
action='store', type='string',
help='print with custom format',
metavar='FMT', default='')
self._command.parser.add_option('-a', '--album', dest='album',
action='store_true',
help='show duplicate albums instead of'
' tracks')
self._command.parser.add_option('-c', '--count', dest='count',
action='store_true',
help='show duplicate counts')
@ -168,15 +157,11 @@ class DuplicatesPlugin(BeetsPlugin):
action='store', metavar='DEST',
help='copy items to dest')
self._command.parser.add_option('-p', '--path', dest='path',
action='store_true',
help='print paths for matched items or'
' albums')
self._command.parser.add_option('-t', '--tag', dest='tag',
action='store',
help='tag matched items with \'k=v\''
' attribute')
self._command.parser.add_all_common_options()
def commands(self):

View file

@ -486,10 +486,7 @@ class EchonestMetadataPlugin(plugins.BeetsPlugin):
'-t', '--threshold', dest='threshold', action='store',
type='float', default=0.15, help='Set difference threshold'
)
sim_cmd.parser.add_option(
'-f', '--format', action='store', default='${difference}: ${path}',
help='print with custom format'
)
sim_cmd.parser.add_format_option()
def sim_func(lib, opts, args):
self.config.set_args(opts)

View file

@ -52,8 +52,7 @@ class MBSyncPlugin(BeetsPlugin):
cmd.parser.add_option('-W', '--nowrite', action='store_false',
default=config['import']['write'], dest='write',
help="don't write updated metadata to files")
cmd.parser.add_option('-f', '--format', action='store', default='',
help='print with custom format')
cmd.parser.add_format_option()
cmd.func = self.func
return [cmd]
@ -64,17 +63,16 @@ class MBSyncPlugin(BeetsPlugin):
pretend = opts.pretend
write = opts.write
query = ui.decargs(args)
fmt = opts.format
self.singletons(lib, query, move, pretend, write, fmt)
self.albums(lib, query, move, pretend, write, fmt)
self.singletons(lib, query, move, pretend, write)
self.albums(lib, query, move, pretend, write)
def singletons(self, lib, query, move, pretend, write, fmt):
def singletons(self, lib, query, move, pretend, write):
"""Retrieve and apply info from the autotagger for items matched by
query.
"""
for item in lib.items(query + ['singleton:true']):
item_formatted = format(item, fmt)
item_formatted = format(item)
if not item.mb_trackid:
self._log.info(u'Skipping singleton with no mb_trackid: {0}',
item_formatted)
@ -93,13 +91,13 @@ class MBSyncPlugin(BeetsPlugin):
autotag.apply_item_metadata(item, track_info)
apply_item_changes(lib, item, move, pretend, write)
def albums(self, lib, query, move, pretend, write, fmt):
def albums(self, lib, query, move, pretend, write):
"""Retrieve and apply info from the autotagger for albums matched by
query and their items.
"""
# Process matching albums.
for a in lib.albums(query):
album_formatted = format(a, fmt)
album_formatted = format(a)
if not a.mb_albumid:
self._log.info(u'Skipping album with no mb_albumid: {0}',
album_formatted)

View file

@ -94,19 +94,13 @@ class MissingPlugin(BeetsPlugin):
self._command = Subcommand('missing',
help=__doc__,
aliases=['miss'])
self._command.parser.add_option('-f', '--format', dest='format',
action='store', type='string',
help='print with custom FORMAT',
metavar='FORMAT', default='')
self._command.parser.add_option('-c', '--count', dest='count',
action='store_true',
help='count missing tracks per album')
self._command.parser.add_option('-t', '--total', dest='total',
action='store_true',
help='count total of missing tracks')
self._command.add_format_option()
def commands(self):
def _miss(lib, opts, args):

View file

@ -42,11 +42,7 @@ class PlayPlugin(BeetsPlugin):
'play',
help='send music to a player as a playlist'
)
play_command.parser.add_option(
'-a', '--album',
action='store_true', default=False,
help='query and load albums rather than tracks'
)
play_command.parser.add_album_option()
play_command.func = self.play_music
return [play_command]

View file

@ -26,7 +26,6 @@ from itertools import groupby
def random_item(lib, opts, args):
query = decargs(args)
fmt = '$path' if opts.path else opts.format
if opts.album:
objs = list(lib.albums(query))
@ -63,20 +62,15 @@ def random_item(lib, opts, args):
objs = random.sample(objs, number)
for item in objs:
print_(format(item, fmt))
print_(format(item))
random_cmd = Subcommand('random',
help='chose a random track or album')
random_cmd.parser.add_option('-a', '--album', action='store_true',
help='choose an album instead of track')
random_cmd.parser.add_option('-p', '--path', action='store_true',
help='print the path of the matched item')
random_cmd.parser.add_option('-f', '--format', action='store',
help='print with custom format', default='')
random_cmd.parser.add_option('-n', '--number', action='store', type="int",
help='number of objects to choose', default=1)
random_cmd.parser.add_option('-e', '--equal-chance', action='store_true',
help='each artist has the same chance')
random_cmd.parser.add_all_common_options()
random_cmd.func = random_item

View file

@ -867,7 +867,6 @@ class ReplayGainPlugin(BeetsPlugin):
self.handle_track(item, write)
cmd = ui.Subcommand('replaygain', help='analyze for ReplayGain')
cmd.parser.add_option('-a', '--album', action='store_true',
help='analyze albums instead of tracks')
cmd.parser.add_album_option()
cmd.func = func
return [cmd]

View file

@ -6,6 +6,9 @@ Changelog
Features:
* Beets now accept top-level options ``--format-item`` and ``--format-album``
before any subcommand to control how items and albums are displayed.
:bug:`1271`:
* :doc:`/plugins/replaygain`: There is a new backend for the `bs1770gain`_
tool. Thanks to :user:`jmwatte`. :bug:`1343`
* There are now multiple levels of verbosity. On the command line, you can
@ -69,10 +72,13 @@ Core changes:
``albumtotal`` computed attribute that provides the total number of tracks
on the album. (The :ref:`per_disc_numbering` option has no influence on this
field.)
* The :ref:`list_format_album` and :ref:`list_format_item` configuration keys
* The `list_format_album` and `list_format_item` configuration keys
now affect (almost) every place where objects are printed and logged.
(Previously, they only controlled the :ref:`list-cmd` command and a few
other scattered pieces.) :bug:`1269`
* `list_format_album` and `list_format_album` have respectively been
renamed :ref:`format_album` and :ref:`format_item`. The old names still work
but each triggers a warning message. :bug:`1271`
Fixes:
@ -120,6 +126,9 @@ Fixes:
For developers:
* the ``OptionParser`` is now a ``CommonOptionsParser`` that offers facilities
for adding usual options (``--album``, ``--path`` and ``--format``). See
:ref:`add_subcommands`. :bug:`1271`
* The logging system in beets has been overhauled. Plugins now each have their
own logger, which helps by automatically adjusting the verbosity level in
import mode and by prefixing the plugin's name. Logging levels are

View file

@ -87,8 +87,11 @@ The function should use any of the utility functions defined in ``beets.ui``.
Try running ``pydoc beets.ui`` to see what's available.
You can add command-line options to your new command using the ``parser`` member
of the ``Subcommand`` class, which is an ``OptionParser`` instance. Just use it
like you would a normal ``OptionParser`` in an independent script.
of the ``Subcommand`` class, which is a ``CommonOptionParser`` instance. Just
use it like you would a normal ``OptionParser`` in an independent script. Note
that it offers several methods to add common options: ``--album``, ``--path``
and ``--format``. This feature is versatile and extensively documented, try
``pydoc beets.ui.CommonOptionParser`` for more information.
.. _plugin_events:

View file

@ -61,7 +61,7 @@ file. The available options mirror the command-line options:
or album. This uses the same template syntax as beets'
:doc:`path formats</reference/pathformat>`. The usage is inspired by, and
therefore similar to, the :ref:`list <list-cmd>` command.
Default: :ref:`list_format_item`
Default: :ref:`format_item`
- **full**: List every track or album that has duplicates, not just the
duplicates themselves.
Default: ``no``.

View file

@ -34,5 +34,5 @@ The command has a few command-line options:
plugin will write new metadata to files' tags. To disable this, use the
``-W`` (``--nowrite``) option.
* To customize the output of unrecognized items, use the ``-f``
(``--format``) option. The default output is ``list_format_item`` or
``list_format_album`` for items and albums, respectively.
(``--format``) option. The default output is ``format_item`` or
``format_album`` for items and albums, respectively.

View file

@ -36,7 +36,7 @@ configuration file. The available options are:
track. This uses the same template syntax as beets'
:doc:`path formats </reference/pathformat>`. The usage is inspired by, and
therefore similar to, the :ref:`list <list-cmd>` command.
Default: :ref:`list_format_item`.
Default: :ref:`format_item`.
- **total**: Print a single count of missing tracks in all albums.
Default: ``no``.

View file

@ -162,25 +162,32 @@ Either ``yes`` or ``no``, indicating whether the autotagger should use
multiple threads. This makes things faster but may behave strangely.
Defaults to ``yes``.
.. _list_format_item:
list_format_item
~~~~~~~~~~~~~~~~
.. _list_format_item:
.. _format_item:
format_item
~~~~~~~~~~~
Format to use when listing *individual items* with the :ref:`list-cmd`
command and other commands that need to print out items. Defaults to
``$artist - $album - $title``. The ``-f`` command-line option overrides
this setting.
.. _list_format_album:
It used to be named `list_format_item`.
list_format_album
~~~~~~~~~~~~~~~~~
.. _list_format_album:
.. _format_album:
format_album
~~~~~~~~~~~~
Format to use when listing *albums* with :ref:`list-cmd` and other
commands. Defaults to ``$albumartist - $album``. The ``-f`` command-line
option overrides this setting.
It used to be named `list_format_album`.
.. _sort_item:
sort_item

View file

@ -988,7 +988,7 @@ class TemplateTest(_common.LibTestCase):
self.assertEqual(self.i.evaluate_template('$foo'), 'baz')
def test_album_and_item_format(self):
config['list_format_album'] = u'foö $foo'
config['format_album'] = u'foö $foo'
album = beets.library.Album()
album.foo = 'bar'
album.tagada = 'togodo'
@ -997,7 +997,7 @@ class TemplateTest(_common.LibTestCase):
self.assertEqual(unicode(album), u"foö bar")
self.assertEqual(str(album), b"fo\xc3\xb6 bar")
config['list_format_item'] = 'bar $foo'
config['format_item'] = 'bar $foo'
item = beets.library.Item()
item.foo = 'bar'
item.tagada = 'togodo'

View file

@ -73,8 +73,8 @@ class MbsyncCliTest(unittest.TestCase, TestHelper):
self.assertEqual(album.album, 'album info')
def test_message_when_skipping(self):
config['list_format_item'] = '$artist - $album - $title'
config['list_format_album'] = '$albumartist - $album'
config['format_item'] = '$artist - $album - $title'
config['format_album'] = '$albumartist - $album'
# Test album with no mb_albumid.
# The default format for an album include $albumartist so
@ -99,6 +99,10 @@ class MbsyncCliTest(unittest.TestCase, TestHelper):
e = "mbsync: Skipping album with no mb_albumid: 'album info'"
self.assertEqual(e, logs[0])
# restore the config
config['format_item'] = '$artist - $album - $title'
config['format_album'] = '$albumartist - $album'
# Test singleton with no mb_trackid.
# The default singleton format includes $artist and $album
# so we need to stub them here

View file

@ -1033,6 +1033,145 @@ class CompletionTest(_common.TestCase):
self.fail('test/test_completion.sh did not execute properly')
class CommonOptionsParserCliTest(unittest.TestCase, TestHelper):
"""Test CommonOptionsParser and formatting LibModel formatting on 'list'
command.
"""
def setUp(self):
self.setup_beets()
self.lib = library.Library(':memory:')
self.item = _common.item()
self.item.path = 'xxx/yyy'
self.lib.add(self.item)
self.lib.add_album([self.item])
def tearDown(self):
self.teardown_beets()
def test_base(self):
l = self.run_with_output('ls')
self.assertEqual(l, 'the artist - the album - the title\n')
l = self.run_with_output('ls', '-a')
self.assertEqual(l, 'the album artist - the album\n')
def test_path_option(self):
l = self.run_with_output('ls', '-p')
self.assertEqual(l, 'xxx/yyy\n')
l = self.run_with_output('ls', '-a', '-p')
self.assertEqual(l, 'xxx\n')
def test_format_option(self):
l = self.run_with_output('ls', '-f', '$artist')
self.assertEqual(l, 'the artist\n')
l = self.run_with_output('ls', '-a', '-f', '$albumartist')
self.assertEqual(l, 'the album artist\n')
def test_root_format_option(self):
l = self.run_with_output('--format-item', '$artist',
'--format-album', 'foo', 'ls')
self.assertEqual(l, 'the artist\n')
l = self.run_with_output('--format-item', 'foo',
'--format-album', '$albumartist', 'ls', '-a')
self.assertEqual(l, 'the album artist\n')
class CommonOptionsParserTest(unittest.TestCase, TestHelper):
def setUp(self):
self.setup_beets()
def tearDown(self):
self.teardown_beets()
def test_album_option(self):
parser = ui.CommonOptionsParser()
self.assertFalse(parser._album_flags)
parser.add_album_option()
self.assertTrue(bool(parser._album_flags))
self.assertEqual(parser.parse_args([]), ({'album': None}, []))
self.assertEqual(parser.parse_args(['-a']), ({'album': True}, []))
self.assertEqual(parser.parse_args(['--album']), ({'album': True}, []))
def test_path_option(self):
parser = ui.CommonOptionsParser()
parser.add_path_option()
self.assertFalse(parser._album_flags)
config['format_item'].set('$foo')
self.assertEqual(parser.parse_args([]), ({'path': None}, []))
self.assertEqual(config['format_item'].get(unicode), u'$foo')
self.assertEqual(parser.parse_args(['-p']),
({'path': True, 'format': '$path'}, []))
self.assertEqual(parser.parse_args(['--path']),
({'path': True, 'format': '$path'}, []))
self.assertEqual(config['format_item'].get(unicode), '$path')
self.assertEqual(config['format_album'].get(unicode), '$path')
def test_format_option(self):
parser = ui.CommonOptionsParser()
parser.add_format_option()
self.assertFalse(parser._album_flags)
config['format_item'].set('$foo')
self.assertEqual(parser.parse_args([]), ({'format': None}, []))
self.assertEqual(config['format_item'].get(unicode), u'$foo')
self.assertEqual(parser.parse_args(['-f', '$bar']),
({'format': '$bar'}, []))
self.assertEqual(parser.parse_args(['--format', '$baz']),
({'format': '$baz'}, []))
self.assertEqual(config['format_item'].get(unicode), '$baz')
self.assertEqual(config['format_album'].get(unicode), '$baz')
def test_format_option_with_target(self):
with self.assertRaises(KeyError):
ui.CommonOptionsParser().add_format_option(target='thingy')
parser = ui.CommonOptionsParser()
parser.add_format_option(target='item')
config['format_item'].set('$item')
config['format_album'].set('$album')
self.assertEqual(parser.parse_args(['-f', '$bar']),
({'format': '$bar'}, []))
self.assertEqual(config['format_item'].get(unicode), '$bar')
self.assertEqual(config['format_album'].get(unicode), '$album')
def test_format_option_with_album(self):
parser = ui.CommonOptionsParser()
parser.add_album_option()
parser.add_format_option()
config['format_item'].set('$item')
config['format_album'].set('$album')
parser.parse_args(['-f', '$bar'])
self.assertEqual(config['format_item'].get(unicode), '$bar')
self.assertEqual(config['format_album'].get(unicode), '$album')
parser.parse_args(['-a', '-f', '$foo'])
self.assertEqual(config['format_item'].get(unicode), '$bar')
self.assertEqual(config['format_album'].get(unicode), '$foo')
parser.parse_args(['-f', '$foo2', '-a'])
self.assertEqual(config['format_album'].get(unicode), '$foo2')
def test_add_all_common_options(self):
parser = ui.CommonOptionsParser()
parser.add_all_common_options()
self.assertEqual(parser.parse_args([]),
({'album': None, 'path': None, 'format': None}, []))
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)