Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 9 commits into from
Jul 20, 2022
Merged
22 changes: 0 additions & 22 deletions Doc/includes/sqlite3/pysqlite_datetime.py

This file was deleted.

39 changes: 23 additions & 16 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -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)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

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::
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

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).
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

.. note::

Expand All @@ -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:

Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
------------------------------
Expand Down
9 changes: 9 additions & 0 deletions Lib/sqlite3/dbapi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,25 @@ def TimestampFromTicks(ticks):
collections.abc.Sequence.register(Row)

def register_adapters_and_converters():
from warnings import warn
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

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".")
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, "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)")
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, "converter"):
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, "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)


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