]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-108278: Deprecate passing the first param of sqlite3.Connection callback APIs...
authorErlend E. Aasland <erlend@python.org>
Tue, 29 Aug 2023 20:02:12 +0000 (22:02 +0200)
committerGitHub <noreply@github.com>
Tue, 29 Aug 2023 20:02:12 +0000 (22:02 +0200)
Deprecate passing the callback callable by keyword for the following
sqlite3.Connection APIs:

- set_authorizer(authorizer_callback)
- set_progress_handler(progress_handler, ...)
- set_trace_callback(trace_callback)

The affected parameters will become positional-only in Python 3.15.

Doc/library/sqlite3.rst
Doc/whatsnew/3.13.rst
Lib/test/test_sqlite3/test_hooks.py
Lib/test/test_sqlite3/test_userfunctions.py
Misc/NEWS.d/next/Library/2023-08-29-11-29-15.gh-issue-108278.-UhsnJ.rst [new file with mode: 0644]
Modules/_sqlite/clinic/connection.c.h
Modules/_sqlite/connection.c

index 5137e173e23740536f772a631953b6643c778aff..0abdab52340dfdcd4c5f236deae570aaff04ffdc 100644 (file)
@@ -763,10 +763,10 @@ Connection objects
          ...     print(row)
          ('acbd18db4cc2f85cedef654fccc4a4d8',)
 
-   .. versionchanged:: 3.13
+      .. versionchanged:: 3.13
 
-      Passing *name*, *narg*, and *func* as keyword arguments is deprecated.
-      These parameters will become positional-only in Python 3.15.
+         Passing *name*, *narg*, and *func* as keyword arguments is deprecated.
+         These parameters will become positional-only in Python 3.15.
 
 
    .. method:: create_aggregate(name, n_arg, aggregate_class)
@@ -822,10 +822,10 @@ Connection objects
 
          3
 
-   .. versionchanged:: 3.13
+      .. versionchanged:: 3.13
 
-      Passing *name*, *n_arg*, and *aggregate_class* as keyword arguments is deprecated.
-      These parameters will become positional-only in Python 3.15.
+         Passing *name*, *n_arg*, and *aggregate_class* as keyword arguments is deprecated.
+         These parameters will become positional-only in Python 3.15.
 
 
    .. method:: create_window_function(name, num_params, aggregate_class, /)
@@ -991,6 +991,11 @@ Connection objects
       .. versionchanged:: 3.11
          Added support for disabling the authorizer using ``None``.
 
+      .. versionchanged:: 3.13
+
+      Passing *authorizer_callback* as a keyword argument to is deprecated.
+      The parameter will become positional-only in Python 3.15.
+
 
    .. method:: set_progress_handler(progress_handler, n)
 
@@ -1006,6 +1011,11 @@ Connection objects
       currently executing query and cause it to raise a :exc:`DatabaseError`
       exception.
 
+      .. versionchanged:: 3.13
+
+      Passing *progress_handler* as a keyword argument to is deprecated.
+      The parameter will become positional-only in Python 3.15.
+
 
    .. method:: set_trace_callback(trace_callback)
 
@@ -1030,6 +1040,11 @@ Connection objects
 
       .. versionadded:: 3.3
 
+      .. versionchanged:: 3.13
+
+      Passing *trace_callback* as a keyword argument to is deprecated.
+      The parameter will become positional-only in Python 3.15.
+
 
    .. method:: enable_load_extension(enabled, /)
 
index 1c94da23bc1b705cf70ba0c63da89db9f1a5cf9c..be5bf9a7ad5aed0a1645e1c7757a9afc0680a62a 100644 (file)
@@ -266,6 +266,13 @@ Deprecated
   * :meth:`~sqlite3.Connection.create_function`
   * :meth:`~sqlite3.Connection.create_aggregate`
 
+  Deprecate passing the callback callable by keyword for the following
+  :class:`sqlite3.Connection` APIs:
+
+  * :meth:`~sqlite3.Connection.set_authorizer`
+  * :meth:`~sqlite3.Connection.set_progress_handler`
+  * :meth:`~sqlite3.Connection.set_trace_callback`
+
   The affected parameters will become positional-only in Python 3.15.
 
   (Contributed by Erlend E. Aasland in :gh:`107948` and :gh:`108278`.)
