[Python-checkins] bpo-45613: Set `sqlite3.threadsafety` dynamically (GH-29227)

pablogsal webhook-mailer at python.org
Wed Nov 3 17:01:47 EDT 2021


https://github.com/python/cpython/commit/c2739867113a2b650db300c03ef06cf18dcee3f4
commit: c2739867113a2b650db300c03ef06cf18dcee3f4
branch: main
author: Erlend Egeberg Aasland <erlend.aasland at innova.no>
committer: pablogsal <Pablogsal at gmail.com>
date: 2021-11-03T21:01:37Z
summary:

bpo-45613: Set `sqlite3.threadsafety` dynamically (GH-29227)

Use the compile-time selected default SQLite threaded mode to set the
DB-API 2.0 attribute 'threadsafety'

Mappings:
  - SQLITE_THREADSAFE=0 => threadsafety=0
  - SQLITE_THREADSAFE=1 => threadsafety=3
  - SQLITE_THREADSAFE=2 => threadsafety=1

files:
A Misc/NEWS.d/next/Library/2021-10-26-14-29-54.bpo-45613.55Ie3c.rst
M Doc/library/sqlite3.rst
M Doc/whatsnew/3.11.rst
M Lib/sqlite3/dbapi2.py
M Lib/test/test_sqlite3/test_dbapi.py
M Modules/_sqlite/module.c

diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index dcffc779cfba3..91bf7b0e7c879 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -159,23 +159,41 @@ Module functions and constants
 
 .. data:: threadsafety
 
-   Integer constant required by the DB-API, stating the level of thread safety
-   the :mod:`sqlite3` module supports. Currently hard-coded to ``1``, meaning
-   *"Threads may share the module, but not connections."* However, this may not
-   always be true. You can check the underlying SQLite library's compile-time
-   threaded mode using the following query::
-
-     import sqlite3
-     con = sqlite3.connect(":memory:")
-     con.execute("""
-         select * from pragma_compile_options
-         where compile_options like 'THREADSAFE=%'
-     """).fetchall()
-
-   Note that the `SQLITE_THREADSAFE levels
-   <https://sqlite.org/compile.html#threadsafe>`_ do not match the DB-API 2.0
-   ``threadsafety`` levels.
-
+   Integer constant required by the DB-API 2.0, stating the level of thread
+   safety the :mod:`sqlite3` module supports. This attribute is set based on
+   the default `threading mode <https://sqlite.org/threadsafe.html>`_ the
+   underlying SQLite library is compiled with. The SQLite threading modes are:
+
+     1. **Single-thread**: In this mode, all mutexes are disabled and SQLite is
+        unsafe to use in more than a single thread at once.
+     2. **Multi-thread**: In this mode, SQLite can be safely used by multiple
+        threads provided that no single database connection is used
+        simultaneously in two or more threads.
+     3. **Serialized**: In serialized mode, SQLite can be safely used by
+        multiple threads with no restriction.
+
+   The mappings from SQLite threading modes to DB-API 2.0 threadsafety levels
+   are as follows:
+
+   +------------------+-----------------+----------------------+-------------------------------+
+   | SQLite threading | `threadsafety`_ | `SQLITE_THREADSAFE`_ | DB-API 2.0 meaning            |
+   | mode             |                 |                      |                               |
+   +==================+=================+======================+===============================+
+   | single-thread    | 0               | 0                    | Threads may not share the     |
+   |                  |                 |                      | module                        |
+   +------------------+-----------------+----------------------+-------------------------------+
+   | multi-thread     | 1               | 2                    | Threads may share the module, |
+   |                  |                 |                      | but not connections           |
+   +------------------+-----------------+----------------------+-------------------------------+
+   | serialized       | 3               | 1                    | Threads may share the module, |
+   |                  |                 |                      | connections and cursors       |
+   +------------------+-----------------+----------------------+-------------------------------+
+
+   .. _threadsafety: https://www.python.org/dev/peps/pep-0249/#threadsafety
+   .. _SQLITE_THREADSAFE: https://sqlite.org/compile.html#threadsafe
+
+   .. versionchanged:: 3.11
+      Set *threadsafety* dynamically instead of hard-coding it to ``1``.
 
 .. data:: PARSE_DECLTYPES
 
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 26d0dbb32af0b..7b15ace403170 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -254,6 +254,10 @@ sqlite3
   setting and getting SQLite limits by connection basis.
   (Contributed by Erlend E. Aasland in :issue:`45243`.)
 
