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