Skip to content

Commit 6dadf6c

Browse files
gh-90016: Deprecate default sqlite3 adapters and converters (#94276)
Co-authored-by: CAM Gerlach <CAM.Gerlach@Gerlach.CAM>
1 parent 000a4ee commit 6dadf6c

File tree

7 files changed

+66
-49
lines changed

7 files changed

+66
-49
lines changed

Doc/includes/sqlite3/pysqlite_datetime.py

-22
This file was deleted.

Doc/library/sqlite3.rst

+23-16
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,8 @@ This function can then be registered using :func:`register_adapter`.
13331333
.. literalinclude:: ../includes/sqlite3/adapter_point_2.py
13341334

13351335

1336+
.. _sqlite3-converters:
1337+
13361338
Converting SQLite values to custom Python types
13371339
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13381340

@@ -1373,27 +1375,28 @@ The following example illustrates the implicit and explicit approaches:
13731375
.. literalinclude:: ../includes/sqlite3/converter_point.py
13741376

13751377

1376-
Default adapters and converters
1377-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1378-
1379-
There are default adapters for the date and datetime types in the datetime
1380-
module. They will be sent as ISO dates/ISO timestamps to SQLite.
1378+
.. _sqlite3-default-converters:
13811379

1382-
The default converters are registered under the name "date" for
1383-
:class:`datetime.date` and under the name "timestamp" for
1384-
:class:`datetime.datetime`.
1380+
Default adapters and converters (deprecated)
1381+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13851382

1386-
This way, you can use date/timestamps from Python without any additional
1387-
fiddling in most cases. The format of the adapters is also compatible with the
1388-
experimental SQLite date/time functions.
1383+
.. note::
13891384

1390-
The following example demonstrates this.
1385+
The default adapters and converters are deprecated as of Python 3.12.
1386+
Instead, use the :ref:`sqlite3-adapter-converter-recipes`
1387+
and tailor them to your needs.
13911388

1392-
.. literalinclude:: ../includes/sqlite3/pysqlite_datetime.py
1389+
The deprecated default adapters and converters consist of:
13931390

1394-
If a timestamp stored in SQLite has a fractional part longer than 6
1395-
numbers, its value will be truncated to microsecond precision by the
1396-
timestamp converter.
1391+
* An adapter for :class:`datetime.date` objects to :class:`strings <str>` in
1392+
`ISO 8601`_ format.
1393+
* An adapter for :class:`datetime.datetime` objects to strings in
1394+
ISO 8601 format.
1395+
* A converter for :ref:`declared <sqlite3-converters>` "date" types to
1396+
:class:`datetime.date` objects.
1397+
* A converter for declared "timestamp" types to
1398+
:class:`datetime.datetime` objects.
1399+
Fractional parts will be truncated to 6 digits (microsecond precision).
13971400

13981401
.. note::
13991402

@@ -1402,6 +1405,10 @@ timestamp converter.
14021405
offsets in timestamps, either leave converters disabled, or register an
14031406
offset-aware converter with :func:`register_converter`.
14041407

1408+
.. deprecated:: 3.12
1409+
1410+
.. _ISO 8601: https://en.wikipedia.org/wiki/ISO_8601
1411+
14051412

14061413
.. _sqlite3-adapter-converter-recipes:
14071414

Doc/whatsnew/3.12.rst

+6
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ Deprecated
135135
* :class:`typing.Hashable` and :class:`typing.Sized` aliases for :class:`collections.abc.Hashable`
136136
and :class:`collections.abc.Sized`. (:gh:`94309`)
137137

138+
* The :mod:`sqlite3` :ref:`default adapters and converters
139+
<sqlite3-default-converters>` are now deprecated.
140+
Instead, use the :ref:`sqlite3-adapter-converter-recipes`
141+
and tailor them to your needs.
142+
(Contributed by Erlend E. Aasland in :gh:`90016`.)
143+
138144

139145
Pending Removal in Python 3.13
140146
------------------------------

Lib/sqlite3/dbapi2.py

+9
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,25 @@ def TimestampFromTicks(ticks):
5555
collections.abc.Sequence.register(Row)
5656

5757
def register_adapters_and_converters():
58+
from warnings import warn
59+
60+
msg = ("The default {what} is deprecated as of Python 3.12; "
61+
"see the sqlite3 documentation for suggested replacement recipes")
62+
5863
def adapt_date(val):
64+
warn(msg.format(what="date adapter"), DeprecationWarning, stacklevel=2)
5965
return val.isoformat()
6066

6167
def adapt_datetime(val):
68+
warn(msg.format(what="datetime adapter"), DeprecationWarning, stacklevel=2)
6269
return val.isoformat(" ")
6370

6471
def convert_date(val):
72+
warn(msg.format(what="date converter"), DeprecationWarning, stacklevel=2)
6573
return datetime.date(*map(int, val.split(b"-")))
6674

6775
def convert_timestamp(val):
76+
warn(msg.format(what="timestamp converter"), DeprecationWarning, stacklevel=2)
6877
datepart, timepart = val.split(b" ")
6978
year, month, day = map(int, datepart.split(b"-"))
7079
timepart_full = timepart.split(b".")

Lib/test/test_sqlite3/test_regression.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ def test_type_map_usage(self):
129129
con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES)
130130
cur = con.cursor()
131131
cur.execute("create table foo(bar timestamp)")
132-
cur.execute("insert into foo(bar) values (?)", (datetime.datetime.now(),))
132+
with self.assertWarnsRegex(DeprecationWarning, "adapter"):
133+
cur.execute("insert into foo(bar) values (?)", (datetime.datetime.now(),))
133134
cur.execute(SELECT)
134135
cur.execute("drop table foo")
135136
cur.execute("create table foo(bar integer)")
@@ -305,7 +306,8 @@ def test_convert_timestamp_microsecond_padding(self):
305306
cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.123456789')")
306307

