Implement --pretend option for the move command

The method `show_path_changes` takes a list of tuples (source, destination)
that will be printed on either single / double line, as proposed in #1405.
This commit is contained in:
Tom Jaspers 2015-05-20 12:53:43 +02:00
parent adebb850b5
commit be484f2af0
5 changed files with 76 additions and 10 deletions

View file

@ -615,6 +615,44 @@ def show_model_changes(new, old=None, fields=None, always=False):
return bool(changes)
def show_path_changes(path_changes):
""" Given a list of tuples (source, destination) that indicate the path
changes, the changes are shown. Output is guaranteed to be unicode.
Every tuple is shown on a single line if the terminal width permits it,
else it is split over two lines. E.g.,
Source -> Destination
vs.
Source
-> Destination
"""
sources, destinations = zip(*path_changes)
# Ensure unicode output
sources = map(util.displayable_path, sources)
destinations = map(util.displayable_path, destinations)
# Calculate widths for terminal split
col_width = (term_width() - len(' -> ')) // 2
max_width = len(max(sources + destinations, key=len))
if max_width > col_width:
# Print every change over two lines
for source, dest in zip(sources, destinations):
log.info(u'{0} \n -> {1}', source, dest)
else:
# Print every change on a single line, and add a header
title_pad = max_width - len('Source ') + len(' -> ')
log.info(u'Source {0} Destination', ' ' * title_pad)
for source, dest in zip(sources, destinations):
pad = max_width - len(source)
log.info(u'{0} {1} -> {2}', source, ' ' * pad, dest)
class CommonOptionsParser(optparse.OptionParser, object):
"""Offers a simple way to add common formatting options.

View file

@ -24,7 +24,7 @@ import re
import beets
from beets import ui
from beets.ui import print_, input_, decargs
from beets.ui import print_, input_, decargs, show_path_changes
from beets import autotag
from beets.autotag import Recommendation
from beets.autotag import hooks
@ -1344,7 +1344,7 @@ default_commands.append(modify_cmd)
# move: Move/copy files to the library or a new base directory.
def move_items(lib, dest, query, copy, album):
def move_items(lib, dest, query, copy, album, pretend):
"""Moves or copies items to a new base directory, given by dest. If
dest is None, then the library's base directory is used, making the
command "consolidate" files.
@ -1355,11 +1355,19 @@ def move_items(lib, dest, query, copy, album):
action = 'Copying' if copy else 'Moving'
entity = 'album' if album else 'item'
log.info(u'{0} {1} {2}s.', action, len(objs), entity)
for obj in objs:
log.debug(u'moving: {0}', util.displayable_path(obj.path))
if pretend:
if album:
show_path_changes([(item.path, item.destination(basedir=dest))
for obj in objs for item in obj.items()])
else:
show_path_changes([(obj.path, obj.destination(basedir=dest))
for obj in objs])
else:
for obj in objs:
log.debug(u'moving: {0}', util.displayable_path(obj.path))
obj.move(copy, basedir=dest)
obj.store()
obj.move(copy, basedir=dest)
obj.store()
def move_func(lib, opts, args):
@ -1369,7 +1377,7 @@ def move_func(lib, opts, args):
if not os.path.isdir(dest):
raise ui.UserError('no such directory: %s' % dest)
move_items(lib, dest, decargs(args), opts.copy, opts.album)
move_items(lib, dest, decargs(args), opts.copy, opts.album, opts.pretend)
move_cmd = ui.Subcommand(
@ -1383,6 +1391,9 @@ move_cmd.parser.add_option(
'-c', '--copy', default=False, action='store_true',
help='copy instead of moving'
)
move_cmd.parser.add_option(
'-p', '--pretend', default=False, action='store_true',
help='show how files would be moved, but don\'t touch anything')
move_cmd.parser.add_album_option()
move_cmd.func = move_func
default_commands.append(move_cmd)

View file

@ -13,6 +13,8 @@ New features:
This plugin is still in an experimental phase. :bug:`1450`
* The :doc:`/plugins/fetchart` plugin will now complain for the `enforce_ratio`
and `min_width` options if no local imaging backend is available. :bug:`1460`
* The `move` command has a new `-p/--pretend` option, making the command show
how the items will be moved, without modifying the files on disk.
Fixes:

View file

@ -245,7 +245,7 @@ move
````
::
beet move [-ca] [-d DIR] QUERY
beet move [-cap] [-d DIR] QUERY
Move or copy items in your library.
@ -255,6 +255,10 @@ destination directory with ``-d`` manually, you can move items matching a query
anywhere in your filesystem. The ``-c`` option copies files instead of moving
them. As with other commands, the ``-a`` option matches albums instead of items.
To perform a "dry run", just use the ``-p`` (for "pretend") flag. This will
show you all how the files would be moved but won't actually change anything
on disk.
.. _update-cmd:
update

View file

@ -373,8 +373,9 @@ class MoveTest(_common.TestCase):
# Alternate destination directory.
self.otherdir = os.path.join(self.temp_dir, 'testotherdir')
def _move(self, query=(), dest=None, copy=False, album=False):
commands.move_items(self.lib, dest, query, copy, album)
def _move(self, query=(), dest=None, copy=False, album=False,
pretend=False):
commands.move_items(self.lib, dest, query, copy, album, pretend)
def test_move_item(self):
self._move()
@ -418,6 +419,16 @@ class MoveTest(_common.TestCase):
self.assertExists(self.i.path)
self.assertNotExists(self.itempath)
def test_pretend_move_item(self):
self._move(dest=self.otherdir, pretend=True)
self.i.load()
self.assertIn('srcfile', self.i.path)
def test_pretend_move_album(self):
self._move(album=True, pretend=True)
self.i.load()
self.assertIn('srcfile', self.i.path)
class UpdateTest(_common.TestCase):
def setUp(self):