From: Mike Bayer Date: Mon, 30 Aug 2021 02:17:13 +0000 (-0400) Subject: ensure pysqlite dialect enumerates correct isolation levels X-Git-Tag: rel_1_4_24~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=916bd20b6d1e7c153d334c31cf0882f502e3815e;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git ensure pysqlite dialect enumerates correct isolation levels Fixed bug where the error message for SQLite invalid isolation level on the pysqlite driver would fail to indicate that "AUTOCOMMIT" is one of the valid isolation levels. Change-Id: Icbceab9a28af6a560859761fa92320b5473269a9 References: #6959 --- diff --git a/doc/build/changelog/unreleased_14/sqlite_autocommit.rst b/doc/build/changelog/unreleased_14/sqlite_autocommit.rst new file mode 100644 index 0000000000..183e0eeed4 --- /dev/null +++ b/doc/build/changelog/unreleased_14/sqlite_autocommit.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: bug, sqlite + + Fixed bug where the error message for SQLite invalid isolation level on the + pysqlite driver would fail to indicate that "AUTOCOMMIT" is one of the + valid isolation levels. diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index dc5ebc3f0c..c4a6bf8e9e 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -1915,7 +1915,9 @@ class SQLiteDialect(default.DefaultDialect): 14, ) - _isolation_lookup = {"READ UNCOMMITTED": 1, "SERIALIZABLE": 0} + _isolation_lookup = util.immutabledict( + {"READ UNCOMMITTED": 1, "SERIALIZABLE": 0} + ) def set_isolation_level(self, connection, level): try: @@ -1925,7 +1927,11 @@ class SQLiteDialect(default.DefaultDialect): exc.ArgumentError( "Invalid value '%s' for isolation_level. " "Valid isolation levels for %s are %s" - % (level, self.name, ", ".join(self._isolation_lookup)) + % ( + level, + self.name, + ", ".join(self._isolation_lookup), + ) ), replace_context=err, ) diff --git a/lib/sqlalchemy/dialects/sqlite/pysqlite.py b/lib/sqlalchemy/dialects/sqlite/pysqlite.py index 96a5351dae..0f96e88303 100644 --- a/lib/sqlalchemy/dialects/sqlite/pysqlite.py +++ b/lib/sqlalchemy/dialects/sqlite/pysqlite.py @@ -492,6 +492,12 @@ class SQLiteDialect_pysqlite(SQLiteDialect): def _get_server_version_info(self, connection): return self.dbapi.sqlite_version_info + _isolation_lookup = SQLiteDialect._isolation_lookup.union( + { + "AUTOCOMMIT": None, + } + ) + def set_isolation_level(self, connection, level): if hasattr(connection, "connection"): dbapi_connection = connection.connection diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index 742a9a1f81..e6e5db774f 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -1226,6 +1226,10 @@ class SuiteRequirements(Requirements): def pep520(self): return self.python36 + @property + def insert_order_dicts(self): + return self.python37 + @property def python36(self): return exclusions.skip_if( diff --git a/test/dialect/test_sqlite.py b/test/dialect/test_sqlite.py index b5ce291eb1..ed0f11907c 100644 --- a/test/dialect/test_sqlite.py +++ b/test/dialect/test_sqlite.py @@ -54,6 +54,7 @@ from sqlalchemy.testing import expect_warnings from sqlalchemy.testing import fixtures from sqlalchemy.testing import is_ from sqlalchemy.testing import mock +from sqlalchemy.testing.assertions import expect_raises_message from sqlalchemy.types import Boolean from sqlalchemy.types import Date from sqlalchemy.types import DateTime @@ -596,6 +597,22 @@ class DialectTest( ) ) + @testing.requires.insert_order_dicts + @testing.only_on("sqlite+pysqlite") + def test_isolation_level_message(self): + # needs to test that all three words are present and we also + # dont want to default all isolation level messages to use + # sorted(), so rely on python 3.7 for ordering of keywords + # in the message + with expect_raises_message( + exc.ArgumentError, + "Invalid value 'invalid' for " + "isolation_level. Valid isolation levels for " + "sqlite are READ UNCOMMITTED, SERIALIZABLE, AUTOCOMMIT", + ): + with testing.db.connect() as conn: + conn.execution_options(isolation_level="invalid") + @testing.only_on("sqlite+pysqlcipher") def test_pysqlcipher_connects(self): """test #6586"""