[Python-checkins] gh-90016: Deprecate default sqlite3 adapters and converters (#94276)

erlend-aasland webhook-mailer at python.org
Wed Jul 20 15:38:11 EDT 2022


https://github.com/python/cpython/commit/6dadf6ca019f2e19ca8f0344903be0c539263c30
commit: 6dadf6ca019f2e19ca8f0344903be0c539263c30
branch: main
author: Erlend Egeberg Aasland <erlend.aasland at protonmail.com>
committer: erlend-aasland <erlend.aasland at protonmail.com>
date: 2022-07-20T21:37:59+02:00
summary:

gh-90016: Deprecate default sqlite3 adapters and converters (#94276)

Co-authored-by: CAM Gerlach <CAM.Gerlach at Gerlach.CAM>

files:
A Misc/NEWS.d/next/Library/2022-06-25-23-44-44.gh-issue-90016.EB409s.rst
D Doc/includes/sqlite3/pysqlite_datetime.py
M Doc/library/sqlite3.rst
M Doc/whatsnew/3.12.rst
M Lib/sqlite3/dbapi2.py
M Lib/test/test_sqlite3/test_regression.py
M Lib/test/test_sqlite3/test_types.py

diff --git a/Doc/includes/sqlite3/pysqlite_datetime.py b/Doc/includes/sqlite3/pysqlite_datetime.py
deleted file mode 100644
index 5d843f906b306..0000000000000
--- a/Doc/includes/sqlite3/pysqlite_datetime.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import sqlite3
-import datetime
-
-con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
-cur = con.cursor()
-cur.execute("create table test(d date, ts timestamp)")
-
-today = datetime.date.today()
-now = datetime.datetime.now()
-
-cur.execute("insert into test(d, ts) values (?, ?)", (today, now))
-cur.execute("select d, ts from test")
-row = cur.fetchone()
-print(today, "=>", row[0], type(row[0]))
-print(now, "=>", row[1], type(row[1]))
-
-cur.execute('select current_date as "d [date]", current_timestamp as "ts [timestamp]"')
-row = cur.fetchone()
-print("current_date", row[0], type(row[0]))
-print("current_timestamp", row[1], type(row[1]))
-
-con.close()
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index d2997dc925178..a7581f2fcbd8c 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -1333,6 +1333,8 @@ This function can then be registered using :func:`register_adapter`.
 .. literalinclude:: ../includes/sqlite3/adapter_point_2.py
 
 
+.. _sqlite3-converters:
+
 Converting SQLite values to custom Python types
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -1373,27 +1375,28 @@ The following example illustrates the implicit and explicit approaches:
 .. literalinclude:: ../includes/sqlite3/converter_point.py
 
 
-Default adapters and converters
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-There are default adapters for the date and datetime types in the datetime
-module. They will be sent as ISO dates/ISO timestamps to SQLite.
+.. _sqlite3-default-converters:
 
-The default converters are registered under the name "date" for
-:class:`datetime.date` and under the name "timestamp" for
-:class:`datetime.datetime`.
+Default adapters and converters (deprecated)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-This way, you can use date/timestamps from Python without any additional
-fiddling in most cases. The format of the adapters is also compatible with the
-experimental SQLite date/time functions.
+.. note::
 
-The following example demonstrates this.
+   The default adapters and converters are deprecated as of Python 3.12.
+   Instead, use the :ref:`sqlite3-adapter-converter-recipes`
+   and tailor them to your needs.
 
-.. literalinclude:: ../includes/sqlite3/pysqlite_datetime.py
+The deprecated default adapters and converters consist of:
 
-If a timestamp stored in SQLite has a fractional part longer than 6
-numbers, its value will be truncated to microsecond precision by the
-timestamp converter.
+* An adapter for :class:`datetime.date` objects to :class:`strings <str>` in
+  `ISO 8601`_ format.
+* An adapter for :class:`datetime.datetime` objects to strings in
+  ISO 8601 format.
+* A converter for :ref:`declared <sqlite3-converters>` "date" types to
+  :class:`datetime.date` objects.
+* A converter for declared "timestamp" types to
+  :class:`datetime.datetime` objects.
+  Fractional parts will be truncated to 6 digits (microsecond precision).
 
 .. note::
 
@@ -1402,6 +1405,10 @@ timestamp converter.
    offsets in timestamps, either leave converters disabled, or register an
    offset-aware converter with :func:`register_converter`.
 
+.. deprecated:: 3.12
+
+.. _ISO 8601: https://en.wikipedia.org/wiki/ISO_8601
+
 
 .. _sqlite3-adapter-converter-recipes:
 
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 942196c03fd92..15282a755c8ac 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -135,6 +135,12 @@ Deprecated
 * :class:`typing.Hashable` and :class:`typing.Sized` aliases for :class:`collections.abc.Hashable`
   and :class:`collections.abc.Sized`. (:gh:`94309`)
 
+* The :mod:`sqlite3` :ref:`default adapters and converters
+  <sqlite3-default-converters>` are now deprecated.
+  Instead, use the :ref:`sqlite3-adapter-converter-recipes`
+  and tailor them to your needs.
+  (Contributed by Erlend E. Aasland in :gh:`90016`.)
+
 
 Pending Removal in Python 3.13
 ------------------------------
diff --git a/Lib/sqlite3/dbapi2.py b/Lib/sqlite3/dbapi2.py
index 3b6d2f7ba2d59..56fc0461e6c92 100644
--- a/Lib/sqlite3/dbapi2.py
+++ b/Lib/sqlite3/dbapi2.py
@@ -55,16 +55,25 @@ def TimestampFromTicks(ticks):
 collections.abc.Sequence.register(Row)
 
 def register_adapters_and_converters():
+    from warnings import warn
+
+    msg = ("The default {what} is deprecated as of Python 3.12; "
+           "see the sqlite3 documentation for suggested replacement recipes")
+
     def adapt_date(val):
+        warn(msg.format(what="date adapter"), DeprecationWarning, stacklevel=2)
         return val.isoformat()
 
     def adapt_datetime(val):
+        warn(msg.format(what="datetime adapter"), DeprecationWarning, stacklevel=2)
         return val.isoformat(" ")
 
     def convert_date(val):
+        warn(msg.format(what="date converter"), DeprecationWarning, stacklevel=2)
         return datetime.date(*map(int, val.split(b"-")))
 
     def convert_timestamp(val):
+        warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
         datepart, timepart = val.split(b" ")
         year, month, day = map(int, datepart.split(b"-"))
         timepart_full = timepart.split(b".")
diff --git a/Lib/test/test_sqlite3/test_regression.py b/Lib/test/test_sqlite3/test_regression.py
index 0b727cecb0e8c..ad83a97c8c40d 100644
--- a/Lib/test/test_sqlite3/test_regression.py
+++ b/Lib/test/test_sqlite3/test_regression.py
@@ -129,7 +129,8 @@ def test_type_map_usage(self):
         con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES)
         cur = con.cursor()
         cur.execute("create table foo(bar timestamp)")
-        cur.execute("insert into foo(bar) values (?)", (datetime.datetime.now(),))
+        with self.assertWarnsRegex(DeprecationWarning, "adapter"):
+            cur.execute("insert into foo(bar) values (?)", (datetime.datetime.now(),))
         cur.execute(SELECT)
         cur.execute("drop table foo")
         cur.execute("create table foo(bar integer)")
@@ -305,7 +306,8 @@ def test_convert_timestamp_microsecond_padding(self):
         cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.123456789')")
 
         cur.execute("SELECT * FROM t")
-        values = [x[0] for x in cur.fetchall()]
+        with self.assertWarnsRegex(DeprecationWarning, "converter"):
+            values = [x[0] for x in cur.fetchall()]
 
         self.assertEqual(values, [
             datetime.datetime(2012, 4, 4, 15, 6, 0, 456000),
diff --git a/Lib/test/test_sqlite3/test_types.py b/Lib/test/test_sqlite3/test_types.py
index 177cd10235039..62318823510d4 100644
--- a/Lib/test/test_sqlite3/test_types.py
+++ b/Lib/test/test_sqlite3/test_types.py
@@ -496,38 +496,51 @@ def tearDown(self):
 
     def test_sqlite_date(self):
         d = sqlite.Date(2004, 2, 14)
-        self.cur.execute("insert into test(d) values (?)", (d,))
+        with self.assertWarnsRegex(DeprecationWarning, "adapter") as cm:
+            self.cur.execute("insert into test(d) values (?)", (d,))
+        self.assertEqual(cm.filename, __file__)
         self.cur.execute("select d from test")
-        d2 = self.cur.fetchone()[0]
+        with self.assertWarnsRegex(DeprecationWarning, "converter") as cm:
+            d2 = self.cur.fetchone()[0]
+        self.assertEqual(cm.filename, __file__)
         self.assertEqual(d, d2)
 
     def test_sqlite_timestamp(self):
         ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0)
-        self.cur.execute("insert into test(ts) values (?)", (ts,))
+        with self.assertWarnsRegex(DeprecationWarning, "adapter") as cm:
+            self.cur.execute("insert into test(ts) values (?)", (ts,))
+        self.assertEqual(cm.filename, __file__)
         self.cur.execute("select ts from test")
-        ts2 = self.cur.fetchone()[0]
+        with self.assertWarnsRegex(DeprecationWarning, "converter") as cm:
+            ts2 = self.cur.fetchone()[0]
+        self.assertEqual(cm.filename, __file__)
         self.assertEqual(ts, ts2)
 
     def test_sql_timestamp(self):
         now = datetime.datetime.utcnow()
         self.cur.execute("insert into test(ts) values (current_timestamp)")
         self.cur.execute("select ts from test")
-        ts = self.cur.fetchone()[0]
+        with self.assertWarnsRegex(DeprecationWarning, "converter"):
+            ts = self.cur.fetchone()[0]
         self.assertEqual(type(ts), datetime.datetime)
         self.assertEqual(ts.year, now.year)
 
     def test_date_time_sub_seconds(self):
         ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0, 500000)
-        self.cur.execute("insert into test(ts) values (?)", (ts,))
+        with self.assertWarnsRegex(DeprecationWarning, "adapter"):
+            self.cur.execute("insert into test(ts) values (?)", (ts,))
         self.cur.execute("select ts from test")
-        ts2 = self.cur.fetchone()[0]
+        with self.assertWarnsRegex(DeprecationWarning, "converter"):
+            ts2 = self.cur.fetchone()[0]
         self.assertEqual(ts, ts2)
 
     def test_date_time_sub_seconds_floating_point(self):
         ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0, 510241)
-        self.cur.execute("insert into test(ts) values (?)", (ts,))
+        with self.assertWarnsRegex(DeprecationWarning, "adapter"):
+            self.cur.execute("insert into test(ts) values (?)", (ts,))
         self.cur.execute("select ts from test")
-        ts2 = self.cur.fetchone()[0]
+        with self.assertWarnsRegex(DeprecationWarning, "converter"):
+            ts2 = self.cur.fetchone()[0]
         self.assertEqual(ts, ts2)
 
 
diff --git a/Misc/NEWS.d/next/Library/2022-06-25-23-44-44.gh-issue-90016.EB409s.rst b/Misc/NEWS.d/next/Library/2022-06-25-23-44-44.gh-issue-90016.EB409s.rst
new file mode 100644
index 0000000000000..040ba44be2b91
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-06-25-23-44-44.gh-issue-90016.EB409s.rst
@@ -0,0 +1,2 @@
+Deprecate :mod:`sqlite3` :ref:`default adapters and converters
+<sqlite3-default-converters>`. Patch by Erlend E. Aasland.



More information about the Python-checkins mailing list