Skip to content

Commit d6dc33e

Browse files
authored
pythongh-134087: enforce signature of threading.RLock (python#134178)
- Reject positional and keyword arguments in `_thread.RLock.__new__`. - Convert `_thread.lock.__new__` to AC.
1 parent 9983c7d commit d6dc33e

File tree

7 files changed

+115
-53
lines changed

7 files changed

+115
-53
lines changed

Doc/whatsnew/3.15.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ sysconfig
145145
(Contributed by Filipe Laíns in :gh:`92897`.)
146146

147147

148+
threading
149+
---------
150+
151+
* Remove support for arbitrary positional or keyword arguments in the C
152+
implementation of :class:`~threading.RLock` objects. This was deprecated
153+
in Python 3.14.
154+
(Contributed by Bénédikt Tran in :gh:`134087`.)
155+
156+
148157
typing
149158
------
150159

Lib/test/lock_tests.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ def test_constructor(self):
124124
lock = self.locktype()
125125
del lock
126126

127+
def test_constructor_noargs(self):
128+
self.assertRaises(TypeError, self.locktype, 1)
129+
self.assertRaises(TypeError, self.locktype, x=1)
130+
self.assertRaises(TypeError, self.locktype, 1, x=2)
131+
127132
def test_repr(self):
128133
lock = self.locktype()
129134
self.assertRegex(repr(lock), "<unlocked .* object (.*)?at .*>")

Lib/test/test_threading.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,8 +2137,7 @@ def test_signature(self): # gh-102029
21372137
]
21382138
for args, kwargs in arg_types:
21392139
with self.subTest(args=args, kwargs=kwargs):
2140-
with self.assertWarns(DeprecationWarning):
2141-
threading.RLock(*args, **kwargs)
2140+
self.assertRaises(TypeError, threading.RLock, *args, **kwargs)
21422141

21432142
# Subtypes with custom `__init__` are allowed (but, not recommended):
21442143
class CustomRLock(self.locktype):
@@ -2156,6 +2155,9 @@ class ConditionAsRLockTests(lock_tests.RLockTests):
21562155
# Condition uses an RLock by default and exports its API.
21572156
locktype = staticmethod(threading.Condition)
21582157

2158+
def test_constructor_noargs(self):
2159+
self.skipTest("Condition allows positional arguments")
2160+
21592161
def test_recursion_count(self):
21602162
self.skipTest("Condition does not expose _recursion_count()")
21612163

Lib/threading.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def gettrace():
123123

124124
Lock = _LockType
125125

126-
def RLock(*args, **kwargs):
126+
def RLock():
127127
"""Factory function that returns a new reentrant lock.
128128
129129
A reentrant lock must be released by the thread that acquired it. Once a
@@ -132,16 +132,9 @@ def RLock(*args, **kwargs):
132132
acquired it.
133133
134134
"""
135-
if args or kwargs:
136-
import warnings
137-
warnings.warn(
138-
'Passing arguments to RLock is deprecated and will be removed in 3.15',
139-
DeprecationWarning,
140-
stacklevel=2,
141-
)
142135
if _CRLock is None:
143-
return _PyRLock(*args, **kwargs)
144-
return _CRLock(*args, **kwargs)
136+
return _PyRLock()
137+
return _CRLock()
145138

