mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
gh-134087: enforce signature of threading.RLock
(#134178)
- Reject positional and keyword arguments in `_thread.RLock.__new__`. - Convert `_thread.lock.__new__` to AC.
This commit is contained in:
parent
9983c7d441
commit
d6dc33ed80
7 changed files with 115 additions and 53 deletions
|
@ -145,6 +145,15 @@ sysconfig
|
||||||
(Contributed by Filipe Laíns in :gh:`92897`.)
|
(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
|
typing
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,11 @@ class BaseLockTests(BaseTestCase):
|
||||||
lock = self.locktype()
|
lock = self.locktype()
|
||||||
del lock
|
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):
|
def test_repr(self):
|
||||||
lock = self.locktype()
|
lock = self.locktype()
|
||||||
self.assertRegex(repr(lock), "<unlocked .* object (.*)?at .*>")
|
self.assertRegex(repr(lock), "<unlocked .* object (.*)?at .*>")
|
||||||
|
|
|
@ -2137,8 +2137,7 @@ class CRLockTests(lock_tests.RLockTests):
|
||||||
]
|
]
|
||||||
for args, kwargs in arg_types:
|
for args, kwargs in arg_types:
|
||||||
with self.subTest(args=args, kwargs=kwargs):
|
with self.subTest(args=args, kwargs=kwargs):
|
||||||
with self.assertWarns(DeprecationWarning):
|
self.assertRaises(TypeError, threading.RLock, *args, **kwargs)
|
||||||
threading.RLock(*args, **kwargs)
|
|
||||||
|
|
||||||
# Subtypes with custom `__init__` are allowed (but, not recommended):
|
# Subtypes with custom `__init__` are allowed (but, not recommended):
|
||||||
class CustomRLock(self.locktype):
|
class CustomRLock(self.locktype):
|
||||||
|
@ -2156,6 +2155,9 @@ class ConditionAsRLockTests(lock_tests.RLockTests):
|
||||||
# Condition uses an RLock by default and exports its API.
|
# Condition uses an RLock by default and exports its API.
|
||||||
locktype = staticmethod(threading.Condition)
|
locktype = staticmethod(threading.Condition)
|
||||||
|
|
||||||
|
def test_constructor_noargs(self):
|
||||||
|
self.skipTest("Condition allows positional arguments")
|
||||||
|
|
||||||
def test_recursion_count(self):
|
def test_recursion_count(self):
|
||||||
self.skipTest("Condition does not expose _recursion_count()")
|
self.skipTest("Condition does not expose _recursion_count()")
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,7 @@ def gettrace():
|
||||||
|
|
||||||
Lock = _LockType
|
Lock = _LockType
|
||||||
|
|
||||||
def RLock(*args, **kwargs):
|
def RLock():
|
||||||
"""Factory function that returns a new reentrant lock.
|
"""Factory function that returns a new reentrant lock.
|
||||||
|
|
||||||
A reentrant lock must be released by the thread that acquired it. Once a
|
A reentrant lock must be released by the thread that acquired it. Once a
|
||||||
|
@ -132,16 +132,9 @@ def RLock(*args, **kwargs):
|
||||||
acquired it.
|
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:
|
if _CRLock is None:
|
||||||
return _PyRLock(*args, **kwargs)
|
return _PyRLock()
|
||||||
return _CRLock(*args, **kwargs)
|
return _CRLock()
|
||||||
|
|
||||||
class _RLock:
|
class _RLock:
|
||||||
"""This class implements reentrant lock objects.
|
"""This class implements reentrant lock objects.
|
||||||
|
|
|
@ -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.
|
|
@ -19,8 +19,6 @@
|
||||||
# include <signal.h> // SIGINT
|
# include <signal.h> // SIGINT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "clinic/_threadmodule.c.h"
|
|
||||||
|
|
||||||
// ThreadError is just an alias to PyExc_RuntimeError
|
// ThreadError is just an alias to PyExc_RuntimeError
|
||||||
#define ThreadError PyExc_RuntimeError
|
#define ThreadError PyExc_RuntimeError
|
||||||
|
|
||||||
|
@ -31,6 +29,7 @@ static struct PyModuleDef thread_module;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyTypeObject *excepthook_type;
|
PyTypeObject *excepthook_type;
|
||||||
PyTypeObject *lock_type;
|
PyTypeObject *lock_type;
|
||||||
|
PyTypeObject *rlock_type;
|
||||||
PyTypeObject *local_type;
|
PyTypeObject *local_type;
|
||||||
PyTypeObject *local_dummy_type;
|
PyTypeObject *local_dummy_type;
|
||||||
PyTypeObject *thread_handle_type;
|
PyTypeObject *thread_handle_type;
|
||||||
|
@ -48,6 +47,17 @@ get_thread_state(PyObject *module)
|
||||||
return (thread_module_state *)state;
|
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
|
#ifdef MS_WINDOWS
|
||||||
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
|
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
|
||||||
|
@ -59,9 +69,14 @@ static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL;
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
module _thread
|
module _thread
|
||||||
|
class _thread.lock "lockobject *" "clinic_state()->lock_type"
|
||||||
|
class _thread.RLock "rlockobject *" "clinic_state()->rlock_type"
|
||||||
[clinic start generated code]*/
|
[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
|
// _ThreadHandle type
|
||||||
|
|
||||||
|
@ -916,25 +931,21 @@ lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy))
|
||||||
}
|
}
|
||||||
#endif /* HAVE_FORK */
|
#endif /* HAVE_FORK */
|
||||||
|
|
||||||
static lockobject *newlockobject(PyObject *module);
|
/*[clinic input]
|
||||||
|
@classmethod
|
||||||
|
_thread.lock.__new__ as lock_new
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
static PyObject *
|
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?
|
lockobject *self = (lockobject *)type->tp_alloc(type, 0);
|
||||||
if (!_PyArg_NoKeywords("lock", kwargs)) {
|
if (self == NULL) {
|
||||||
goto error;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!_PyArg_CheckPositional("lock", PyTuple_GET_SIZE(args), 0, 0)) {
|
self->lock = (PyMutex){0};
|
||||||
goto error;
|
return (PyObject *)self;
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *module = PyType_GetModuleByDef(type, &thread_module);
|
|
||||||
assert(module != NULL);
|
|
||||||
return (PyObject *)newlockobject(module);
|
|
||||||
|
|
||||||
error:
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1186,8 +1197,14 @@ PyDoc_STRVAR(rlock_is_owned_doc,
|
||||||
\n\
|
\n\
|
||||||
For internal use by `threading.Condition`.");
|
For internal use by `threading.Condition`.");
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
@classmethod
|
||||||
|
_thread.RLock.__new__ as rlock_new
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
static PyObject *
|
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);
|
rlockobject *self = (rlockobject *) type->tp_alloc(type, 0);
|
||||||
if (self == NULL) {
|
if (self == NULL) {
|
||||||
|
@ -1267,20 +1284,6 @@ static PyType_Spec rlock_type_spec = {
|
||||||
.slots = rlock_type_slots,
|
.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 */
|
/* Thread-local objects */
|
||||||
|
|
||||||
/* Quick overview:
|
/* Quick overview:
|
||||||
|
@ -2035,7 +2038,8 @@ Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``."
|
||||||
static PyObject *
|
static PyObject *
|
||||||
thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored))
|
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,
|
PyDoc_STRVAR(allocate_lock_doc,
|
||||||
|
@ -2645,15 +2649,13 @@ thread_module_exec(PyObject *module)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RLock
|
// RLock
|
||||||
PyTypeObject *rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec);
|
state->rlock_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &rlock_type_spec, NULL);
|
||||||
if (rlock_type == NULL) {
|
if (state->rlock_type == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (PyModule_AddType(module, rlock_type) < 0) {
|
if (PyModule_AddType(module, state->rlock_type) < 0) {
|
||||||
Py_DECREF(rlock_type);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_DECREF(rlock_type);
|
|
||||||
|
|
||||||
// Local dummy
|
// Local dummy
|
||||||
state->local_dummy_type = (PyTypeObject *)PyType_FromSpec(&local_dummy_type_spec);
|
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);
|
thread_module_state *state = get_thread_state(module);
|
||||||
Py_VISIT(state->excepthook_type);
|
Py_VISIT(state->excepthook_type);
|
||||||
Py_VISIT(state->lock_type);
|
Py_VISIT(state->lock_type);
|
||||||
|
Py_VISIT(state->rlock_type);
|
||||||
Py_VISIT(state->local_type);
|
Py_VISIT(state->local_type);
|
||||||
Py_VISIT(state->local_dummy_type);
|
Py_VISIT(state->local_dummy_type);
|
||||||
Py_VISIT(state->thread_handle_type);
|
Py_VISIT(state->thread_handle_type);
|
||||||
|
@ -2752,6 +2755,7 @@ thread_module_clear(PyObject *module)
|
||||||
thread_module_state *state = get_thread_state(module);
|
thread_module_state *state = get_thread_state(module);
|
||||||
Py_CLEAR(state->excepthook_type);
|
Py_CLEAR(state->excepthook_type);
|
||||||
Py_CLEAR(state->lock_type);
|
Py_CLEAR(state->lock_type);
|
||||||
|
Py_CLEAR(state->rlock_type);
|
||||||
Py_CLEAR(state->local_type);
|
Py_CLEAR(state->local_type);
|
||||||
Py_CLEAR(state->local_dummy_type);
|
Py_CLEAR(state->local_dummy_type);
|
||||||
Py_CLEAR(state->thread_handle_type);
|
Py_CLEAR(state->thread_handle_type);
|
||||||
|
|
50
Modules/clinic/_threadmodule.c.h
generated
50
Modules/clinic/_threadmodule.c.h
generated
|
@ -6,7 +6,53 @@ preserve
|
||||||
# include "pycore_gc.h" // PyGC_Head
|
# include "pycore_gc.h" // PyGC_Head
|
||||||
# include "pycore_runtime.h" // _Py_ID()
|
# include "pycore_runtime.h" // _Py_ID()
|
||||||
#endif
|
#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))
|
#if (defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) || defined(MS_WINDOWS))
|
||||||
|
|
||||||
|
@ -103,4 +149,4 @@ exit:
|
||||||
#ifndef _THREAD_SET_NAME_METHODDEF
|
#ifndef _THREAD_SET_NAME_METHODDEF
|
||||||
#define _THREAD_SET_NAME_METHODDEF
|
#define _THREAD_SET_NAME_METHODDEF
|
||||||
#endif /* !defined(_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]*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue