mirror of https://github.com/python/cpython
gh-90016: Deprecate default sqlite3 adapters and converters (#94276)
Co-authored-by: CAM Gerlach <CAM.Gerlach@Gerlach.CAM>
This commit is contained in:
parent
000a4eebe7
commit
6dadf6ca01
|
@ -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()
|
|
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. _sqlite3-default-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`.
|
||||
.. note::
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The following example demonstrates this.
|
||||
The deprecated default adapters and converters consist of:
|
||||
|
||||
.. literalinclude:: ../includes/sqlite3/pysqlite_datetime.py
|
||||
|
||||
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:
|
||||
|
||||
|
|
|
@ -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
|
||||
------------------------------
|
||||
|
|
|
@ -55,16 +55,25 @@ Binary = memoryview
|
|||
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".")
|
||||
|
|
|
@ -129,7 +129,8 @@ class RegressionTests(unittest.TestCase):
|
|||
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 @@ class RegressionTests(unittest.TestCase):
|
|||
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),
|
||||
|
|
|
@ -496,38 +496,51 @@ class DateTimeTests(unittest.TestCase):
|
|||
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Deprecate :mod:`sqlite3` :ref:`default adapters and converters
|
||||
<sqlite3-default-converters>`. Patch by Erlend E. Aasland.
|
Loading…
Reference in New Issue