146139
class _RLock:
147140
"""This class implements reentrant lock objects.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Remove support for arbitrary positional or keyword arguments in the C
2+
implementation of :class:`threading.RLock` objects. This was deprecated
3+
since Python 3.14. Patch by Bénédikt Tran.

Modules/_threadmodule.c

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
# include <signal.h> // SIGINT
2020
#endif
2121

22-
#include "clinic/_threadmodule.c.h"
23-
2422
// ThreadError is just an alias to PyExc_RuntimeError
2523
#define ThreadError PyExc_RuntimeError
2624

@@ -31,6 +29,7 @@ static struct PyModuleDef thread_module;
3129
typedef struct {
3230
PyTypeObject *excepthook_type;
3331
PyTypeObject *lock_type;
32+
PyTypeObject *rlock_type;
3433
PyTypeObject *local_type;
3534
PyTypeObject *local_dummy_type;
3635
PyTypeObject *thread_handle_type;
@@ -48,6 +47,17 @@ get_thread_state(PyObject *module)
4847
return (thread_module_state *)state;
4948
}
5049

50+
static inline thread_module_state*
51+
get_thread_state_by_cls(PyTypeObject *cls)
52+
{
53+
// Use PyType_GetModuleByDef() to handle (R)Lock subclasses.
54+
PyObject *module = PyType_GetModuleByDef(cls, &thread_module);
55+
if (module == NULL) {
56+
return NULL;
57+
}
58+
return get_thread_state(module);
59+
}
60+
5161

5262
#ifdef MS_WINDOWS
5363
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
@@ -59,9 +69,14 @@ static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL;
5969

6070
/*[clinic input]
6171
module _thread
72+
class _thread.lock "lockobject *" "clinic_state()->lock_type"
73+
class _thread.RLock "rlockobject *" "clinic_state()->rlock_type"
6274
[clinic start generated code]*/
63-
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=be8dbe5cc4b16df7]*/
75+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c5a0f8c492a0c263]*/
6476

77+
#define clinic_state() get_thread_state_by_cls(type)
78+
#include "clinic/_threadmodule.c.h"
79+
#undef clinic_state
6580

6681
// _ThreadHandle type
6782

@@ -916,25 +931,21 @@ lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy))
916931
}
917932
#endif /* HAVE_FORK */
918933

919-
static lockobject *newlockobject(PyObject *module);
934+
/*[clinic input]
935+
@classmethod
936+
_thread.lock.__new__ as lock_new
937+
[clinic start generated code]*/
920938

921939
static PyObject *
922-
lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
940+
lock_new_impl(PyTypeObject *type)
941+
/*[clinic end generated code: output=eab660d5a4c05c8a input=260208a4e277d250]*/
923942
{
924-
// convert to AC?
925-
if (!_PyArg_NoKeywords("lock", kwargs)) {
926-
goto error;
927-
}
928-
if (!_PyArg_CheckPositional("lock", PyTuple_GET_SIZE(args), 0, 0)) {
929-
goto error;
943+
lockobject *self = (lockobject *)type->tp_alloc(type, 0);
944+
if (self == NULL) {
945+
return NULL;
930946
}
931-
932-
PyObject *module = PyType_GetModuleByDef(type, &thread_module);
933-
assert(module != NULL);
934-
return (PyObject *)newlockobject(module);
935-
936-
error:
937-
return NULL;
947+
self->lock = (PyMutex){0};
948+
return (PyObject *)self;
938949
}
939950

940951

@@ -1186,8 +1197,14 @@ PyDoc_STRVAR(rlock_is_owned_doc,
11861197
\n\
11871198
For internal use by `threading.Condition`.");
11881199

1200+
/*[clinic input]
1201+
@classmethod
1202+
_thread.RLock.__new__ as rlock_new
1203+
[clinic start generated code]*/
1204+
11891205
static PyObject *
1190-
rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1206+
rlock_new_impl(PyTypeObject *type)
1207+
/*[clinic end generated code: output=bb4fb1edf6818df5 input=013591361bf1ac6e]*/
11911208
{
11921209
rlockobject *self = (rlockobject *) type->tp_alloc(type, 0);
11931210
if (self == NULL) {
@@ -1267,20 +1284,6 @@ static PyType_Spec rlock_type_spec = {
12671284
.slots = rlock_type_slots,
12681285
};
12691286

1270-
static lockobject *
1271-
newlockobject(PyObject *module)
1272-
{
1273-
thread_module_state *state = get_thread_state(module);
1274-
1275-
PyTypeObject *type = state->lock_type;
1276-
lockobject *self = (lockobject *)type->tp_alloc(type, 0);
1277-
if (self == NULL) {
1278-
return NULL;
1279-
}
1280-
self->lock = (PyMutex){0};
1281-
return self;
1282-
}
1283-
12841287
/* Thread-local objects */
12851288

12861289
/* Quick overview:
@@ -2035,7 +2038,8 @@ Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``."
20352038
static PyObject *
20362039
thread_PyThread_allocate_lock(PyObject *module, PyObject *Py_UNUSED(ignored))
20372040
{
2038-
return (PyObject *) newlockobject(module);
2041+
thread_module_state *state = get_thread_state(module);
2042+
return lock_new_impl(state->lock_type);
20392043
}
20402044

20412045
PyDoc_STRVAR(allocate_lock_doc,
@@ -2645,15 +2649,13 @@ thread_module_exec(PyObject *module)
26452649
}
26462650

26472651
// RLock
2648-
PyTypeObject *rlock_type = (PyTypeObject *)PyType_FromSpec(&rlock_type_spec);
2649-
if (rlock_type == NULL) {
2652+
state->rlock_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, &rlock_type_spec, NULL);
2653+
if (state->rlock_type == NULL) {
26502654
return -1;
26512655
}
2652-
if (PyModule_AddType(module, rlock_type) < 0) {
2653-
Py_DECREF(rlock_type);
2656+
if (PyModule_AddType(module, state->rlock_type) < 0) {
26542657
return -1;
26552658
}
2656-
Py_DECREF(rlock_type);
26572659

26582660
// Local dummy
26592661
state->local_dummy_type = (PyTypeObject *)PyType_FromSpec(&local_dummy_type_spec);
@@ -2740,6 +2742,7 @@ thread_module_traverse(PyObject *module, visitproc visit, void *arg)
27402742
thread_module_state *state = get_thread_state(module);
27412743
Py_VISIT(state->excepthook_type);
27422744
Py_VISIT(state->lock_type);
2745+
Py_VISIT(state->rlock_type);
27432746
Py_VISIT(state->local_type);
27442747
Py_VISIT(state->local_dummy_type);
27452748
Py_VISIT(state->thread_handle_type);
@@ -2752,6 +2755,7 @@ thread_module_clear(PyObject *module)
27522755
thread_module_state *state = get_thread_state(module);
27532756
Py_CLEAR(state->excepthook_type);
27542757
Py_CLEAR(state->lock_type);
2758+
Py_CLEAR(state->rlock_type);
27552759
Py_CLEAR(state->local_type);
27562760
Py_CLEAR(state->local_dummy_type);
27572761
Py_CLEAR(state->thread_handle_type);

Modules/clinic/_threadmodule.c.h

Lines changed: 48 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)