mirror of
https://github.com/beetbox/beets.git
synced 2026-01-29 11:35:27 +01:00
FileOperationError: also include the file path
This makes the errors fully self-descriptive, which simplifies logging them as errors (you just have to `log.error(exc)` to get a reasonable message). We also now handle these at the top level in case someone forgets to add a handler. But in this case, we also send the full traceback to the debug log.
This commit is contained in:
parent
3a26845f5e
commit
05ebd6d28e
4 changed files with 34 additions and 19 deletions
|
|
@ -896,8 +896,7 @@ def manipulate_files(session):
|
|||
try:
|
||||
item.write()
|
||||
except library.FileOperationError as exc:
|
||||
log.error(u'could not write {0}: {1}'.format(
|
||||
util.displayable_path(item.path), exc))
|
||||
log.error(exc)
|
||||
|
||||
# Save new paths.
|
||||
with session.lib.transaction():
|
||||
|
|
|
|||
|
|
@ -265,28 +265,39 @@ class FileOperationError(Exception):
|
|||
Possibilities include an unsupported media type, a permissions
|
||||
error, and an unhandled Mutagen exception.
|
||||
"""
|
||||
def __init__(self, reason):
|
||||
"""Create an exception with the underlying (chained) exception
|
||||
`reason`.
|
||||
def __init__(self, path, reason):
|
||||
"""Create an exception describing an operation on the file at
|
||||
`path` with the underlying (chained) exception `reason`.
|
||||
"""
|
||||
super(FileOperationError, self).__init__(reason)
|
||||
super(FileOperationError, self).__init__(path, reason)
|
||||
self.path = path
|
||||
self.reason = reason
|
||||
|
||||
def __str__(self):
|
||||
"""Get a string representing the error. Uses the same string as
|
||||
the underlying reason.
|
||||
def __unicode__(self):
|
||||
"""Get a string representing the error. Describes both the
|
||||
underlying reason and the file path in question.
|
||||
"""
|
||||
return str(self.reason)
|
||||
return u'{0}: {1}'.format(
|
||||
util.displayable_path(self.path),
|
||||
unicode(self.reason)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return unicode(self).encode('utf8')
|
||||
|
||||
|
||||
class ReadError(FileOperationError):
|
||||
"""An error while reading a file (i.e. in `Item.read`).
|
||||
"""
|
||||
def __unicode__(self):
|
||||
return u'error reading ' + super(ReadError, self).__unicode__()
|
||||
|
||||
|
||||
class WriteError(FileOperationError):
|
||||
"""An error while writing a file (i.e. in `Item.write`).
|
||||
"""
|
||||
def __unicode__(self):
|
||||
return u'error writing ' + super(WriteError, self).__unicode__()
|
||||
|
||||
|
||||
|
||||
|
|
@ -384,7 +395,7 @@ class Item(LibModel):
|
|||
try:
|
||||
f = MediaFile(syspath(read_path))
|
||||
except (OSError, IOError) as exc:
|
||||
raise ReadError(exc)
|
||||
raise ReadError(read_path, exc)
|
||||
|
||||
for key in ITEM_KEYS_META:
|
||||
value = getattr(f, key)
|
||||
|
|
@ -406,12 +417,12 @@ class Item(LibModel):
|
|||
def write(self):
|
||||
"""Write the item's metadata to the associated file.
|
||||
|
||||
Can raises either a `ReadError` or a `WriteError`.
|
||||
Can raise either a `ReadError` or a `WriteError`.
|
||||
"""
|
||||
try:
|
||||
f = MediaFile(syspath(self.path))
|
||||
except (OSError, IOError) as exc:
|
||||
raise ReadError(exc)
|
||||
raise ReadError(self.path, exc)
|
||||
|
||||
plugins.send('write', item=self)
|
||||
|
||||
|
|
@ -420,7 +431,7 @@ class Item(LibModel):
|
|||
try:
|
||||
f.save(id3v23=beets.config['id3v23'].get(bool))
|
||||
except (OSError, IOError, MutagenError) as exc:
|
||||
raise WriteError(exc)
|
||||
raise WriteError(self.path, exc)
|
||||
|
||||
# The file has a new mtime.
|
||||
self.mtime = self.current_mtime()
|
||||
|
|
|
|||
|
|
@ -834,7 +834,7 @@ def vararg_callback(option, opt_str, value, parser):
|
|||
break
|
||||
value.append(arg)
|
||||
|
||||
del parser.rargs[:len(value)-1]
|
||||
del parser.rargs[:len(value) - 1]
|
||||
setattr(parser.values, option.dest, value)
|
||||
|
||||
|
||||
|
|
@ -960,8 +960,15 @@ def main(args=None):
|
|||
except util.HumanReadableException as exc:
|
||||
exc.log(log)
|
||||
sys.exit(1)
|
||||
except library.FileOperationError as exc:
|
||||
# These errors have reasonable human-readable descriptions, but
|
||||
# we still want to log their tracebacks for debugging.
|
||||
log.debug(traceback.format_exc())
|
||||
log.error(exc)
|
||||
sys.exit(1)
|
||||
except confit.ConfigError as exc:
|
||||
log.error(u'configuration error: {0}'.format(exc))
|
||||
sys.exit(1)
|
||||
except IOError as exc:
|
||||
if exc.errno == errno.EPIPE:
|
||||
# "Broken pipe". End silently.
|
||||
|
|
|
|||
|
|
@ -1137,8 +1137,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
|
|||
try:
|
||||
item.write()
|
||||
except library.FileOperationError as exc:
|
||||
log.error(u'could not write {0}: {1}'.format(
|
||||
util.displayable_path(item.path), exc))
|
||||
log.error(exc)
|
||||
|
||||
modify_cmd = ui.Subcommand('modify',
|
||||
help='change metadata fields', aliases=('mod',))
|
||||
|
|
@ -1238,8 +1237,7 @@ def write_items(lib, query, pretend):
|
|||
try:
|
||||
item.write()
|
||||
except library.FileOperationError as exc:
|
||||
log.error(u'could not write {0}: {1}'.format(
|
||||
util.displayable_path(item.path), exc))
|
||||
log.error(exc)
|
||||
|
||||
write_cmd = ui.Subcommand('write', help='write tag information to files')
|
||||
write_cmd.parser.add_option('-p', '--pretend', action='store_true',
|
||||
|
|
|
|||
Loading…
Reference in a new issue