+* :mod:`sqlite3` now sets :attr:`sqlite3.threadsafety` based on the default
+  threading mode the underlying SQLite library has been compiled with.
+  (Contributed by Erlend E. Aasland in :issue:`45613`.)
+
 
 threading
 ---------
diff --git a/Lib/sqlite3/dbapi2.py b/Lib/sqlite3/dbapi2.py
index cfe6225f46efc..7cf4dd32d541d 100644
--- a/Lib/sqlite3/dbapi2.py
+++ b/Lib/sqlite3/dbapi2.py
@@ -28,8 +28,6 @@
 
 paramstyle = "qmark"
 
-threadsafety = 1
-
 apilevel = "2.0"
 
 Date = datetime.date
diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py
index 998b7cb8c7fc0..ba3652a04a2d8 100644
--- a/Lib/test/test_sqlite3/test_dbapi.py
+++ b/Lib/test/test_sqlite3/test_dbapi.py
@@ -54,8 +54,9 @@ def test_api_level(self):
                          "apilevel is %s, should be 2.0" % sqlite.apilevel)
 
     def test_thread_safety(self):
-        self.assertEqual(sqlite.threadsafety, 1,
-                         "threadsafety is %d, should be 1" % sqlite.threadsafety)
+        self.assertIn(sqlite.threadsafety, {0, 1, 3},
+                      "threadsafety is %d, should be 0, 1 or 3" %
+                      sqlite.threadsafety)
 
     def test_param_style(self):
         self.assertEqual(sqlite.paramstyle, "qmark",
diff --git a/Misc/NEWS.d/next/Library/2021-10-26-14-29-54.bpo-45613.55Ie3c.rst b/Misc/NEWS.d/next/Library/2021-10-26-14-29-54.bpo-45613.55Ie3c.rst
new file mode 100644
index 0000000000000..ac0937b54aeea
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-10-26-14-29-54.bpo-45613.55Ie3c.rst
@@ -0,0 +1,3 @@
+:mod:`sqlite3` now sets :attr:`sqlite3.threadsafety` based on the default
+threading mode the underlying SQLite library has been compiled with. Patch by
+Erlend E. Aasland.
diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c
index 3bca33b8e8c27..3b9f79799b5c5 100644
--- a/Modules/_sqlite/module.c
+++ b/Modules/_sqlite/module.c
@@ -540,6 +540,28 @@ add_integer_constants(PyObject *module) {
     return 0;
 }
 
+/* Convert SQLite default threading mode (as set by the compile-time constant
+ * SQLITE_THREADSAFE) to the corresponding DB-API 2.0 (PEP 249) threadsafety
+ * level. */
+static int
+get_threadsafety(pysqlite_state *state)
+{
+    int mode = sqlite3_threadsafe();
+    switch (mode) {
+    case 0:        // Single-thread mode; threads may not share the module.
+        return 0;
+    case 1:        // Serialized mode; threads may share the module,
+        return 3;  // connections, and cursors.
+    case 2:        // Multi-thread mode; threads may share the module, but not
+        return 1;  // connections.
+    default:
+        PyErr_Format(state->InterfaceError,
+                     "Unable to interpret SQLite threadsafety mode. Got %d, "
+                     "expected 0, 1, or 2", mode);
+        return -1;
+    }
+}
+
 static int
 module_traverse(PyObject *module, visitproc visit, void *arg)
 {
@@ -689,6 +711,14 @@ module_exec(PyObject *module)
         goto error;
     }
 
+    int threadsafety = get_threadsafety(state);
+    if (threadsafety < 0) {
+        goto error;
+    }
+    if (PyModule_AddIntConstant(module, "threadsafety", threadsafety) < 0) {
+        goto error;
+    }
+
     /* initialize microprotocols layer */
     if (pysqlite_microprotocols_init(module) < 0) {
         goto error;



More information about the Python-checkins mailing list