Handle exceptions in item.write and use plugin abort

This commit is contained in:
Thomas Scholtes 2014-03-13 13:46:07 +01:00
parent a744e8ea59
commit a399f294e8
5 changed files with 27 additions and 39 deletions

View file

@ -894,7 +894,7 @@ def manipulate_files(session):
item.move(True)
if config['import']['write'] and task.should_write_tags():
ui.commands._item_write(item)
item.write()
# Save new paths.
with session.lib.transaction():

View file

@ -371,15 +371,26 @@ class Item(LibModel):
self.path = read_path
def write(self):
"""Writes the item's metadata to the associated file.
"""Try to write the item's metadata to the associated file.
Returns ``True`` if the write was successful. The method catches
file system read and write exceptions and logs an error message.
If any of 'write' event handlers returns a truthy value the
write will not be performed and an error message is logged.
"""
plugins.send('write', item=self)
if any(plugins.send('write', item=self)):
log.error(u'plugin aborted writing {0}'.format(
util.displayable_path(item.path)))
return
try:
f = MediaFile(syspath(self.path))
except (OSError, IOError) as exc:
raise util.FilesystemError(exc, 'read', (self.path,),
traceback.format_exc())
log.error(u'could not read {0}: {1}'.format(
util.displayable_path(item.path), exc
))
return
for key in ITEM_KEYS_WRITABLE:
setattr(f, key, self[key])
@ -387,12 +398,15 @@ class Item(LibModel):
try:
f.save(id3v23=beets.config['id3v23'].get(bool))
except (OSError, IOError) as exc:
raise util.FilesystemError(exc, 'write', (self.path,),
traceback.format_exc())
log.error(u'could not write {0}: {1}'.format(
util.displayable_path(item.path), exc
))
return
# The file has a new mtime.
self.mtime = self.current_mtime()
plugins.send('after_write', item=self)
return True
# Files themselves.

View file

@ -30,15 +30,6 @@ LASTFM_KEY = '2dc3914abf35f0d9c92d97d8f8e42b43'
log = logging.getLogger('beets')
class BeforeWriteError(Exception):
"""May be raised by plugins in a ``write`` event handler to abort
prevent writing the item's file.
Beets will catch this exception during a call to ``item.write()``
and display it to the user as an error.
"""
# Managing the plugins themselves.
class BeetsPlugin(object):
@ -362,10 +353,7 @@ def send(event, **arguments):
name of the event to send, all other named arguments go to the
event handler(s).
Returns the number of handlers called.
Returns a list of return values from the handlers.
"""
log.debug('Sending event: %s' % event)
handlers = event_handlers()[event]
for handler in handlers:
handler(**arguments)
return len(handlers)
return [handler(**arguments) for handler in event_handlers()[event]]

View file

@ -76,20 +76,6 @@ def _do_query(lib, query, album, also_items=True):
return items, albums
def _item_write(item):
"""Wrapper for ``item.write()`` that handles exceptions and logs
errors during user interaction.
"""
try:
item.write()
except (mediafile.UnreadableFileError,
util.FilesystemError,
plugins.BeforeWriteError) as exc:
log.error(u'could not write {0}: {1}'.format(
util.displayable_path(item.path), exc
))
# fields: Shows a list of available fields for queries and format strings.
@ -1148,7 +1134,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
else:
changed_items = changed
for item in changed_items:
_item_write(item)
item.write()
modify_cmd = ui.Subcommand('modify',
help='change metadata fields', aliases=('mod',))
@ -1245,7 +1231,7 @@ def write_items(lib, query, pretend):
changed = ui.show_model_changes(item, clean_item,
library.ITEM_KEYS_WRITABLE, always=True)
if changed and not pretend:
_item_write(item)
item.write()
write_cmd = ui.Subcommand('write', help='write tag information to files')
write_cmd.parser.add_option('-p', '--pretend', action='store_true',

View file

@ -142,8 +142,8 @@ currently available are:
* *write*: called with an ``Item`` object just before a file's metadata is
written to disk (i.e., just before the file on disk is opened). Event
handlers may raise ``plugins.BeforeWriteError`` to prevent beets from
writing the file and display an error to the user.
handlers may return a truthy value to prevent beets from writing the
file. In that case make sure that you log an appropriate message.
* *after_write*: called with an ``Item`` object after a file's metadata is
written to disk (i.e., just after the file on disk is closed).