Skip to content

Commit

Permalink
pythongh-90016: Deprecate default sqlite3 adapters and converters
Browse files Browse the repository at this point in the history
  • Loading branch information
erlend-aasland committed Jun 25, 2022
1 parent bd3c1c1 commit 2a586c4
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 50 deletions.
22 changes: 0 additions & 22 deletions Doc/includes/sqlite3/pysqlite_datetime.py

This file was deleted.

39 changes: 22 additions & 17 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -1345,27 +1347,24 @@ 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.
Default adapters and converters (deprecated)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The default converters are registered under the name "date" for
:class:`datetime.date` and under the name "timestamp" for
:class:`datetime.datetime`.

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.
Use the :ref:`sqlite3-adapter-converter-recipes`
and tailor them to you need.

.. literalinclude:: ../includes/sqlite3/pysqlite_datetime.py
The deprecated default adapters and converters consists 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.
* Adapt :class:`datetime.date` objects to ISO format :class:`strings <str>`.
* Adapt :class:`datetime.datetime` objects to ISO format :class:`strings <str>`.
* Convert :ref:`declared <sqlite3-converters>` "date" types to ``datetime.date``
objects.
* Convert declared "timestamp" types to ``datetime.datetime`` objects.
If a timestamp stored in SQLite has a fractional part longer than 6
numbers, its value will be truncated to microsecond precision.

.. note::

Expand All @@ -1374,6 +1373,12 @@ timestamp converter.
offsets in timestamps, either leave converters disabled, or register an
offset-aware converter with :func:`register_converter`.

.. versionchanged:: 3.12

Deprecated default adapters and converters.

.. deprecated:: 3.12


.. _sqlite3-adapter-converter-recipes:

Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ CPython bytecode changes
Deprecated
==========

* The default :mod:`sqlite3` adapters and converters are now deprecated.
(Contributed by Erlend E. Aasland in :gh:`90016`.)


Pending Removal in Python 3.13
==============================
Expand Down
8 changes: 8 additions & 0 deletions Lib/sqlite3/dbapi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,24 @@ def TimestampFromTicks(ticks):
collections.abc.Sequence.register(Row)

def register_adapters_and_converters():
from warnings import warn

msg = "The default {what} are deprecated as of Python 3.12"

def adapt_date(val):
warn(msg.format(what="adapters"), DeprecationWarning, stacklevel=2)
return val.isoformat()

def adapt_datetime(val):
warn(msg.format(what="adapters"), DeprecationWarning, stacklevel=2)
return val.isoformat(" ")

def convert_date(val):
warn(msg.format(what="converters"), DeprecationWarning, stacklevel=2)
return datetime.date(*map(int, val.split(b"-")))

def convert_timestamp(val):
warn(msg.format(what="converters"), DeprecationWarning, stacklevel=2)
datepart, timepart = val.split(b" ")
year, month, day = map(int, datepart.split(b"-"))
timepart_full = timepart.split(b".")
Expand Down
6 changes: 4 additions & 2 deletions Lib/test/test_sqlite3/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, "adapters"):
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)")
Expand Down Expand Up @@ -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, "converters"):
values = [x[0] for x in cur.fetchall()]

self.assertEqual(values, [
datetime.datetime(2012, 4, 4, 15, 6, 0, 456000),
Expand Down
31 changes: 22 additions & 9 deletions Lib/test/test_sqlite3/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, "adapters") 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, "converters") 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, "adapters") 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, "converters") 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, "converters"):
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, "adapters"):
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, "converters"):
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, "adapters"):
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, "converters"):
ts2 = self.cur.fetchone()[0]
self.assertEqual(ts, ts2)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Deprecate default :mod:`sqlite3` adapters and converters. Patch by Erlend E.
Aasland.

0 comments on commit 2a586c4

Please sign in to comment.