From 598e26aad669f82f5b2c8d0214117d21d2065873 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 23 May 2022 10:42:17 +0200 Subject: [PATCH 01/32] gh-90016: Reword sqlite3 adapter/converter docs --- Doc/includes/sqlite3/converter_point.py | 21 ++-- Doc/library/sqlite3.rst | 154 ++++++++++++------------ Modules/_sqlite/clinic/module.c.h | 32 ++--- Modules/_sqlite/module.c | 26 ++-- 4 files changed, 114 insertions(+), 119 deletions(-) diff --git a/Doc/includes/sqlite3/converter_point.py b/Doc/includes/sqlite3/converter_point.py index 5df828e3361246..3001133330a539 100644 --- a/Doc/includes/sqlite3/converter_point.py +++ b/Doc/includes/sqlite3/converter_point.py @@ -5,28 +5,23 @@ def __init__(self, x, y): self.x, self.y = x, y def __repr__(self): - return "(%f;%f)" % (self.x, self.y) + return f"Point({self.x}, {self.y})" def adapt_point(point): - return ("%f;%f" % (point.x, point.y)).encode('ascii') + return ("%f;%f" % (point.x, point.y)).encode("utf-8") def convert_point(s): x, y = list(map(float, s.split(b";"))) return Point(x, y) -# Register the adapter +# Register the adapter and converter sqlite3.register_adapter(Point, adapt_point) - -# Register the converter sqlite3.register_converter("point", convert_point) +# 1) Parse using declared types p = Point(4.0, -3.2) - -######################### -# 1) Using declared types con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES) -cur = con.cursor() -cur.execute("create table test(p point)") +cur = con.execute("create table test(p point)") cur.execute("insert into test(p) values (?)", (p,)) cur.execute("select p from test") @@ -34,11 +29,9 @@ def convert_point(s): cur.close() con.close() -####################### -# 1) Using column names +# 2) Parse using column names con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES) -cur = con.cursor() -cur.execute("create table test(p)") +cur = con.execute("create table test(p)") cur.execute("insert into test(p) values (?)", (p,)) cur.execute('select p as "p [point]" from test') diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 1843e22640f014..b73ec141f7e353 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -199,31 +199,37 @@ Module functions and constants .. data:: PARSE_DECLTYPES - This constant is meant to be used with the *detect_types* parameter of the - :func:`connect` function. + Use this flag with the *detect_types* keyword of :meth:`connect` to enable + parsing of declared types for each column it return. + The types are declared when the database table is created. + :mod:`sqlite3` will look up a converter function using the first word of the + declared type as the converter dictionary key. + The following SQL code results in the following lookups: - Setting it makes the :mod:`sqlite3` module parse the declared type for each - column it returns. It will parse out the first word of the declared type, - i. e. for "integer primary key", it will parse out "integer", or for - "number(10)" it will parse out "number". Then for that column, it will look - into the converters dictionary and use the converter function registered for - that type there. + .. code-block:: sql + CREATE TABLE test( + i integer primary key, ! will look up a converter named "integer" + p point, ! will look up a converter named "point" + n number(10) ! will look up a converter named "number" + ) + + This flag may be paired with :const:`PARSE_COLNAMES` using the ``|`` + operator. .. data:: PARSE_COLNAMES - This constant is meant to be used with the *detect_types* parameter of the - :func:`connect` function. + Use this flag with the *detect_types* keyword of :meth:`connect` to enable + parsing of column names in queries. + :mod:`sqlite3` will look for strings containing brackets, and will look up a + converter function using the word inside the brackets as the converter + dictionary key. + + .. code-block:: sql + SELECT p as "p [point]" FROM test; ! will look up converter "point" - Setting this makes the SQLite interface parse the column name for each column it - returns. It will look for a string formed [mytype] in there, and then decide - that 'mytype' is the type of the column. It will try to find an entry of - 'mytype' in the converters dictionary and then use the converter function found - there to return the value. The column name found in :attr:`Cursor.description` - does not include the type, i. e. if you use something like - ``'as "Expiration date [datetime]"'`` in your SQL, then we will parse out - everything until the first ``'['`` for the column name and strip - the preceding space: the column name would simply be "Expiration date". + This flag may be paired with :const:`PARSE_DECLTYPES` using the ``|`` + operator. .. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri]) @@ -250,11 +256,12 @@ Module functions and constants *detect_types* parameter and the using custom **converters** registered with the module-level :func:`register_converter` function allow you to easily do that. - *detect_types* defaults to 0 (i. e. off, no type detection), you can set it to - any combination of :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES` to turn - type detection on. Due to SQLite behaviour, types can't be detected for generated - fields (for example ``max(data)``), even when *detect_types* parameter is set. In - such case, the returned type is :class:`str`. + *detect_types* defaults to 0 (type detection disabled). + Set it to any combination of :const:`PARSE_DECLTYPES` and + :const:`PARSE_COLNAMES` to enable type detection. + Types cannot be detected for generated fields (for example ``max(data)``), + even when *detect_types* parameter is set. In such cases, the returned type + is :class:`str`. By default, *check_same_thread* is :const:`True` and only the creating thread may use the connection. If set :const:`False`, the returned connection may be shared @@ -309,21 +316,24 @@ Module functions and constants Added the ``sqlite3.connect/handle`` auditing event. -.. function:: register_converter(typename, callable) +.. function:: register_converter(type, callable) - Registers a callable to convert a bytestring from the database into a custom - Python type. The callable will be invoked for all database values that are of - the type *typename*. Confer the parameter *detect_types* of the :func:`connect` - function for how the type detection works. Note that *typename* and the name of - the type in your query are matched in case-insensitive manner. + Register *callable* to convert SQLite *type* into a Python type. + *callable* is invoked for all values of type *typename*. + Confer the parameter *detect_types* of :meth:`Connection.connect` + regarding how type detection works. + + .. note:: + *type* and the name of the type in your query are matched in a + case-insensitive manner. .. function:: register_adapter(type, callable) - Registers a callable to convert the custom Python type *type* into one of - SQLite's supported types. The callable *callable* accepts as single parameter - the Python value, and must return a value of the following types: int, - float, str or bytes. + Register *callable* to adapt Python *type* into an SQLite type. + *callable* is called with the Python value and must return a valid SQLite + type: + :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. .. function:: complete_statement(sql) @@ -1205,33 +1215,30 @@ you can let the :mod:`sqlite3` module convert SQLite types to different Python types via converters. -Using adapters to store additional Python types in SQLite databases -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Using adapters to store custom Python types in SQLite databases +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -As described before, SQLite supports only a limited set of types natively. To -use other Python types with SQLite, you must **adapt** them to one of the -sqlite3 module's supported types for SQLite: one of NoneType, int, float, -str, bytes. +To store custom Python types in SQLite databases, **adapt** them one of the +basic types supported by SQLite: +:class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. -There are two ways to enable the :mod:`sqlite3` module to adapt a custom Python -type to one of the supported ones. +There are two ways to adapt Python objects to SQLite types: +letting your object adapt itself, or using an adapter function. +The latter will take precedence above the former. For a library that exports a +custom type, it may make sense to let that type be able to adapt itself. As an +application developer, it may make more sense to take control, and register +custom adapter functions. Letting your object adapt itself """""""""""""""""""""""""""""""" -This is a good approach if you write the class yourself. Let's suppose you have -a class like this:: - - class Point: - def __init__(self, x, y): - self.x, self.y = x, y - -Now you want to store the point in a single SQLite column. First you'll have to -choose one of the supported types to be used for representing the point. -Let's just use str and separate the coordinates using a semicolon. Then you need -to give your class a method ``__conform__(self, protocol)`` which must return -the converted value. The parameter *protocol* will be :class:`PrepareProtocol`. +Suppose we have `Point` class that represents a pair of coordinates, +``x`` and ``y``, in a Cartesian coordinate system. +We want to store the coordinate pair as a text string in the database, +using a semicolon to separate the coordinates. +We implement this by adding a ``__conform__(self, protocol)`` method which +returns the adapted value. *protocol* will be :class:`PrepareProtocol`. .. literalinclude:: ../includes/sqlite3/adapter_point_1.py @@ -1239,8 +1246,9 @@ the converted value. The parameter *protocol* will be :class:`PrepareProtocol`. Registering an adapter callable """"""""""""""""""""""""""""""" -The other possibility is to create a function that converts the type to the -string representation and register the function with :meth:`register_adapter`. +Continuing the above example, let's rewrite it using an adapter function. +We use :meth:`register_adapter` to add an adapter function that takes a Python +type as its argument, and returns an SQLite compatible type. .. literalinclude:: ../includes/sqlite3/adapter_point_2.py @@ -1255,21 +1263,16 @@ but as a Unix timestamp. Converting SQLite values to custom Python types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Writing an adapter lets you send custom Python types to SQLite. But to make it -really useful we need to make the Python to SQLite to Python roundtrip work. - -Enter converters. - -Let's go back to the :class:`Point` class. We stored the x and y coordinates -separated via semicolons as strings in SQLite. +To be able to convert SQLite value to custom Python types, we use _converters_. -First, we'll define a converter function that accepts the string as a parameter -and constructs a :class:`Point` object from it. +Let's revisit the ``Point`` class example from above; +the coordinate pair is stored in the database as a semicolon separated string. +We define a converter that accept a string, and return a ``Point`` object. .. note:: - Converter functions **always** get called with a :class:`bytes` object, no - matter under which data type you sent the value to SQLite. + Converter functions **always** are passed a :class:`bytes` object, + no matter the underlying SQLite data type. :: @@ -1277,17 +1280,16 @@ and constructs a :class:`Point` object from it. x, y = map(float, s.split(b";")) return Point(x, y) -Now you need to make the :mod:`sqlite3` module know that what you select from -the database is actually a point. There are two ways of doing this: - -* Implicitly via the declared type - -* Explicitly via the column name +We now need to tell :mod:`sqlite3` when it should convert a given SQLite value. +This is done when connecting to a database, using the *detect_types* keyword of +:meth:`connect`. We've got three options: -Both ways are described in section :ref:`sqlite3-module-contents`, in the entries -for the constants :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES`. +* Implicit: set *detect_types* to :const:`PARSE_DECLTYPES` +* Explicit: set *detect_types* to :const:`PARSE_COLNAMES` +* Both: set *detect_types* to + ``sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES`` -The following example illustrates both approaches. +The following example illustrates the implicit and explicit approach: .. literalinclude:: ../includes/sqlite3/converter_point.py diff --git a/Modules/_sqlite/clinic/module.c.h b/Modules/_sqlite/clinic/module.c.h index d3367cf62bf724..fcb83720e0b663 100644 --- a/Modules/_sqlite/clinic/module.c.h +++ b/Modules/_sqlite/clinic/module.c.h @@ -157,55 +157,55 @@ pysqlite_complete_statement(PyObject *module, PyObject *const *args, Py_ssize_t } PyDoc_STRVAR(pysqlite_register_adapter__doc__, -"register_adapter($module, type, caster, /)\n" +"register_adapter($module, type, callable, /)\n" "--\n" "\n" -"Registers an adapter with sqlite3\'s adapter registry."); +"Register a function to adapt Python types to SQLite types."); #define PYSQLITE_REGISTER_ADAPTER_METHODDEF \ {"register_adapter", _PyCFunction_CAST(pysqlite_register_adapter), METH_FASTCALL, pysqlite_register_adapter__doc__}, static PyObject * pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, - PyObject *caster); + PyObject *adapter); static PyObject * pysqlite_register_adapter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyTypeObject *type; - PyObject *caster; + PyObject *adapter; if (!_PyArg_CheckPositional("register_adapter", nargs, 2, 2)) { goto exit; } type = (PyTypeObject *)args[0]; - caster = args[1]; - return_value = pysqlite_register_adapter_impl(module, type, caster); + adapter = args[1]; + return_value = pysqlite_register_adapter_impl(module, type, adapter); exit: return return_value; } PyDoc_STRVAR(pysqlite_register_converter__doc__, -"register_converter($module, name, converter, /)\n" +"register_converter($module, type, callable, /)\n" "--\n" "\n" -"Registers a converter with sqlite3."); +"Register a function to convert SQLite types to Python types."); #define PYSQLITE_REGISTER_CONVERTER_METHODDEF \ {"register_converter", _PyCFunction_CAST(pysqlite_register_converter), METH_FASTCALL, pysqlite_register_converter__doc__}, static PyObject * -pysqlite_register_converter_impl(PyObject *module, PyObject *orig_name, - PyObject *callable); +pysqlite_register_converter_impl(PyObject *module, PyObject *tp, + PyObject *converter); static PyObject * pysqlite_register_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *orig_name; - PyObject *callable; + PyObject *tp; + PyObject *converter; if (!_PyArg_CheckPositional("register_converter", nargs, 2, 2)) { goto exit; @@ -217,9 +217,9 @@ pysqlite_register_converter(PyObject *module, PyObject *const *args, Py_ssize_t if (PyUnicode_READY(args[0]) == -1) { goto exit; } - orig_name = args[0]; - callable = args[1]; - return_value = pysqlite_register_converter_impl(module, orig_name, callable); + tp = args[0]; + converter = args[1]; + return_value = pysqlite_register_converter_impl(module, tp, converter); exit: return return_value; @@ -292,4 +292,4 @@ pysqlite_adapt(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=a7cfa6dc9d54273c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b03f4a0db3e35eb7 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 9b0e921511c56a..cedd94ad62472a 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -108,16 +108,16 @@ pysqlite_complete_statement_impl(PyObject *module, const char *statement) _sqlite3.register_adapter as pysqlite_register_adapter type: object(type='PyTypeObject *') - caster: object + callable as adapter: object / -Registers an adapter with sqlite3's adapter registry. +Register a function to adapt Python types to SQLite types. [clinic start generated code]*/ static PyObject * pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, - PyObject *caster) -/*[clinic end generated code: output=a287e8db18e8af23 input=b4bd87afcadc535d]*/ + PyObject *adapter) +/*[clinic end generated code: output=83bab29ff572feb4 input=b6813f5d620955ed]*/ { int rc; @@ -131,7 +131,7 @@ pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, pysqlite_state *state = pysqlite_get_state(module); PyObject *protocol = (PyObject *)state->PrepareProtocolType; - rc = pysqlite_microprotocols_add(state, type, protocol, caster); + rc = pysqlite_microprotocols_add(state, type, protocol, adapter); if (rc == -1) { return NULL; } @@ -142,29 +142,29 @@ pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, /*[clinic input] _sqlite3.register_converter as pysqlite_register_converter - name as orig_name: unicode - converter as callable: object + type as tp: unicode + callable as converter: object / -Registers a converter with sqlite3. +Register a function to convert SQLite types to Python types. [clinic start generated code]*/ static PyObject * -pysqlite_register_converter_impl(PyObject *module, PyObject *orig_name, - PyObject *callable) -/*[clinic end generated code: output=a2f2bfeed7230062 input=90f645419425d6c4]*/ +pysqlite_register_converter_impl(PyObject *module, PyObject *tp, + PyObject *converter) +/*[clinic end generated code: output=59b8c05cdddf7e54 input=63d20a7be9873f12]*/ { PyObject* name = NULL; PyObject* retval = NULL; /* convert the name to upper case */ pysqlite_state *state = pysqlite_get_state(module); - name = PyObject_CallMethodNoArgs(orig_name, state->str_upper); + name = PyObject_CallMethodNoArgs(tp, state->str_upper); if (!name) { goto error; } - if (PyDict_SetItem(state->converters, name, callable) != 0) { + if (PyDict_SetItem(state->converters, name, converter) != 0) { goto error; } From e5b1b88ea41aababed070664a73f0c2cea037376 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 23 May 2022 10:48:28 +0200 Subject: [PATCH 02/32] Add current adapters/converter as recipes: improve this --- Doc/library/sqlite3.rst | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index b73ec141f7e353..64baaf9680a742 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1323,6 +1323,45 @@ timestamp converter. offsets in timestamps, either leave converters disabled, or register an offset-aware converter with :func:`register_converter`. + +.. _sqlite3-adapter-converter-recipes: + +Adapter and Converter Recipes +----------------------------- + +This section shows recipes for common adapters and converters. + +.. testcode:: + + # Timezone naive datetime adapters and converters. + def adapt_date(val): + return val.isoformat() + + def adapt_datetime(val): + return val.isoformat(" ") + + def convert_date(val): + return datetime.date(*map(int, val.split(b"-"))) + + def convert_timestamp(val): + datepart, timepart = val.split(b" ") + year, month, day = map(int, datepart.split(b"-")) + timepart_full = timepart.split(b".") + hours, minutes, seconds = map(int, timepart_full[0].split(b":")) + if len(timepart_full) == 2: + microseconds = int('{:0<6.6}'.format(timepart_full[1].decode())) + else: + microseconds = 0 + + val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds) + return val + + register_adapter(datetime.date, adapt_date) + register_adapter(datetime.datetime, adapt_datetime) + register_converter("date", convert_date) + register_converter("timestamp", convert_timestamp) + + .. _sqlite3-controlling-transactions: Controlling Transactions From 9399b548acb8654433b21ac81b58457624b4fb75 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 23 May 2022 10:54:40 +0200 Subject: [PATCH 03/32] default role --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 64baaf9680a742..2b7568ac961197 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1233,7 +1233,7 @@ custom adapter functions. Letting your object adapt itself """""""""""""""""""""""""""""""" -Suppose we have `Point` class that represents a pair of coordinates, +Suppose we have ``Point`` class that represents a pair of coordinates, ``x`` and ``y``, in a Cartesian coordinate system. We want to store the coordinate pair as a text string in the database, using a semicolon to separate the coordinates. From 40bb59a20f749ee9623a33845dd9c321ae4f36e2 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 23 May 2022 11:17:52 +0200 Subject: [PATCH 04/32] Formatting --- Doc/library/sqlite3.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 2b7568ac961197..8f3dad4ba3126e 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -207,6 +207,7 @@ Module functions and constants The following SQL code results in the following lookups: .. code-block:: sql + CREATE TABLE test( i integer primary key, ! will look up a converter named "integer" p point, ! will look up a converter named "point" @@ -226,6 +227,7 @@ Module functions and constants dictionary key. .. code-block:: sql + SELECT p as "p [point]" FROM test; ! will look up converter "point" This flag may be paired with :const:`PARSE_DECLTYPES` using the ``|`` @@ -1333,6 +1335,8 @@ This section shows recipes for common adapters and converters. .. testcode:: + import sqlite3 + # Timezone naive datetime adapters and converters. def adapt_date(val): return val.isoformat() @@ -1356,10 +1360,10 @@ This section shows recipes for common adapters and converters. val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds) return val - register_adapter(datetime.date, adapt_date) - register_adapter(datetime.datetime, adapt_datetime) - register_converter("date", convert_date) - register_converter("timestamp", convert_timestamp) + sqlite3.register_adapter(datetime.date, adapt_date) + sqlite3.register_adapter(datetime.datetime, adapt_datetime) + sqlite3.register_converter("date", convert_date) + sqlite3.register_converter("timestamp", convert_timestamp) .. _sqlite3-controlling-transactions: From 82cf3e2ff587c5c750c90247281d62212dc97b1b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 23 May 2022 21:05:44 +0200 Subject: [PATCH 05/32] Address Serhiy's review - revert most argument spec changes - align argument spec across docstrings/docs - improve wording of register_adapter/register_converter - undo note --- Doc/library/sqlite3.rst | 23 +++++++++++------------ Modules/_sqlite/clinic/module.c.h | 28 ++++++++++++++-------------- Modules/_sqlite/module.c | 22 +++++++++++----------- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 8f3dad4ba3126e..bc14b832f21542 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -318,23 +318,22 @@ Module functions and constants Added the ``sqlite3.connect/handle`` auditing event. -.. function:: register_converter(type, callable) +.. function:: register_converter(typename, converter) - Register *callable* to convert SQLite *type* into a Python type. - *callable* is invoked for all values of type *typename*. - Confer the parameter *detect_types* of :meth:`Connection.connect` - regarding how type detection works. + Register callable *converter* to convert SQLite type name *typename* into a + Python type. The converter is invoked for all SQLite values of type + *typename*. Confer the parameter *detect_types* of + :meth:`Connection.connect` regarding how type detection works. - .. note:: - *type* and the name of the type in your query are matched in a - case-insensitive manner. + Note: *typename* and the name of the type in your query are matched in a + case-insensitive manner. -.. function:: register_adapter(type, callable) +.. function:: register_adapter(type, adapter) - Register *callable* to adapt Python *type* into an SQLite type. - *callable* is called with the Python value and must return a valid SQLite - type: + Register callable *adapter* to adapt Python type *type* into an SQLite type. + The adapter is called with a Python object as its sole argument, + and must return a valid SQLite type: :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. diff --git a/Modules/_sqlite/clinic/module.c.h b/Modules/_sqlite/clinic/module.c.h index fcb83720e0b663..be07d5b2613fda 100644 --- a/Modules/_sqlite/clinic/module.c.h +++ b/Modules/_sqlite/clinic/module.c.h @@ -157,7 +157,7 @@ pysqlite_complete_statement(PyObject *module, PyObject *const *args, Py_ssize_t } PyDoc_STRVAR(pysqlite_register_adapter__doc__, -"register_adapter($module, type, callable, /)\n" +"register_adapter($module, type, adapter, /)\n" "--\n" "\n" "Register a function to adapt Python types to SQLite types."); @@ -167,28 +167,28 @@ PyDoc_STRVAR(pysqlite_register_adapter__doc__, static PyObject * pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, - PyObject *adapter); + PyObject *caster); static PyObject * pysqlite_register_adapter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyTypeObject *type; - PyObject *adapter; + PyObject *caster; if (!_PyArg_CheckPositional("register_adapter", nargs, 2, 2)) { goto exit; } type = (PyTypeObject *)args[0]; - adapter = args[1]; - return_value = pysqlite_register_adapter_impl(module, type, adapter); + caster = args[1]; + return_value = pysqlite_register_adapter_impl(module, type, caster); exit: return return_value; } PyDoc_STRVAR(pysqlite_register_converter__doc__, -"register_converter($module, type, callable, /)\n" +"register_converter($module, typename, converter, /)\n" "--\n" "\n" "Register a function to convert SQLite types to Python types."); @@ -197,15 +197,15 @@ PyDoc_STRVAR(pysqlite_register_converter__doc__, {"register_converter", _PyCFunction_CAST(pysqlite_register_converter), METH_FASTCALL, pysqlite_register_converter__doc__}, static PyObject * -pysqlite_register_converter_impl(PyObject *module, PyObject *tp, - PyObject *converter); +pysqlite_register_converter_impl(PyObject *module, PyObject *orig_name, + PyObject *callable); static PyObject * pysqlite_register_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *tp; - PyObject *converter; + PyObject *orig_name; + PyObject *callable; if (!_PyArg_CheckPositional("register_converter", nargs, 2, 2)) { goto exit; @@ -217,9 +217,9 @@ pysqlite_register_converter(PyObject *module, PyObject *const *args, Py_ssize_t if (PyUnicode_READY(args[0]) == -1) { goto exit; } - tp = args[0]; - converter = args[1]; - return_value = pysqlite_register_converter_impl(module, tp, converter); + orig_name = args[0]; + callable = args[1]; + return_value = pysqlite_register_converter_impl(module, orig_name, callable); exit: return return_value; @@ -292,4 +292,4 @@ pysqlite_adapt(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=b03f4a0db3e35eb7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8f65c836463b53d0 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index cedd94ad62472a..16dab09ff71643 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -108,7 +108,7 @@ pysqlite_complete_statement_impl(PyObject *module, const char *statement) _sqlite3.register_adapter as pysqlite_register_adapter type: object(type='PyTypeObject *') - callable as adapter: object + adapter as caster: object / Register a function to adapt Python types to SQLite types. @@ -116,8 +116,8 @@ Register a function to adapt Python types to SQLite types. static PyObject * pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, - PyObject *adapter) -/*[clinic end generated code: output=83bab29ff572feb4 input=b6813f5d620955ed]*/ + PyObject *caster) +/*[clinic end generated code: output=a287e8db18e8af23 input=f96c4fb2beba002b]*/ { int rc; @@ -131,7 +131,7 @@ pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, pysqlite_state *state = pysqlite_get_state(module); PyObject *protocol = (PyObject *)state->PrepareProtocolType; - rc = pysqlite_microprotocols_add(state, type, protocol, adapter); + rc = pysqlite_microprotocols_add(state, type, protocol, caster); if (rc == -1) { return NULL; } @@ -142,29 +142,29 @@ pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, /*[clinic input] _sqlite3.register_converter as pysqlite_register_converter - type as tp: unicode - callable as converter: object + typename as orig_name: unicode + converter as callable: object / Register a function to convert SQLite types to Python types. [clinic start generated code]*/ static PyObject * -pysqlite_register_converter_impl(PyObject *module, PyObject *tp, - PyObject *converter) -/*[clinic end generated code: output=59b8c05cdddf7e54 input=63d20a7be9873f12]*/ +pysqlite_register_converter_impl(PyObject *module, PyObject *orig_name, + PyObject *callable) +/*[clinic end generated code: output=a2f2bfeed7230062 input=138f93f0063cb031]*/ { PyObject* name = NULL; PyObject* retval = NULL; /* convert the name to upper case */ pysqlite_state *state = pysqlite_get_state(module); - name = PyObject_CallMethodNoArgs(tp, state->str_upper); + name = PyObject_CallMethodNoArgs(orig_name, state->str_upper); if (!name) { goto error; } - if (PyDict_SetItem(state->converters, name, converter) != 0) { + if (PyDict_SetItem(state->converters, name, callable) != 0) { goto error; } From 0b6f3818addf57c5333bb06dad44947c95328adb Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 25 May 2022 20:27:54 +0200 Subject: [PATCH 06/32] Address Alex' review --- Doc/includes/sqlite3/converter_point.py | 2 +- Doc/library/sqlite3.rst | 64 ++++++++++++++----------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Doc/includes/sqlite3/converter_point.py b/Doc/includes/sqlite3/converter_point.py index 3001133330a539..147807a2225ff0 100644 --- a/Doc/includes/sqlite3/converter_point.py +++ b/Doc/includes/sqlite3/converter_point.py @@ -8,7 +8,7 @@ def __repr__(self): return f"Point({self.x}, {self.y})" def adapt_point(point): - return ("%f;%f" % (point.x, point.y)).encode("utf-8") + return f"{point.x};{point.y}".encode("utf-8") def convert_point(s): x, y = list(map(float, s.split(b";"))) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index bc14b832f21542..eb3e9bca804b3a 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -199,12 +199,12 @@ Module functions and constants .. data:: PARSE_DECLTYPES - Use this flag with the *detect_types* keyword of :meth:`connect` to enable - parsing of declared types for each column it return. + Use this flag with the *detect_types* parameter of :meth:`connect` to enable + parsing of declared types for each column returned. The types are declared when the database table is created. - :mod:`sqlite3` will look up a converter function using the first word of the + ``sqlite3`` will look up a converter function using the first word of the declared type as the converter dictionary key. - The following SQL code results in the following lookups: + For example, the following SQL code results in the following lookups: .. code-block:: sql @@ -220,11 +220,11 @@ Module functions and constants .. data:: PARSE_COLNAMES - Use this flag with the *detect_types* keyword of :meth:`connect` to enable + Use this flag with the *detect_types* parameter of :meth:`connect` to enable parsing of column names in queries. - :mod:`sqlite3` will look for strings containing brackets, and will look up a - converter function using the word inside the brackets as the converter - dictionary key. + ``sqlite3`` will look for strings containing square brackets (``[]``), + and will look up a converter function using the word inside the brackets as + the converter dictionary key. .. code-block:: sql @@ -262,8 +262,8 @@ Module functions and constants Set it to any combination of :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES` to enable type detection. Types cannot be detected for generated fields (for example ``max(data)``), - even when *detect_types* parameter is set. In such cases, the returned type - is :class:`str`. + even when the *detect_types* parameter is set. + In such cases, the returned type is :class:`str`. By default, *check_same_thread* is :const:`True` and only the creating thread may use the connection. If set :const:`False`, the returned connection may be shared @@ -320,9 +320,9 @@ Module functions and constants .. function:: register_converter(typename, converter) - Register callable *converter* to convert SQLite type name *typename* into a - Python type. The converter is invoked for all SQLite values of type - *typename*. Confer the parameter *detect_types* of + Register the *converter* callable to convert SQLite objects of type *typename* into a + Python object of a specific type. The converter is invoked for all SQLite values of type + *typename*. Consult the parameter *detect_types* of :meth:`Connection.connect` regarding how type detection works. Note: *typename* and the name of the type in your query are matched in a @@ -331,7 +331,7 @@ Module functions and constants .. function:: register_adapter(type, adapter) - Register callable *adapter* to adapt Python type *type* into an SQLite type. + Register an *adapter* callable to adapt the Python type *type* into an SQLite type. The adapter is called with a Python object as its sole argument, and must return a valid SQLite type: :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. @@ -1219,12 +1219,13 @@ types via converters. Using adapters to store custom Python types in SQLite databases ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +SQLite supports only a limited set of types natively. To store custom Python types in SQLite databases, **adapt** them one of the basic types supported by SQLite: :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. There are two ways to adapt Python objects to SQLite types: -letting your object adapt itself, or using an adapter function. +letting your object adapt itself, or using an *adapter callable*. The latter will take precedence above the former. For a library that exports a custom type, it may make sense to let that type be able to adapt itself. As an application developer, it may make more sense to take control, and register @@ -1236,10 +1237,11 @@ Letting your object adapt itself Suppose we have ``Point`` class that represents a pair of coordinates, ``x`` and ``y``, in a Cartesian coordinate system. -We want to store the coordinate pair as a text string in the database, +The coordinate pair will be stored as a text string in the database, using a semicolon to separate the coordinates. -We implement this by adding a ``__conform__(self, protocol)`` method which -returns the adapted value. *protocol* will be :class:`PrepareProtocol`. +This can be implemented by adding a ``__conform__(self, protocol)`` +method which returns the adapted value. +The object passed to *protocol* will be of type :class:`PrepareProtocol`. .. literalinclude:: ../includes/sqlite3/adapter_point_1.py @@ -1247,9 +1249,9 @@ returns the adapted value. *protocol* will be :class:`PrepareProtocol`. Registering an adapter callable """"""""""""""""""""""""""""""" -Continuing the above example, let's rewrite it using an adapter function. -We use :meth:`register_adapter` to add an adapter function that takes a Python -type as its argument, and returns an SQLite compatible type. +The other possibility is to create a function that converts the Python object +to an SQLite-compatible type. +This function can then be registered using :meth:`register_adapter`. .. literalinclude:: ../includes/sqlite3/adapter_point_2.py @@ -1264,15 +1266,19 @@ but as a Unix timestamp. Converting SQLite values to custom Python types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Writing an adapter lets you send custom Python types to SQLite. But to make it +really useful we need to make the Python to SQLite to Python roundtrip work. To be able to convert SQLite value to custom Python types, we use _converters_. -Let's revisit the ``Point`` class example from above; -the coordinate pair is stored in the database as a semicolon separated string. -We define a converter that accept a string, and return a ``Point`` object. +Let's go back to the :class:`Point` class. We stored the x and y coordinates +separated via semicolons as strings in SQLite. + +First, we'll define a converter function that accepts the string as a parameter +and constructs a :class:`Point` object from it. .. note:: - Converter functions **always** are passed a :class:`bytes` object, + Converter functions are **always** passed a :class:`bytes` object, no matter the underlying SQLite data type. :: @@ -1281,9 +1287,9 @@ We define a converter that accept a string, and return a ``Point`` object. x, y = map(float, s.split(b";")) return Point(x, y) -We now need to tell :mod:`sqlite3` when it should convert a given SQLite value. -This is done when connecting to a database, using the *detect_types* keyword of -:meth:`connect`. We've got three options: +We now need to tell ``sqlite3`` when it should convert a given SQLite value. +This is done when connecting to a database, using the *detect_types* parameter +of :meth:`connect`. We've got three options: * Implicit: set *detect_types* to :const:`PARSE_DECLTYPES` * Explicit: set *detect_types* to :const:`PARSE_COLNAMES` @@ -1336,7 +1342,7 @@ This section shows recipes for common adapters and converters. import sqlite3 - # Timezone naive datetime adapters and converters. + # Timezone-naive datetime adapters and converters. def adapt_date(val): return val.isoformat() From c4dde483c89f432df856cadad413aba5fbdbe4ad Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 25 May 2022 21:03:22 +0200 Subject: [PATCH 07/32] Nit --- Doc/library/sqlite3.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index eb3e9bca804b3a..120162d57e5e34 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1336,13 +1336,12 @@ timestamp converter. Adapter and Converter Recipes ----------------------------- -This section shows recipes for common adapters and converters. +This section shows recipes for timezone-naive adapters and converters. .. testcode:: import sqlite3 - # Timezone-naive datetime adapters and converters. def adapt_date(val): return val.isoformat() @@ -1350,15 +1349,17 @@ This section shows recipes for common adapters and converters. return val.isoformat(" ") def convert_date(val): + import datetime return datetime.date(*map(int, val.split(b"-"))) def convert_timestamp(val): + import datetime datepart, timepart = val.split(b" ") year, month, day = map(int, datepart.split(b"-")) timepart_full = timepart.split(b".") hours, minutes, seconds = map(int, timepart_full[0].split(b":")) if len(timepart_full) == 2: - microseconds = int('{:0<6.6}'.format(timepart_full[1].decode())) + microseconds = int("{:0<6.6}".format(timepart_full[1].decode())) else: microseconds = 0 From 72013efd4613c05266f5490d795e27998c3516fe Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 25 May 2022 22:30:11 +0200 Subject: [PATCH 08/32] Address Alex' nitpicks --- Doc/library/sqlite3.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 120162d57e5e34..5619b56d61d0bb 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -323,7 +323,7 @@ Module functions and constants Register the *converter* callable to convert SQLite objects of type *typename* into a Python object of a specific type. The converter is invoked for all SQLite values of type *typename*. Consult the parameter *detect_types* of - :meth:`Connection.connect` regarding how type detection works. + :meth:`Connection.connect` for information regarding how type detection works. Note: *typename* and the name of the type in your query are matched in a case-insensitive manner. @@ -1220,7 +1220,7 @@ Using adapters to store custom Python types in SQLite databases ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SQLite supports only a limited set of types natively. -To store custom Python types in SQLite databases, **adapt** them one of the +To store custom Python types in SQLite databases, **adapt** them to one of the basic types supported by SQLite: :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. @@ -1235,7 +1235,7 @@ custom adapter functions. Letting your object adapt itself """""""""""""""""""""""""""""""" -Suppose we have ``Point`` class that represents a pair of coordinates, +Suppose we have a ``Point`` class that represents a pair of coordinates, ``x`` and ``y``, in a Cartesian coordinate system. The coordinate pair will be stored as a text string in the database, using a semicolon to separate the coordinates. @@ -1268,7 +1268,7 @@ Converting SQLite values to custom Python types Writing an adapter lets you send custom Python types to SQLite. But to make it really useful we need to make the Python to SQLite to Python roundtrip work. -To be able to convert SQLite value to custom Python types, we use _converters_. +To be able to convert SQLite values to custom Python types, we use _converters_. Let's go back to the :class:`Point` class. We stored the x and y coordinates separated via semicolons as strings in SQLite. From 3c70d523c34d1408c950a66dbcad84f9a9b043d6 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 25 May 2022 22:57:52 +0200 Subject: [PATCH 09/32] Address in-house review --- Doc/library/sqlite3.rst | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 5619b56d61d0bb..60d1ce2663e39b 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -199,7 +199,7 @@ Module functions and constants .. data:: PARSE_DECLTYPES - Use this flag with the *detect_types* parameter of :meth:`connect` to enable + Use this flag together with the *detect_types* parameter of :meth:`connect` to enable parsing of declared types for each column returned. The types are declared when the database table is created. ``sqlite3`` will look up a converter function using the first word of the @@ -220,7 +220,7 @@ Module functions and constants .. data:: PARSE_COLNAMES - Use this flag with the *detect_types* parameter of :meth:`connect` to enable + Use this flag together with the *detect_types* parameter of :meth:`connect` to enable parsing of column names in queries. ``sqlite3`` will look for strings containing square brackets (``[]``), and will look up a converter function using the word inside the brackets as @@ -1220,7 +1220,7 @@ Using adapters to store custom Python types in SQLite databases ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SQLite supports only a limited set of types natively. -To store custom Python types in SQLite databases, **adapt** them to one of the +To store custom Python types in SQLite databases, *adapt* them to one of the basic types supported by SQLite: :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. @@ -1256,9 +1256,9 @@ This function can then be registered using :meth:`register_adapter`. .. literalinclude:: ../includes/sqlite3/adapter_point_2.py The :mod:`sqlite3` module has two default adapters for Python's built-in -:class:`datetime.date` and :class:`datetime.datetime` types. Now let's suppose -we want to store :class:`datetime.datetime` objects not in ISO representation, -but as a Unix timestamp. +:class:`datetime.date` and :class:`datetime.datetime` types. +Suppose we want to store :class:`datetime.datetime` objects not in ISO +representation, but as a Unix timestamp. .. literalinclude:: ../includes/sqlite3/adapter_datetime.py @@ -1266,9 +1266,8 @@ but as a Unix timestamp. Converting SQLite values to custom Python types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Writing an adapter lets you send custom Python types to SQLite. But to make it -really useful we need to make the Python to SQLite to Python roundtrip work. -To be able to convert SQLite values to custom Python types, we use _converters_. +Writing an adapter lets you send custom Python types to SQLite. +To be able to convert SQLite values to custom Python types, we use *converters*. Let's go back to the :class:`Point` class. We stored the x and y coordinates separated via semicolons as strings in SQLite. @@ -1289,7 +1288,7 @@ and constructs a :class:`Point` object from it. We now need to tell ``sqlite3`` when it should convert a given SQLite value. This is done when connecting to a database, using the *detect_types* parameter -of :meth:`connect`. We've got three options: +of :meth:`connect`. There are three options: * Implicit: set *detect_types* to :const:`PARSE_DECLTYPES` * Explicit: set *detect_types* to :const:`PARSE_COLNAMES` From 681baadb2a51816ca8f5b811152ede637944d90d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 25 May 2022 23:39:55 +0200 Subject: [PATCH 10/32] Recipes, take 1 --- Doc/includes/sqlite3/adapter_datetime.py | 17 ------- Doc/library/sqlite3.rst | 57 +++++++++++------------- 2 files changed, 27 insertions(+), 47 deletions(-) delete mode 100644 Doc/includes/sqlite3/adapter_datetime.py diff --git a/Doc/includes/sqlite3/adapter_datetime.py b/Doc/includes/sqlite3/adapter_datetime.py deleted file mode 100644 index d5221d80c35c8a..00000000000000 --- a/Doc/includes/sqlite3/adapter_datetime.py +++ /dev/null @@ -1,17 +0,0 @@ -import sqlite3 -import datetime -import time - -def adapt_datetime(ts): - return time.mktime(ts.timetuple()) - -sqlite3.register_adapter(datetime.datetime, adapt_datetime) - -con = sqlite3.connect(":memory:") -cur = con.cursor() - -now = datetime.datetime.now() -cur.execute("select ?", (now,)) -print(cur.fetchone()[0]) - -con.close() diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 60d1ce2663e39b..7b00e3c539c261 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1255,13 +1255,6 @@ This function can then be registered using :meth:`register_adapter`. .. literalinclude:: ../includes/sqlite3/adapter_point_2.py -The :mod:`sqlite3` module has two default adapters for Python's built-in -:class:`datetime.date` and :class:`datetime.datetime` types. -Suppose we want to store :class:`datetime.datetime` objects not in ISO -representation, but as a Unix timestamp. - -.. literalinclude:: ../includes/sqlite3/adapter_datetime.py - Converting SQLite values to custom Python types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1333,41 +1326,45 @@ timestamp converter. .. _sqlite3-adapter-converter-recipes: Adapter and Converter Recipes ------------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This section shows recipes for timezone-naive adapters and converters. +This section shows recipes for common adapters and converters. -.. testcode:: +.. code-block:: + import datetime import sqlite3 - def adapt_date(val): + def adapt_date_iso(val): + """Adapt datetime.date to ISO 8601 date.""" + return val.isoformat() + + def adapt_datetime_iso(val): + """Adapt datetime.datetime to timezone-naive ISO 8601 date.""" return val.isoformat() - def adapt_datetime(val): - return val.isoformat(" ") + def adapt_datetime_epoch(val) + """Adapt datetime.datetime to Unix timestamp.""" + return int(val.timestamp()) + + sqlite3.register_adapter(datetime.date, adapt_date_iso) + sqlite3.register_adapter(datetime.datetime, adapt_datetime_iso) + sqlite3.register_adapter(datetime.datetime, adapt_datetime_epoch) def convert_date(val): - import datetime - return datetime.date(*map(int, val.split(b"-"))) + """Convert ISO 8601 date to datetime.date object.""" + return datetime.date.fromisoformat(val) + + def convert_datetime(val): + """Convert ISO 8601 datetime to datetime.datetime object.""" + return datetime.datetime.fromisoformat(val) def convert_timestamp(val): - import datetime - datepart, timepart = val.split(b" ") - year, month, day = map(int, datepart.split(b"-")) - timepart_full = timepart.split(b".") - hours, minutes, seconds = map(int, timepart_full[0].split(b":")) - if len(timepart_full) == 2: - microseconds = int("{:0<6.6}".format(timepart_full[1].decode())) - else: - microseconds = 0 - - val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds) - return val - - sqlite3.register_adapter(datetime.date, adapt_date) - sqlite3.register_adapter(datetime.datetime, adapt_datetime) + """Convert Unix epoch timestamp to datetime.datetime object.""" + return datetime.datetime.fromtimestamp(val) + sqlite3.register_converter("date", convert_date) + sqlite3.register_converter("datetime", convert_datetime) sqlite3.register_converter("timestamp", convert_timestamp) From bf4cb1fdfc857fdf689d8dc0a06fe3090e3c68ce Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 25 May 2022 23:57:11 +0200 Subject: [PATCH 11/32] Docstring wording --- Modules/_sqlite/clinic/module.c.h | 6 +++--- Modules/_sqlite/module.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/_sqlite/clinic/module.c.h b/Modules/_sqlite/clinic/module.c.h index be07d5b2613fda..ef0dd4d1c103ea 100644 --- a/Modules/_sqlite/clinic/module.c.h +++ b/Modules/_sqlite/clinic/module.c.h @@ -160,7 +160,7 @@ PyDoc_STRVAR(pysqlite_register_adapter__doc__, "register_adapter($module, type, adapter, /)\n" "--\n" "\n" -"Register a function to adapt Python types to SQLite types."); +"Register a function to adapt Python objects to SQLite values."); #define PYSQLITE_REGISTER_ADAPTER_METHODDEF \ {"register_adapter", _PyCFunction_CAST(pysqlite_register_adapter), METH_FASTCALL, pysqlite_register_adapter__doc__}, @@ -191,7 +191,7 @@ PyDoc_STRVAR(pysqlite_register_converter__doc__, "register_converter($module, typename, converter, /)\n" "--\n" "\n" -"Register a function to convert SQLite types to Python types."); +"Register a function to convert SQLite values to Python objects."); #define PYSQLITE_REGISTER_CONVERTER_METHODDEF \ {"register_converter", _PyCFunction_CAST(pysqlite_register_converter), METH_FASTCALL, pysqlite_register_converter__doc__}, @@ -292,4 +292,4 @@ pysqlite_adapt(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=8f65c836463b53d0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9ac18606b0eaec03 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 16dab09ff71643..24d45397e22181 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -111,13 +111,13 @@ _sqlite3.register_adapter as pysqlite_register_adapter adapter as caster: object / -Register a function to adapt Python types to SQLite types. +Register a function to adapt Python objects to SQLite values. [clinic start generated code]*/ static PyObject * pysqlite_register_adapter_impl(PyObject *module, PyTypeObject *type, PyObject *caster) -/*[clinic end generated code: output=a287e8db18e8af23 input=f96c4fb2beba002b]*/ +/*[clinic end generated code: output=a287e8db18e8af23 input=29a5e0f213030242]*/ { int rc; @@ -146,13 +146,13 @@ _sqlite3.register_converter as pysqlite_register_converter converter as callable: object / -Register a function to convert SQLite types to Python types. +Register a function to convert SQLite values to Python objects. [clinic start generated code]*/ static PyObject * pysqlite_register_converter_impl(PyObject *module, PyObject *orig_name, PyObject *callable) -/*[clinic end generated code: output=a2f2bfeed7230062 input=138f93f0063cb031]*/ +/*[clinic end generated code: output=a2f2bfeed7230062 input=159a444971b40378]*/ { PyObject* name = NULL; PyObject* retval = NULL; From 2d1a0c1e28cde64ac2c8850d24c082c95b033f77 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 09:51:08 +0200 Subject: [PATCH 12/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 7b00e3c539c261..a6c93ceb38c33a 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -199,8 +199,9 @@ Module functions and constants .. data:: PARSE_DECLTYPES - Use this flag together with the *detect_types* parameter of :meth:`connect` to enable - parsing of declared types for each column returned. + Pass this flag value to the *detect_types* parameter of + :func:`connect` to look up a converter function using + the declared types for each column. The types are declared when the database table is created. ``sqlite3`` will look up a converter function using the first word of the declared type as the converter dictionary key. From 94308ff8ab10718513ef6a0aa46783c2803df252 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 09:55:13 +0200 Subject: [PATCH 13/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index a6c93ceb38c33a..a0857286ed8865 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -205,7 +205,8 @@ Module functions and constants The types are declared when the database table is created. ``sqlite3`` will look up a converter function using the first word of the declared type as the converter dictionary key. - For example, the following SQL code results in the following lookups: + For example: + .. code-block:: sql From 06657f2c144bfc210bd8e600906656ff71e95d8f Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 09:57:54 +0200 Subject: [PATCH 14/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index a0857286ed8865..3cd732322e233b 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -216,8 +216,8 @@ Module functions and constants n number(10) ! will look up a converter named "number" ) - This flag may be paired with :const:`PARSE_COLNAMES` using the ``|`` - operator. + This flag may be combined with :const:`PARSE_COLNAMES` using the ``|`` + (bitwise or) operator. .. data:: PARSE_COLNAMES From b98e3637a7ca83dc9abdcb022ea27417b6a4ecf6 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 09:58:35 +0200 Subject: [PATCH 15/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 3cd732322e233b..0150a3463ea9d7 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -222,8 +222,9 @@ Module functions and constants .. data:: PARSE_COLNAMES - Use this flag together with the *detect_types* parameter of :meth:`connect` to enable - parsing of column names in queries. + Pass this flag value to the *detect_types* parameter of + :func:`connect` to look up a converter function by + parsing the column names in queries. ``sqlite3`` will look for strings containing square brackets (``[]``), and will look up a converter function using the word inside the brackets as the converter dictionary key. From 97812bb750b449f70245c055ea0569b4b6cfa988 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 10:56:03 +0200 Subject: [PATCH 16/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 0150a3463ea9d7..4201c2291c6383 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -233,8 +233,8 @@ Module functions and constants SELECT p as "p [point]" FROM test; ! will look up converter "point" - This flag may be paired with :const:`PARSE_DECLTYPES` using the ``|`` - operator. + This flag may be combined with :const:`PARSE_DECLTYPES` using the ``|`` + (bitwise or) operator. .. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri]) From d3dd5a2e8e5dba0152ae0530dc1f4fb006e79990 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 10:57:18 +0200 Subject: [PATCH 17/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 4201c2291c6383..c0ae499ed1f7a1 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -262,8 +262,9 @@ Module functions and constants module-level :func:`register_converter` function allow you to easily do that. *detect_types* defaults to 0 (type detection disabled). - Set it to any combination of :const:`PARSE_DECLTYPES` and - :const:`PARSE_COLNAMES` to enable type detection. + Set it to any combination (using ``|``, bitwise or) of + :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES` + to enable type detection. Types cannot be detected for generated fields (for example ``max(data)``), even when the *detect_types* parameter is set. In such cases, the returned type is :class:`str`. From f381b659f775aa71b5669284352282f0004fae46 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 10:57:39 +0200 Subject: [PATCH 18/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index c0ae499ed1f7a1..b067f0405ed790 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1285,7 +1285,7 @@ and constructs a :class:`Point` object from it. We now need to tell ``sqlite3`` when it should convert a given SQLite value. This is done when connecting to a database, using the *detect_types* parameter -of :meth:`connect`. There are three options: +of :func:`connect`. There are three options: * Implicit: set *detect_types* to :const:`PARSE_DECLTYPES` * Explicit: set *detect_types* to :const:`PARSE_COLNAMES` From 65eb45c7adb63316db1f83d07fcc21e02d134912 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 10:58:42 +0200 Subject: [PATCH 19/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index b067f0405ed790..5e8d1f04a224d4 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -327,7 +327,7 @@ Module functions and constants Register the *converter* callable to convert SQLite objects of type *typename* into a Python object of a specific type. The converter is invoked for all SQLite values of type *typename*. Consult the parameter *detect_types* of - :meth:`Connection.connect` for information regarding how type detection works. + :func:`connect` for information regarding how type detection works. Note: *typename* and the name of the type in your query are matched in a case-insensitive manner. From 172c7d9ae52918ecaae8e391048fe1d46f074c90 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 10:58:58 +0200 Subject: [PATCH 20/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 5e8d1f04a224d4..0ff84021bc80f9 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1292,7 +1292,7 @@ of :func:`connect`. There are three options: * Both: set *detect_types* to ``sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES`` -The following example illustrates the implicit and explicit approach: +The following example illustrates the implicit and explicit approaches: .. literalinclude:: ../includes/sqlite3/converter_point.py From 5ac2af9e3a0fb83619f205780da7e0c95874f925 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 11:02:04 +0200 Subject: [PATCH 21/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 0ff84021bc80f9..6d4eccfcebd186 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -329,8 +329,8 @@ Module functions and constants *typename*. Consult the parameter *detect_types* of :func:`connect` for information regarding how type detection works. - Note: *typename* and the name of the type in your query are matched in a - case-insensitive manner. + Note: *typename* and the name of the type in your query are matched + case-insensitively. .. function:: register_adapter(type, adapter) From 433bf5a3387e99980a648b35030d22d8a62f9481 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 11:04:31 +0200 Subject: [PATCH 22/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 6d4eccfcebd186..719b7771e5d12d 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -336,7 +336,7 @@ Module functions and constants .. function:: register_adapter(type, adapter) Register an *adapter* callable to adapt the Python type *type* into an SQLite type. - The adapter is called with a Python object as its sole argument, + The adapter is called with a Python type object as its sole argument, and must return a valid SQLite type: :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. From f7646de12cbfb9521bb60f78ceea194568edc2f7 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 11:12:13 +0200 Subject: [PATCH 23/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 719b7771e5d12d..4691d6351f487d 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1231,7 +1231,7 @@ basic types supported by SQLite: There are two ways to adapt Python objects to SQLite types: letting your object adapt itself, or using an *adapter callable*. The latter will take precedence above the former. For a library that exports a -custom type, it may make sense to let that type be able to adapt itself. As an +custom type, it may make sense to enable that type to adapt itself. As an application developer, it may make more sense to take control, and register custom adapter functions. From 6fbcff81a8ab9408a5605098dd61fde747d32c63 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Wed, 22 Jun 2022 11:19:09 +0200 Subject: [PATCH 24/32] Assuming direct control Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 4691d6351f487d..8a775e1e680888 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1232,7 +1232,7 @@ There are two ways to adapt Python objects to SQLite types: letting your object adapt itself, or using an *adapter callable*. The latter will take precedence above the former. For a library that exports a custom type, it may make sense to enable that type to adapt itself. As an -application developer, it may make more sense to take control, and register +application developer, it may make more sense to take direct control by registering custom adapter functions. From b319b546205f4abde72000a1651e657f5ab00f56 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Jun 2022 22:15:45 +0200 Subject: [PATCH 25/32] Address the last part of CAM's review --- Doc/library/sqlite3.rst | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 8a775e1e680888..b258a631185cd4 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -224,10 +224,8 @@ Module functions and constants Pass this flag value to the *detect_types* parameter of :func:`connect` to look up a converter function by - parsing the column names in queries. - ``sqlite3`` will look for strings containing square brackets (``[]``), - and will look up a converter function using the word inside the brackets as - the converter dictionary key. + parsing the type name from the column name in queries. + The type name must be wrapped in square brackets (``[]``) .. code-block:: sql @@ -256,7 +254,8 @@ Module functions and constants For the *isolation_level* parameter, please see the :attr:`~Connection.isolation_level` property of :class:`Connection` objects. - SQLite natively supports only the types TEXT, INTEGER, REAL, BLOB and NULL. If + SQLite :ref:`natively supports` + only the types TEXT, INTEGER, REAL, BLOB and NULL. If you want to use other types you must add support for them yourself. The *detect_types* parameter and the using custom **converters** registered with the module-level :func:`register_converter` function allow you to easily do that. @@ -265,6 +264,7 @@ Module functions and constants Set it to any combination (using ``|``, bitwise or) of :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES` to enable type detection. + Column names takes precedence over declared types if both flags are set. Types cannot be detected for generated fields (for example ``max(data)``), even when the *detect_types* parameter is set. In such cases, the returned type is :class:`str`. @@ -336,9 +336,9 @@ Module functions and constants .. function:: register_adapter(type, adapter) Register an *adapter* callable to adapt the Python type *type* into an SQLite type. - The adapter is called with a Python type object as its sole argument, - and must return a valid SQLite type: - :class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. + The adapter is called with a Python object of type *type* as its sole argument, + and must return a value of a + :ref:`type that SQLite natively understands`. .. function:: complete_statement(sql) @@ -1223,10 +1223,9 @@ types via converters. Using adapters to store custom Python types in SQLite databases ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -SQLite supports only a limited set of types natively. +SQLite supports only a limited set of data types natively. To store custom Python types in SQLite databases, *adapt* them to one of the -basic types supported by SQLite: -:class:`int`, :class:`float`, :class:`str`, :class:`bytes`, or :const:`None`. +:ref:`Python types SQLite natively understands`. There are two ways to adapt Python objects to SQLite types: letting your object adapt itself, or using an *adapter callable*. @@ -1255,7 +1254,7 @@ Registering an adapter callable The other possibility is to create a function that converts the Python object to an SQLite-compatible type. -This function can then be registered using :meth:`register_adapter`. +This function can then be registered using :func:`register_adapter`. .. literalinclude:: ../includes/sqlite3/adapter_point_2.py @@ -1263,8 +1262,9 @@ This function can then be registered using :meth:`register_adapter`. Converting SQLite values to custom Python types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Writing an adapter lets you send custom Python types to SQLite. -To be able to convert SQLite values to custom Python types, we use *converters*. +Writing an adapter lets you convert *from* custom Python types *to* SQLite values. +To be able to convert *from* SQLite values *to* custom Python types, +we use *converters*. Let's go back to the :class:`Point` class. We stored the x and y coordinates separated via semicolons as strings in SQLite. @@ -1290,7 +1290,8 @@ of :func:`connect`. There are three options: * Implicit: set *detect_types* to :const:`PARSE_DECLTYPES` * Explicit: set *detect_types* to :const:`PARSE_COLNAMES` * Both: set *detect_types* to - ``sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES`` + ``sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES``. + Colum names take precedence over declared types. The following example illustrates the implicit and explicit approaches: From e821a7eb6604a6677274c5e42819a1f46d7dcb1d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Jun 2022 22:22:25 +0200 Subject: [PATCH 26/32] Clarify parse column names further --- Doc/library/sqlite3.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 99ff22700a2c76..d3936523e9e1d9 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -234,8 +234,9 @@ Module functions and constants Pass this flag value to the *detect_types* parameter of :func:`connect` to look up a converter function by - parsing the type name from the column name in queries. - The type name must be wrapped in square brackets (``[]``) + using the type name, parsed from the query column name, + as the converter dictionary key. + The type name must be wrapped in square brackets (``[]``). .. code-block:: sql From bc295d8d0795991b7f88f05092540265f31fe2c9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Jun 2022 22:23:59 +0200 Subject: [PATCH 27/32] Revert unneeded change --- Doc/library/sqlite3.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index d3936523e9e1d9..078c0871bbdc2b 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -265,8 +265,7 @@ Module functions and constants For the *isolation_level* parameter, please see the :attr:`~Connection.isolation_level` property of :class:`Connection` objects. - SQLite :ref:`natively supports` - only the types TEXT, INTEGER, REAL, BLOB and NULL. If + SQLite natively supports only the types TEXT, INTEGER, REAL, BLOB and NULL. If you want to use other types you must add support for them yourself. The *detect_types* parameter and the using custom **converters** registered with the module-level :func:`register_converter` function allow you to easily do that. From 9235f8d7c61d513df3eb88d550a828cd4eb53f46 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Jun 2022 22:33:53 +0200 Subject: [PATCH 28/32] Further clarify register_converter --- Doc/library/sqlite3.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 078c0871bbdc2b..4b3235af8eba48 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -336,7 +336,10 @@ Module functions and constants Register the *converter* callable to convert SQLite objects of type *typename* into a Python object of a specific type. The converter is invoked for all SQLite values of type - *typename*. Consult the parameter *detect_types* of + *typename*; + it is passed a :class:`bytes` object and should return an object of the + desired Python type. + Consult the parameter *detect_types* of :func:`connect` for information regarding how type detection works. Note: *typename* and the name of the type in your query are matched From 84841642219afdebea7eaf04d8adf9bd34face1d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Jun 2022 22:38:30 +0200 Subject: [PATCH 29/32] Use testsetup/doctest --- Doc/library/sqlite3.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 4b3235af8eba48..e189b9c80e6b80 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1379,11 +1379,14 @@ Adapter and Converter Recipes This section shows recipes for common adapters and converters. -.. code-block:: +.. testsetup:: - import datetime import sqlite3 +.. doctest:: + + import datetime + def adapt_date_iso(val): """Adapt datetime.date to ISO 8601 date.""" return val.isoformat() From b579f67a58bd4ec6b7e5a024e4f0c3640ecb0fb7 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 23 Jun 2022 23:11:06 +0200 Subject: [PATCH 30/32] Revert "Use testsetup/doctest" This reverts commit 84841642219afdebea7eaf04d8adf9bd34face1d. --- Doc/library/sqlite3.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index e189b9c80e6b80..4b3235af8eba48 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1379,13 +1379,10 @@ Adapter and Converter Recipes This section shows recipes for common adapters and converters. -.. testsetup:: - - import sqlite3 - -.. doctest:: +.. code-block:: import datetime + import sqlite3 def adapt_date_iso(val): """Adapt datetime.date to ISO 8601 date.""" From 0e42fa6739f20b7849bb0b7353924f9685a3037a Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Fri, 24 Jun 2022 12:42:57 +0200 Subject: [PATCH 31/32] Update Doc/library/sqlite3.rst Co-authored-by: CAM Gerlach --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 4b3235af8eba48..4e3c99e6d6ace3 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -267,7 +267,7 @@ Module functions and constants SQLite natively supports only the types TEXT, INTEGER, REAL, BLOB and NULL. If you want to use other types you must add support for them yourself. The - *detect_types* parameter and the using custom **converters** registered with the + *detect_types* parameter and using custom **converters** registered with the module-level :func:`register_converter` function allow you to easily do that. *detect_types* defaults to 0 (type detection disabled). From 8d97fcb8e2f1c537d7a09bc9ca0e64fe0ba0bf8b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 25 Jun 2022 20:43:43 +0200 Subject: [PATCH 32/32] Reflow --- Doc/library/sqlite3.rst | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 4e3c99e6d6ace3..a99485b6ba7bc4 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -334,9 +334,9 @@ Module functions and constants .. function:: register_converter(typename, converter) - Register the *converter* callable to convert SQLite objects of type *typename* into a - Python object of a specific type. The converter is invoked for all SQLite values of type - *typename*; + Register the *converter* callable to convert SQLite objects of type + *typename* into a Python object of a specific type. + The converter is invoked for all SQLite values of type *typename*; it is passed a :class:`bytes` object and should return an object of the desired Python type. Consult the parameter *detect_types* of @@ -348,9 +348,10 @@ Module functions and constants .. function:: register_adapter(type, adapter) - Register an *adapter* callable to adapt the Python type *type* into an SQLite type. - The adapter is called with a Python object of type *type* as its sole argument, - and must return a value of a + Register an *adapter* callable to adapt the Python type *type* into an + SQLite type. + The adapter is called with a Python object of type *type* as its sole + argument, and must return a value of a :ref:`type that SQLite natively understands`. @@ -1273,10 +1274,11 @@ To store custom Python types in SQLite databases, *adapt* them to one of the There are two ways to adapt Python objects to SQLite types: letting your object adapt itself, or using an *adapter callable*. -The latter will take precedence above the former. For a library that exports a -custom type, it may make sense to enable that type to adapt itself. As an -application developer, it may make more sense to take direct control by registering -custom adapter functions. +The latter will take precedence above the former. +For a library that exports a custom type, +it may make sense to enable that type to adapt itself. +As an application developer, it may make more sense to take direct control by +registering custom adapter functions. Letting your object adapt itself @@ -1306,7 +1308,8 @@ This function can then be registered using :func:`register_adapter`. Converting SQLite values to custom Python types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Writing an adapter lets you convert *from* custom Python types *to* SQLite values. +Writing an adapter lets you convert *from* custom Python types *to* SQLite +values. To be able to convert *from* SQLite values *to* custom Python types, we use *converters*.