index 33f0af99532a104fcb51d8ed511c0bcbd95312fd..49e72f8fcfbcbdad1eb4c5398abe49c3c8c8b78b 100644 (file)
@@ -219,6 +219,18 @@ class ProgressTests(MemoryDatabaseMixin, unittest.TestCase):
                 create table foo(a, b)
                 """)
 
+    def test_progress_handler_keyword_args(self):
+        regex = (
+            r"Passing keyword argument 'progress_handler' to "
+            r"_sqlite3.Connection.set_progress_handler\(\) is deprecated. "
+            r"Parameter 'progress_handler' will become positional-only in "
+            r"Python 3.15."
+        )
+
+        with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
+            self.con.set_progress_handler(progress_handler=lambda: None, n=1)
+        self.assertEqual(cm.filename, __file__)
+
 
 class TraceCallbackTests(MemoryDatabaseMixin, unittest.TestCase):
 
@@ -340,6 +352,18 @@ class TraceCallbackTests(MemoryDatabaseMixin, unittest.TestCase):
             cx.set_trace_callback(lambda stmt: 5/0)
             cx.execute("select 1")
 
+    def test_trace_keyword_args(self):
+        regex = (
+            r"Passing keyword argument 'trace_callback' to "
+            r"_sqlite3.Connection.set_trace_callback\(\) is deprecated. "
+            r"Parameter 'trace_callback' will become positional-only in "
+            r"Python 3.15."
+        )
+
+        with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
+            self.con.set_trace_callback(trace_callback=lambda: None)
+        self.assertEqual(cm.filename, __file__)
+
 
 if __name__ == "__main__":
     unittest.main()
index d86b8c6025f6713c80e827c044d22faae394edf8..09019498fd56820d4ee9fb46cfd3f31b1e1a0e73 100644 (file)
@@ -737,6 +737,27 @@ class AggregateTests(unittest.TestCase):
                 val = cur.fetchone()[0]
                 self.assertEqual(val, txt)
 
+    def test_agg_keyword_args(self):
+        regex = (
+            r"Passing keyword arguments 'name', 'n_arg' and 'aggregate_class' to "
+            r"_sqlite3.Connection.create_aggregate\(\) is deprecated. "
+            r"Parameters 'name', 'n_arg' and 'aggregate_class' will become "
+            r"positional-only in Python 3.15."
+        )
+
+        with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
+            self.con.create_aggregate("test", 1, aggregate_class=AggrText)
+        self.assertEqual(cm.filename, __file__)
+
+        with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
+            self.con.create_aggregate("test", n_arg=1, aggregate_class=AggrText)
+        self.assertEqual(cm.filename, __file__)
+
+        with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
+            self.con.create_aggregate(name="test", n_arg=0,
+                                      aggregate_class=AggrText)
+        self.assertEqual(cm.filename, __file__)
+
 
 class AuthorizerTests(unittest.TestCase):
     @staticmethod
@@ -779,6 +800,18 @@ class AuthorizerTests(unittest.TestCase):
         self.con.execute("select * from t2")
         self.con.execute("select c2 from t1")
 
+    def test_authorizer_keyword_args(self):
+        regex = (
+            r"Passing keyword argument 'authorizer_callback' to "
+            r"_sqlite3.Connection.set_authorizer\(\) is deprecated. "
+            r"Parameter 'authorizer_callback' will become positional-only in "
+            r"Python 3.15."
+        )
+
+        with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
+            self.con.set_authorizer(authorizer_callback=lambda: None)
+        self.assertEqual(cm.filename, __file__)
+
 
 class AuthorizerRaiseExceptionTests(AuthorizerTests):
     @staticmethod
diff --git a/Misc/NEWS.d/next/Library/2023-08-29-11-29-15.gh-issue-108278.-UhsnJ.rst b/Misc/NEWS.d/next/Library/2023-08-29-11-29-15.gh-issue-108278.-UhsnJ.rst
new file mode 100644 (file)
index 0000000..fa2dbdf
--- /dev/null
@@ -0,0 +1,10 @@
+Deprecate passing the callback callable by keyword for the following
+:class:`sqlite3.Connection` APIs:
+
+* :meth:`~sqlite3.Connection.set_authorizer`
+* :meth:`~sqlite3.Connection.set_progress_handler`
+* :meth:`~sqlite3.Connection.set_trace_callback`
+
+The affected parameters will become positional-only in Python 3.15.
+
+Patch by Erlend E. Aasland.
index 992b2e6c72ea5634377892ac2b37973eb1ab5cd1..f9510f47dd9efa1e029fe1103f83d5a0bd0bb88b 100644 (file)
@@ -712,7 +712,12 @@ PyDoc_STRVAR(pysqlite_connection_set_authorizer__doc__,
 "set_authorizer($self, /, authorizer_callback)\n"
 "--\n"
 "\n"
-"Sets authorizer callback.");
+"Set authorizer callback.\n"
+"\n"
+"Note: Passing keyword argument \'authorizer_callback\' to\n"
+"_sqlite3.Connection.set_authorizer() is deprecated. Parameter\n"
+"\'authorizer_callback\' will become positional-only in Python 3.15.\n"
+"");
 
 #define PYSQLITE_CONNECTION_SET_AUTHORIZER_METHODDEF    \
     {"set_authorizer", _PyCFunction_CAST(pysqlite_connection_set_authorizer), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pysqlite_connection_set_authorizer__doc__},
@@ -722,13 +727,24 @@ pysqlite_connection_set_authorizer_impl(pysqlite_Connection *self,
                                         PyTypeObject *cls,
                                         PyObject *callable);
 
+// Emit compiler warnings when we get to Python 3.15.
+#if PY_VERSION_HEX >= 0x030f00C0
+#  error "Update the clinic input of '_sqlite3.Connection.set_authorizer'."
+#elif PY_VERSION_HEX >= 0x030f00A0
+#  ifdef _MSC_VER
+#    pragma message ("Update the clinic input of '_sqlite3.Connection.set_authorizer'.")
+#  else
+#    warning "Update the clinic input of '_sqlite3.Connection.set_authorizer'."
+#  endif
+#endif
+
 static PyObject *
 pysqlite_connection_set_authorizer(pysqlite_Connection *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyObject *return_value = NULL;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
 
-    #define NUM_KEYWORDS 1
+    #define NUM_KEYWORDS 2
     static struct {
         PyGC_Head _this_is_not_used;
         PyObject_VAR_HEAD
@@ -758,6 +774,16 @@ pysqlite_connection_set_authorizer(pysqlite_Connection *self, PyTypeObject *cls,
     if (!args) {
         goto exit;
     }
+    if (nargs < 1) {
+        if (PyErr_WarnEx(PyExc_DeprecationWarning,
+                "Passing keyword argument 'authorizer_callback' to "
+                "_sqlite3.Connection.set_authorizer() is deprecated. Parameter "
+                "'authorizer_callback' will become positional-only in Python "
+                "3.15.", 1))
+        {
+            goto exit;
+        }
+    }
     callable = args[0];
     return_value = pysqlite_connection_set_authorizer_impl(self, cls, callable);
 
@@ -769,7 +795,22 @@ PyDoc_STRVAR(pysqlite_connection_set_progress_handler__doc__,
 "set_progress_handler($self, /, progress_handler, n)\n"
 "--\n"
 "\n"
-"Sets progress handler callback.");
+"Set progress handler callback.\n"
+"\n"
+"  progress_handler\n"
+"    A callable that takes no arguments.\n"
+"    If the callable returns non-zero, the current query is terminated,\n"
+"    and an exception is raised.\n"
+"  n\n"
+"    The number of SQLite virtual machine instructions that are\n"
+"    executed between invocations of \'progress_handler\'.\n"
+"\n"
+"If \'progress_handler\' is None or \'n\' is 0, the progress handler is disabled.\n"
+"\n"
+"Note: Passing keyword argument \'progress_handler\' to\n"
+"_sqlite3.Connection.set_progress_handler() is deprecated. Parameter\n"
+"\'progress_handler\' will become positional-only in Python 3.15.\n"
+"");
 
 #define PYSQLITE_CONNECTION_SET_PROGRESS_HANDLER_METHODDEF    \
     {"set_progress_handler", _PyCFunction_CAST(pysqlite_connection_set_progress_handler), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pysqlite_connection_set_progress_handler__doc__},
@@ -779,13 +820,24 @@ pysqlite_connection_set_progress_handler_impl(pysqlite_Connection *self,
                                               PyTypeObject *cls,
                                               PyObject *callable, int n);
 
+// Emit compiler warnings when we get to Python 3.15.
+#if PY_VERSION_HEX >= 0x030f00C0
+#  error "Update the clinic input of '_sqlite3.Connection.set_progress_handler'."
+#elif PY_VERSION_HEX >= 0x030f00A0
+#  ifdef _MSC_VER
+#    pragma message ("Update the clinic input of '_sqlite3.Connection.set_progress_handler'.")
+#  else
+#    warning "Update the clinic input of '_sqlite3.Connection.set_progress_handler'."
+#  endif
+#endif
+
 static PyObject *
 pysqlite_connection_set_progress_handler(pysqlite_Connection *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyObject *return_value = NULL;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
 
-    #define NUM_KEYWORDS 2
+    #define NUM_KEYWORDS 3
     static struct {
         PyGC_Head _this_is_not_used;
         PyObject_VAR_HEAD
@@ -816,6 +868,16 @@ pysqlite_connection_set_progress_handler(pysqlite_Connection *self, PyTypeObject
     if (!args) {
         goto exit;
     }
+    if (nargs < 1) {
+        if (PyErr_WarnEx(PyExc_DeprecationWarning,
+                "Passing keyword argument 'progress_handler' to "
+                "_sqlite3.Connection.set_progress_handler() is deprecated. "
+                "Parameter 'progress_handler' will become positional-only in "
+                "Python 3.15.", 1))
+        {
+            goto exit;
+        }
+    }
     callable = args[0];
     n = PyLong_AsInt(args[1]);
     if (n == -1 && PyErr_Occurred()) {
@@ -831,7 +893,12 @@ PyDoc_STRVAR(pysqlite_connection_set_trace_callback__doc__,
 "set_trace_callback($self, /, trace_callback)\n"
 "--\n"
 "\n"
-"Sets a trace callback called for each SQL statement (passed as unicode).");
+"Set a trace callback called for each SQL statement (passed as unicode).\n"
+"\n"
+"Note: Passing keyword argument \'trace_callback\' to\n"
+"_sqlite3.Connection.set_trace_callback() is deprecated. Parameter\n"
+"\'trace_callback\' will become positional-only in Python 3.15.\n"
+"");
 
 #define PYSQLITE_CONNECTION_SET_TRACE_CALLBACK_METHODDEF    \
     {"set_trace_callback", _PyCFunction_CAST(pysqlite_connection_set_trace_callback), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pysqlite_connection_set_trace_callback__doc__},
@@ -841,13 +908,24 @@ pysqlite_connection_set_trace_callback_impl(pysqlite_Connection *self,
                                             PyTypeObject *cls,
                                             PyObject *callable);
 
+// Emit compiler warnings when we get to Python 3.15.
+#if PY_VERSION_HEX >= 0x030f00C0
+#  error "Update the clinic input of '_sqlite3.Connection.set_trace_callback'."
+#elif PY_VERSION_HEX >= 0x030f00A0
+#  ifdef _MSC_VER
+#    pragma message ("Update the clinic input of '_sqlite3.Connection.set_trace_callback'.")
+#  else
+#    warning "Update the clinic input of '_sqlite3.Connection.set_trace_callback'."
+#  endif
+#endif
+
 static PyObject *
 pysqlite_connection_set_trace_callback(pysqlite_Connection *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyObject *return_value = NULL;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
 
-    #define NUM_KEYWORDS 1
+    #define NUM_KEYWORDS 2
     static struct {
         PyGC_Head _this_is_not_used;
         PyObject_VAR_HEAD
@@ -877,6 +955,16 @@ pysqlite_connection_set_trace_callback(pysqlite_Connection *self, PyTypeObject *
     if (!args) {
         goto exit;
     }
+    if (nargs < 1) {
+        if (PyErr_WarnEx(PyExc_DeprecationWarning,
+                "Passing keyword argument 'trace_callback' to "
+                "_sqlite3.Connection.set_trace_callback() is deprecated. "
+                "Parameter 'trace_callback' will become positional-only in Python"
+                " 3.15.", 1))
+        {
+            goto exit;
+        }
+    }
     callable = args[0];
     return_value = pysqlite_connection_set_trace_callback_impl(self, cls, callable);
 
@@ -1734,4 +1822,4 @@ exit:
 #ifndef DESERIALIZE_METHODDEF
     #define DESERIALIZE_METHODDEF
 #endif /* !defined(DESERIALIZE_METHODDEF) */
-/*[clinic end generated code: output=f80eb1d02cf698e4 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=f06b254bc5c2bcaf input=a9049054013a1b77]*/
index 45ec0f99237b7fa68418f701bf43a2f88609cca2..24090b0b63936aa8bc9419653563fac35b180389 100644 (file)
@@ -1499,17 +1499,17 @@ exit:
 _sqlite3.Connection.set_authorizer as pysqlite_connection_set_authorizer
 
     cls: defining_class
-    /
     authorizer_callback as callable: object
+    / [from 3.15]
 
-Sets authorizer callback.
+Set authorizer callback.
 [clinic start generated code]*/
 
 static PyObject *
 pysqlite_connection_set_authorizer_impl(pysqlite_Connection *self,
                                         PyTypeObject *cls,
                                         PyObject *callable)
-/*[clinic end generated code: output=75fa60114fc971c3 input=605d32ba92dd3eca]*/
+/*[clinic end generated code: output=75fa60114fc971c3 input=a52bd4937c588752]*/
 {
     if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
         return NULL;
@@ -1541,18 +1541,25 @@ pysqlite_connection_set_authorizer_impl(pysqlite_Connection *self,
 _sqlite3.Connection.set_progress_handler as pysqlite_connection_set_progress_handler
 
     cls: defining_class
-    /
     progress_handler as callable: object
+        A callable that takes no arguments.
+        If the callable returns non-zero, the current query is terminated,
+        and an exception is raised.
+    / [from 3.15]
     n: int
+        The number of SQLite virtual machine instructions that are
+        executed between invocations of 'progress_handler'.
 
-Sets progress handler callback.
+Set progress handler callback.
+
+If 'progress_handler' is None or 'n' is 0, the progress handler is disabled.
 [clinic start generated code]*/
 
 static PyObject *
 pysqlite_connection_set_progress_handler_impl(pysqlite_Connection *self,
                                               PyTypeObject *cls,
                                               PyObject *callable, int n)
-/*[clinic end generated code: output=0739957fd8034a50 input=f7c1837984bd86db]*/
+/*[clinic end generated code: output=0739957fd8034a50 input=b4d6e2ef8b4d32f9]*/
 {
     if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
         return NULL;
@@ -1578,17 +1585,17 @@ pysqlite_connection_set_progress_handler_impl(pysqlite_Connection *self,
 _sqlite3.Connection.set_trace_callback as pysqlite_connection_set_trace_callback
 
     cls: defining_class
-    /
     trace_callback as callable: object
+    / [from 3.15]
 
-Sets a trace callback called for each SQL statement (passed as unicode).
+Set a trace callback called for each SQL statement (passed as unicode).
 [clinic start generated code]*/
 
 static PyObject *
 pysqlite_connection_set_trace_callback_impl(pysqlite_Connection *self,
                                             PyTypeObject *cls,
                                             PyObject *callable)
-/*[clinic end generated code: output=d91048c03bfcee05 input=351a94210c5f81bb]*/
+/*[clinic end generated code: output=d91048c03bfcee05 input=d705d592ec03cf28]*/
 {
     if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
         return NULL;