307308
cur.execute("SELECT * FROM t")
308-
values = [x[0] for x in cur.fetchall()]
309+
with self.assertWarnsRegex(DeprecationWarning, "converter"):
310+
values = [x[0] for x in cur.fetchall()]
309311

310312
self.assertEqual(values, [
311313
datetime.datetime(2012, 4, 4, 15, 6, 0, 456000),

Lib/test/test_sqlite3/test_types.py

+22-9
Original file line numberDiff line numberDiff line change
@@ -496,38 +496,51 @@ def tearDown(self):
496496

497497
def test_sqlite_date(self):
498498
d = sqlite.Date(2004, 2, 14)
499-
self.cur.execute("insert into test(d) values (?)", (d,))
499+
with self.assertWarnsRegex(DeprecationWarning, "adapter") as cm:
500+
self.cur.execute("insert into test(d) values (?)", (d,))
501+
self.assertEqual(cm.filename, __file__)
500502
self.cur.execute("select d from test")
501-
d2 = self.cur.fetchone()[0]
503+
with self.assertWarnsRegex(DeprecationWarning, "converter") as cm:
504+
d2 = self.cur.fetchone()[0]
505+
self.assertEqual(cm.filename, __file__)
502506
self.assertEqual(d, d2)
503507

504508
def test_sqlite_timestamp(self):
505509
ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0)
506-
self.cur.execute("insert into test(ts) values (?)", (ts,))
510+
with self.assertWarnsRegex(DeprecationWarning, "adapter") as cm:
511+
self.cur.execute("insert into test(ts) values (?)", (ts,))
512+
self.assertEqual(cm.filename, __file__)
507513
self.cur.execute("select ts from test")
508-
ts2 = self.cur.fetchone()[0]
514+
with self.assertWarnsRegex(DeprecationWarning, "converter") as cm:
515+
ts2 = self.cur.fetchone()[0]
516+
self.assertEqual(cm.filename, __file__)
509517
self.assertEqual(ts, ts2)
510518

511519
def test_sql_timestamp(self):
512520
now = datetime.datetime.utcnow()
513521
self.cur.execute("insert into test(ts) values (current_timestamp)")
514522
self.cur.execute("select ts from test")
515-
ts = self.cur.fetchone()[0]
523+
with self.assertWarnsRegex(DeprecationWarning, "converter"):
524+
ts = self.cur.fetchone()[0]
516525
self.assertEqual(type(ts), datetime.datetime)
517526
self.assertEqual(ts.year, now.year)
518527

519528
def test_date_time_sub_seconds(self):
520529
ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0, 500000)
521-
self.cur.execute("insert into test(ts) values (?)", (ts,))
530+
with self.assertWarnsRegex(DeprecationWarning, "adapter"):
531+
self.cur.execute("insert into test(ts) values (?)", (ts,))
522532
self.cur.execute("select ts from test")
523-
ts2 = self.cur.fetchone()[0]
533+
with self.assertWarnsRegex(DeprecationWarning, "converter"):
534+
ts2 = self.cur.fetchone()[0]
524535
self.assertEqual(ts, ts2)
525536

526537
def test_date_time_sub_seconds_floating_point(self):
527538
ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0, 510241)
528-
self.cur.execute("insert into test(ts) values (?)", (ts,))
539+
with self.assertWarnsRegex(DeprecationWarning, "adapter"):
540+
self.cur.execute("insert into test(ts) values (?)", (ts,))
529541
self.cur.execute("select ts from test")
530-
ts2 = self.cur.fetchone()[0]
542+
with self.assertWarnsRegex(DeprecationWarning, "converter"):
543+
ts2 = self.cur.fetchone()[0]
531544
self.assertEqual(ts, ts2)
532545

533546

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Deprecate :mod:`sqlite3` :ref:`default adapters and converters
2+
<sqlite3-default-converters>`. Patch by Erlend E. Aasland.

0 commit comments

Comments
 (0)