From 11e3a5a923dfa878af6147cdd758aa38ac3328d5 Mon Sep 17 00:00:00 2001 From: Mary011196 Date: Wed, 12 Apr 2017 00:38:58 +0300 Subject: [PATCH 01/12] OperationalError from SQlite that indicates a permissions problem on database file. --- beets/ui/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index 29e228497d..d208aa6926 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -1159,10 +1159,13 @@ def _open_library(config): ) lib.get_item(0) # Test database connection. except (sqlite3.OperationalError, sqlite3.DatabaseError): + message = "" log.debug(u'{}', traceback.format_exc()) - raise UserError(u"database file {0} could not be opened".format( + if e.args[0] == "unable to open database file": + message = "It might be a permissions problem." + raise UserError(u"database file {0} could not be opened.%s".format( util.displayable_path(dbpath) - )) + )%message) log.debug(u'library database: {0}\n' u'library directory: {1}', util.displayable_path(lib.path), From 3fd04ad642f42439a24c1693c0b90f9c05e86d37 Mon Sep 17 00:00:00 2001 From: Mary011196 Date: Fri, 14 Apr 2017 09:28:13 +0300 Subject: [PATCH 02/12] adding AccessFileError as new class error excpetion --- beets/dbcore/db.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 6b0ed8b43e..89b7bd17b6 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -32,6 +32,10 @@ from .query import MatchQuery, NullSort, TrueQuery import six +class AccessFileError(Exception): + """UI exception. Commands should throw this in order to display + nonrecoverable errors to the user. + """ class FormattedMapping(collections.Mapping): """A `dict`-like formatted view of a model. @@ -680,8 +684,11 @@ def mutate(self, statement, subvals=()): """Execute an SQL statement with substitution values and return the row ID of the last affected row. """ - cursor = self.db._connection().execute(statement, subvals) - return cursor.lastrowid + + cursor = self.db._connection().execute(statement, subvals) + raise AccessFileError("unable to open database file. It might be a permissions problem") + return cursor.lastrowid + def script(self, statements): """Execute a string containing multiple SQL statements.""" From bccfcb6b4f8707741ecef510a091305d915d5a8b Mon Sep 17 00:00:00 2001 From: Mary011196 Date: Fri, 14 Apr 2017 09:33:23 +0300 Subject: [PATCH 03/12] handlinig AccessFileError in main --- beets/ui/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index d208aa6926..7720c93587 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -41,6 +41,7 @@ from beets.util import confit, as_string from beets.autotag import mb from beets.dbcore import query as db_query +from beets.dbcore import db import six # On Windows platforms, use colorama to support "ANSI" terminal colors. @@ -1159,13 +1160,10 @@ def _open_library(config): ) lib.get_item(0) # Test database connection. except (sqlite3.OperationalError, sqlite3.DatabaseError): - message = "" log.debug(u'{}', traceback.format_exc()) - if e.args[0] == "unable to open database file": - message = "It might be a permissions problem." - raise UserError(u"database file {0} could not be opened.%s".format( + raise UserError(u"database file {0} could not be opened".format( util.displayable_path(dbpath) - )%message) + )) log.debug(u'library database: {0}\n' u'library directory: {1}', util.displayable_path(lib.path), @@ -1250,3 +1248,6 @@ def main(args=None): except KeyboardInterrupt: # Silently ignore ^C except in verbose mode. log.debug(u'{}', traceback.format_exc()) + except db.AccessFileError as exc: + log.error(u'{0}',exc) + sys.exit(1) From cb2f47d8d918ed7845cec899515cde48627e4411 Mon Sep 17 00:00:00 2001 From: Mary Koliopoulou Date: Fri, 14 Apr 2017 09:41:24 +0300 Subject: [PATCH 04/12] Update db.py --- beets/dbcore/db.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 89b7bd17b6..24ee4a1d73 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -685,9 +685,9 @@ def mutate(self, statement, subvals=()): the row ID of the last affected row. """ - cursor = self.db._connection().execute(statement, subvals) - raise AccessFileError("unable to open database file. It might be a permissions problem") - return cursor.lastrowid + cursor = self.db._connection().execute(statement, subvals) + raise AccessFileError("unable to open database file. It might be a permissions problem") + return cursor.lastrowid def script(self, statements): From cf744eb1f7519ba75f3b85d6abc45e188f2a5f91 Mon Sep 17 00:00:00 2001 From: Mary Koliopoulou Date: Fri, 14 Apr 2017 19:42:23 +0300 Subject: [PATCH 05/12] Update db.py --- beets/dbcore/db.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 24ee4a1d73..df42ab05fb 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -684,17 +684,17 @@ def mutate(self, statement, subvals=()): """Execute an SQL statement with substitution values and return the row ID of the last affected row. """ - - cursor = self.db._connection().execute(statement, subvals) - raise AccessFileError("unable to open database file. It might be a permissions problem") - return cursor.lastrowid + try: + cursor = self.db._connection().execute(statement, subvals) + return cursor.lastrowid + except sqlite3.OperationalError as e: + raise AccessFileError("unable to open database file. It might be a permissions problem") def script(self, statements): """Execute a string containing multiple SQL statements.""" self.db._connection().executescript(statements) - class Database(object): """A container for Model objects that wraps an SQLite database as the backend. From f7a58447f0230e83b76f68c5423e20708830ba93 Mon Sep 17 00:00:00 2001 From: Mary011196 Date: Sun, 16 Apr 2017 15:15:45 +0300 Subject: [PATCH 06/12] Change the error name to DBAccessError --- beets/ui/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index 7720c93587..ecd8f01f1f 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -1248,6 +1248,6 @@ def main(args=None): except KeyboardInterrupt: # Silently ignore ^C except in verbose mode. log.debug(u'{}', traceback.format_exc()) - except db.AccessFileError as exc: - log.error(u'{0}',exc) + except db.DBAccessError as exc: + log.error(u'{0}', exc) sys.exit(1) From 0492741bf7013219cc4eca9813b4c46859b839f5 Mon Sep 17 00:00:00 2001 From: Mary011196 Date: Sun, 16 Apr 2017 15:18:40 +0300 Subject: [PATCH 07/12] Error check with if statemetnt --- beets/dbcore/db.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index df42ab05fb..bfc870b388 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -32,11 +32,15 @@ from .query import MatchQuery, NullSort, TrueQuery import six -class AccessFileError(Exception): - """UI exception. Commands should throw this in order to display - nonrecoverable errors to the user. + +class DBAccessError(Exception): + """The SQLite database became inaccessible. + This can happen when trying to read or write the + database when, for example, the database file is deleted or otherwise disappears. + There is probably no way to recover from this error. """ + class FormattedMapping(collections.Mapping): """A `dict`-like formatted view of a model. @@ -688,13 +692,16 @@ def mutate(self, statement, subvals=()): cursor = self.db._connection().execute(statement, subvals) return cursor.lastrowid except sqlite3.OperationalError as e: - raise AccessFileError("unable to open database file. It might be a permissions problem") - + if e.args[0] in ('unable to open database file', + 'attempt to write a readonly database file'): + raise DBAccessError("Unable to open database file." \ + "It might be a permissions problem.") def script(self, statements): """Execute a string containing multiple SQL statements.""" self.db._connection().executescript(statements) + class Database(object): """A container for Model objects that wraps an SQLite database as the backend. From e756f988454a8c3c2a3fbdad76a8592a71aea24a Mon Sep 17 00:00:00 2001 From: Mary011196 Date: Sun, 16 Apr 2017 17:24:48 +0300 Subject: [PATCH 08/12] Correcting the mistakes that Travis CI showed me --- beets/dbcore/db.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index bfc870b388..c9f1f72a91 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -36,7 +36,8 @@ class DBAccessError(Exception): """The SQLite database became inaccessible. This can happen when trying to read or write the - database when, for example, the database file is deleted or otherwise disappears. + database when, for example, the database file + is deleted or otherwise disappears. There is probably no way to recover from this error. """ @@ -692,10 +693,10 @@ def mutate(self, statement, subvals=()): cursor = self.db._connection().execute(statement, subvals) return cursor.lastrowid except sqlite3.OperationalError as e: - if e.args[0] in ('unable to open database file', - 'attempt to write a readonly database file'): - raise DBAccessError("Unable to open database file." \ - "It might be a permissions problem.") + if e.args[0] in ("attempt to write a readonly database", + "unable to open database file"): + raise DBAccessError('Unable to open database file.' + 'It might be a permissions problem') def script(self, statements): """Execute a string containing multiple SQL statements.""" From 621427fa636050bfd96b3e388ab24aff8a061c79 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Tue, 18 Apr 2017 10:31:09 -0400 Subject: [PATCH 09/12] Reformat a docstring --- beets/dbcore/db.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index c9f1f72a91..058b52ff5d 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -35,10 +35,10 @@ class DBAccessError(Exception): """The SQLite database became inaccessible. - This can happen when trying to read or write the - database when, for example, the database file - is deleted or otherwise disappears. - There is probably no way to recover from this error. + + This can happen when trying to read or write the database when, for + example, the database file is deleted or otherwise disappears. There + is probably no way to recover from this error. """ From 19e09585d8fc43cf59433af5ed8229acccaf8f12 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Tue, 18 Apr 2017 10:32:44 -0400 Subject: [PATCH 10/12] Re-raise other errors And re-use the SQLite error string instead of a hand-written one for now. --- beets/dbcore/db.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 058b52ff5d..ef7231a762 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -693,10 +693,14 @@ def mutate(self, statement, subvals=()): cursor = self.db._connection().execute(statement, subvals) return cursor.lastrowid except sqlite3.OperationalError as e: + # In two specific cases, SQLite reports an error while accessing + # the underlying database file. We surface these exceptions as + # DBAccessError so the application can abort. if e.args[0] in ("attempt to write a readonly database", "unable to open database file"): - raise DBAccessError('Unable to open database file.' - 'It might be a permissions problem') + raise DBAccessError(e.args[0]) + else: + raise def script(self, statements): """Execute a string containing multiple SQL statements.""" From 906bd97d46f2535c271923ea879f9d1a45f46176 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Tue, 18 Apr 2017 17:57:13 -0400 Subject: [PATCH 11/12] Hint about database access errors --- beets/ui/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index ecd8f01f1f..39d3d52ac8 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -1249,5 +1249,9 @@ def main(args=None): # Silently ignore ^C except in verbose mode. log.debug(u'{}', traceback.format_exc()) except db.DBAccessError as exc: - log.error(u'{0}', exc) + log.error( + u'database access error: {0}\n' + u'the library file might have a permissions problem', + exc + ) sys.exit(1) From 7eaaa995668064aad334b75ba16456df9e532556 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Tue, 18 Apr 2017 17:59:12 -0400 Subject: [PATCH 12/12] Changelog entry for #2508 --- docs/changelog.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 37c1ef9fb0..14e0f98fc7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -84,6 +84,8 @@ Fixes: AAC codec instead of faac. Thanks to :user:`jansol`. :bug:`2484` * Fix import of multidisc releases with subdirectories, which previously made each disc be imported separately in different releases. :bug:`2493` +* When the SQLite database stops being accessible, we now print a friendly + error message. Thanks to :user:`Mary011196`. :bug:`1676` :bug:`2508` 1.4.3 (January 9, 2017)