https://github.com/python/cpython/commit/d6dc33ed8086fbd79f6adaeac4e329f29a13f834
commit: d6dc33ed8086fbd79f6adaeac4e329f29a13f834
branch: main
author: Bénédikt Tran <10796600+picn...@users.noreply.github.com>
committer: picnixz <10796600+picn...@users.noreply.github.com>
date: 2025-05-19T11:26:14+02:00
summary:

gh-134087: enforce signature of `threading.RLock` (#134178)

- Reject positional and keyword arguments in `_thread.RLock.__new__`.
- Convert `_thread.lock.__new__` to AC.

files:
A Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst
M Doc/whatsnew/3.15.rst
M Lib/test/lock_tests.py
M Lib/test/test_threading.py
M Lib/threading.py
M Modules/_threadmodule.c
M Modules/clinic/_threadmodule.c.h

diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 987cf944972329..bf186c191b04d1 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -145,6 +145,15 @@ sysconfig
   (Contributed by Filipe Laíns in :gh:`92897`.)
 
 
+threading
+---------
+
+* Remove support for arbitrary positional or keyword arguments in the C
+  implementation of :class:`~threading.RLock` objects. This was deprecated
+  in Python 3.14.
+  (Contributed by Bénédikt Tran in :gh:`134087`.)
+
+
 typing
 ------
 
diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py
index 009e04e9c0b522..d64b2b9fe28694 100644
--- a/Lib/test/lock_tests.py
+++ b/Lib/test/lock_tests.py
@@ -124,6 +124,11 @@ def test_constructor(self):
         lock = self.locktype()
         del lock
 
+    def test_constructor_noargs(self):
+        self.assertRaises(TypeError, self.locktype, 1)
+        self.assertRaises(TypeError, self.locktype, x=1)
+        self.assertRaises(TypeError, self.locktype, 1, x=2)
+
     def test_repr(self):
         lock = self.locktype()
         self.assertRegex(repr(lock), "<unlocked .* object (.*)?at .*>")
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index abe63c10c0ac7c..b6e2d419019aa1 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -2137,8 +2137,7 @@ def test_signature(self):  # gh-102029
         ]
         for args, kwargs in arg_types:
             with self.subTest(args=args, kwargs=kwargs):
-                with self.assertWarns(DeprecationWarning):
-                    threading.RLock(*args, **kwargs)
+                self.assertRaises(TypeError, threading.RLock, *args, **kwargs)
 
         # Subtypes with custom `__init__` are allowed (but, not recommended):
         class CustomRLock(self.locktype):
@@ -2156,6 +2155,9 @@ class ConditionAsRLockTests(lock_tests.RLockTests):
     # Condition uses an RLock by default and exports its API.
     locktype = staticmethod(threading.Condition)
 
+    def test_constructor_noargs(self):
+        self.skipTest("Condition allows positional arguments")
+
     def test_recursion_count(self):
         self.skipTest("Condition does not expose _recursion_count()")
 
diff --git a/Lib/threading.py b/Lib/threading.py
index 39a1a7f4cdfda0..9feada3b8bb15b 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -123,7 +123,7 @@ def gettrace():
 
 Lock = _LockType
 
-def RLock(*args, **kwargs):
+def RLock():
     """Factory function that returns a new reentrant lock.
 
     A reentrant lock must be released by the thread that acquired it. Once a
@@ -132,16 +132,9 @@ def RLock(*args, **kwargs):
     acquired it.
 
     """
-    if args or kwargs:
-        import warnings
-        warnings.warn(
-            'Passing arguments to RLock is deprecated and will be removed in 
3.15',
-            DeprecationWarning,
-            stacklevel=2,
-        )
     if _CRLock is None:
-        return _PyRLock(*args, **kwargs)
-    return _CRLock(*args, **kwargs)
+        return _PyRLock()
+    return _CRLock()
 
 class _RLock:
     """This class implements reentrant lock objects.
diff --git 
a/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst 
b/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst
new file mode 100644
index 00000000000000..c4a05965f73ece
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst
@@ -0,0 +1,3 @@
+Remove support for arbitrary positional or keyword arguments in the C
+implementation of :class:`threading.RLock` objects. This was deprecated
+since Python 3.14. Patch by Bénédikt Tran.
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index 9776a32755db68..77286ed2a74669 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -19,8 +19,6 @@
 #  include <signal.h>             // SIGINT
 #endif
 
-#include "clinic/_threadmodule.c.h"
-
 // ThreadError is just an alias to PyExc_RuntimeError
 #define ThreadError PyExc_RuntimeError
 
@@ -31,6 +29,7 @@ static struct PyModuleDef thread_module;
 typedef struct {
     PyTypeObject *excepthook_type;
     PyTypeObject *lock_type;
+    PyTypeObject *rlock_type;
     PyTypeObject *local_type;
     PyTypeObject *local_dummy_type;
     PyTypeObject *thread_handle_type;
@@ -48,6 +47,17 @@ get_thread_state(PyObject *module)
     return (thread_module_state *)state;
 }
 
+static inline thread_module_state*
+get_thread_state_by_cls(PyTypeObject *cls)
+{
+    // Use PyType_GetModuleByDef() to handle (R)Lock subclasses.
+    PyObject *module = PyType_GetModuleByDef(cls, &thread_module);
+    if (module == NULL) {
+        return NULL;
+    }
+    return get_thread_state(module);
+}
+
 
 #ifdef MS_WINDOWS
 typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
@@ -59,9 +69,14 @@ static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = 
NULL;
 
 /*[clinic input]
 module _thread
+class _thread.lock "lockobject *" "clinic_state()->lock_type"
+class _thread.RLock "rlockobject *" "clinic_state()->rlock_type"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=be8dbe5cc4b16df7]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c5a0f8c492a0c263]*/
 
+#define clinic_state() get_thread_state_by_cls(type)
+#include "clinic/_threadmodule.c.h"
+#undef clinic_state
 
 // _ThreadHandle type
 
@@ -916,25 +931,21 @@ lock__at_fork_reinit(PyObject *op, PyObject 
*Py_UNUSED(dummy))
 }
 #endif  /* HAVE_FORK */
 
-static lockobject *newlockobject(PyObject *module);
+/*[clinic input]
+@classmethod
+_thread.lock.__new__ as lock_new
+[clinic start generated code]*/
 
 static PyObject *
-lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+lock_new_impl(PyTypeObject *type)
+/*[clinic end generated code: output=eab660d5a4c05c8a input=260208a4e277d250]*/
 {
-    // convert to AC?
-    if (!_PyArg_NoKeywords("lock", kwargs)) {
-        goto error;
-    }
-    if (!_PyArg_CheckPositional("lock", PyTuple_GET_SIZE(args), 0, 0)) {
-        goto error;
+    lockobject *self = (lockobject *)type->tp_alloc(type, 0);
+    if (self == NULL) {
+        return NULL;
     }
-
-    PyObject *module = PyType_GetModuleByDef(type, &thread_module);
-    assert(module != NULL);
-    return (PyObject *)newlockobject(module);
-
-error:
-    return NULL;
+    self->lock = (PyMutex){0};
+    return (PyObject *)self;
 }
 
 
@@ -1186,8 +1197,14 @@ PyDoc_STRVAR(rlock_is_owned_doc,
 \n\
 For internal use by `threading.Condition`.");
 
+/*[clinic input]
+@classmethod
+_thread.RLock.__new__ as rlock_new
+[clinic start generated code]*/
+
 static PyObject *
-rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+rlock_new_impl(PyTypeObject *type)
+/*[clinic end generated code: output=bb4fb1edf6818df5 input=013591361bf1ac6e]*/
 {
     rlockobject *self = (rlockobject *) type->tp_alloc(type, 0);
     if (self == NULL) {
@@ -1267,20 +1284,6 @@ static PyType_Spec rlock_type_spec = {
     .slots = rlock_type_slots,
 };
 
-static lockobject *
-newlockobject(PyObject *module)
-{
-    thread_module_state *state = get_thread_state(module);
-
-    PyTypeObject *type = state->lock_type;
-    lockobject *self = (lockobject *)type->tp_alloc(type, 0);
-    if (self == NULL) {
-        return NULL;
-    }
-    self->lock = (PyMutex){0};
-    return self;
-}
-
 /* Thread-local objects */
 
 /* Quick overview:
@@ -2035,7 +2038,8 @@ Note: the default signal handler for SIGINT raises 
``KeyboardInterrupt``."
 static PyObject *
 thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored))
 {
-    return (PyObject *) newlockobject(module);
+    thread_module_state *state = get_thread_state(module);
+    return lock_new_impl(state->lock_type);
 }
 
 PyDoc_STRVAR(allocate_lock_doc,
@@ -2645,15 +2649,13 @@ thread_module_exec(PyObject *module)
     }
 
     // RLock
-    PyTypeObject *rlock_type = (PyTypeObject 
*)PyType_FromSpec(&rlock_type_spec);
-    if (rlock_type == NULL) {
+    state->rlock_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, 
&rlock_type_spec, NULL);
+    if (state->rlock_type == NULL) {
         return -1;
     }
-    if (PyModule_AddType(module, rlock_type) < 0) {
-        Py_DECREF(rlock_type);
+    if (PyModule_AddType(module, state->rlock_type) < 0) {
         return -1;
     }
-    Py_DECREF(rlock_type);
 
     // Local dummy
     state->local_dummy_type = (PyTypeObject 
*)PyType_FromSpec(&local_dummy_type_spec);
@@ -2740,6 +2742,7 @@ thread_module_traverse(PyObject *module, visitproc visit, 
void *arg)
     thread_module_state *state = get_thread_state(module);
     Py_VISIT(state->excepthook_type);
     Py_VISIT(state->lock_type);
+    Py_VISIT(state->rlock_type);
     Py_VISIT(state->local_type);
     Py_VISIT(state->local_dummy_type);
     Py_VISIT(state->thread_handle_type);
@@ -2752,6 +2755,7 @@ thread_module_clear(PyObject *module)
     thread_module_state *state = get_thread_state(module);
     Py_CLEAR(state->excepthook_type);
     Py_CLEAR(state->lock_type);
+    Py_CLEAR(state->rlock_type);
     Py_CLEAR(state->local_type);
     Py_CLEAR(state->local_dummy_type);
     Py_CLEAR(state->thread_handle_type);
diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h
index 8930e54170caf4..fd8e32a2261d38 100644
--- a/Modules/clinic/_threadmodule.c.h
+++ b/Modules/clinic/_threadmodule.c.h
@@ -6,7 +6,53 @@ preserve
 #  include "pycore_gc.h"          // PyGC_Head
 #  include "pycore_runtime.h"     // _Py_ID()
 #endif
-#include "pycore_modsupport.h"    // _PyArg_UnpackKeywords()
+#include "pycore_modsupport.h"    // _PyArg_NoKeywords()
+
+static PyObject *
+lock_new_impl(PyTypeObject *type);
+
+static PyObject *
+lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+    PyObject *return_value = NULL;
+    PyTypeObject *base_tp = clinic_state()->lock_type;
+
+    if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+        !_PyArg_NoPositional("lock", args)) {
+        goto exit;
+    }
+    if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+        !_PyArg_NoKeywords("lock", kwargs)) {
+        goto exit;
+    }
+    return_value = lock_new_impl(type);
+
+exit:
+    return return_value;
+}
+
+static PyObject *
+rlock_new_impl(PyTypeObject *type);
+
+static PyObject *
+rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+    PyObject *return_value = NULL;
+    PyTypeObject *base_tp = clinic_state()->rlock_type;
+
+    if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+        !_PyArg_NoPositional("RLock", args)) {
+        goto exit;
+    }
+    if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+        !_PyArg_NoKeywords("RLock", kwargs)) {
+        goto exit;
+    }
+    return_value = rlock_new_impl(type);
+
+exit:
+    return return_value;
+}
 
 #if (defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) || 
defined(MS_WINDOWS))
 
@@ -103,4 +149,4 @@ _thread_set_name(PyObject *module, PyObject *const *args, 
Py_ssize_t nargs, PyOb
 #ifndef _THREAD_SET_NAME_METHODDEF
     #define _THREAD_SET_NAME_METHODDEF
 #endif /* !defined(_THREAD_SET_NAME_METHODDEF) */
-/*[clinic end generated code: output=e978dc4615b9bc35 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b381ec5e313198e7 input=a9049054013a1b77]*/

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to