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.

38 changes: 21 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,23 @@ 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)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

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::
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`,
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved
and tailor them to your needs.

.. literalinclude:: ../includes/sqlite3/pysqlite_datetime.py
The deprecated default adapters and converters consists of:
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

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 :class:`strings <str>` in ISO format.
* Adapt :class:`datetime.datetime` objects to strings ISO format.
* Convert :ref:`declared <sqlite3-converters>` "date" types to ``datetime.date``
objects.
* Convert declared "timestamp" types to ``datetime.datetime`` objects.
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved
Fractional parts will be truncated to 6 digits (microsecond precision).
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

.. note::

Expand All @@ -1374,6 +1372,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
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved


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


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

msg = "The default {what} are deprecated as of Python 3.12"
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

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