Skip to content

Commit 4b1c4f6

Browse files
committed
gh-114315: Make threading.Lock a real class
1 parent 3620fa4 commit 4b1c4f6

File tree

5 files changed

+30
-9
lines changed

5 files changed

+30
-9
lines changed

Doc/library/threading.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,9 +534,11 @@ All methods are executed atomically.
534534
lock, subsequent attempts to acquire it block, until it is released; any
535535
thread may release it.
536536

537-
Note that ``Lock`` is actually a factory function which returns an instance
538-
of the most efficient version of the concrete Lock class that is supported
539-
by the platform.
537+
.. versionchanged:: 3.13
538+
Prior to 3.13 ``Lock`` actually used to be a factory
539+
function which returned an instance
540+
of the most efficient version of the concrete Lock class that is supported
541+
by the platform.
540542

541543

542544
.. method:: acquire(blocking=True, timeout=-1)

Lib/test/test_threading.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,15 @@ def test_args_argument(self):
170170
t.start()
171171
t.join()
172172

173-
@cpython_only
174-
def test_disallow_instantiation(self):
175-
# Ensure that the type disallows instantiation (bpo-43916)
176-
lock = threading.Lock()
177-
test.support.check_disallow_instantiation(self, type(lock))
173+
def test_lock_no_args(self):
174+
threading.Lock() # works
175+
self.assertRaises(TypeError, threading.Lock, 1)
176+
self.assertRaises(TypeError, threading.Lock, a=1)
177+
self.assertRaises(TypeError, threading.Lock, 1, 2, a=1, b=2)
178+
179+
def test_lock_or_none(self):
180+
import types
181+
self.assertIsInstance(threading.Lock | None, types.UnionType)
178182

179183
# Create a bunch of threads, let each do some work, wait until all are
180184
# done.

Lib/threading.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
_start_joinable_thread = _thread.start_joinable_thread
3737
_daemon_threads_allowed = _thread.daemon_threads_allowed
3838
_allocate_lock = _thread.allocate_lock
39+
_LockType = _thread.LockType
3940
_set_sentinel = _thread._set_sentinel
4041
get_ident = _thread.get_ident
4142
_is_main_interpreter = _thread._is_main_interpreter
@@ -107,7 +108,7 @@ def gettrace():
107108

108109
# Synchronization classes
109110

110-
Lock = type(_allocate_lock())
111+
Lock = _LockType
111112

112113
def RLock(*args, **kwargs):
113114
"""Factory function that returns a new reentrant lock.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make :class:`threading.Lock` a real class, not a factory function. Add
2+
``__new__`` to ``_thread.lock`` type.

Modules/_threadmodule.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "Python.h"
66
#include "pycore_interp.h" // _PyInterpreterState.threads.count
77
#include "pycore_moduleobject.h" // _PyModule_GetState()
8+
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
89
#include "pycore_pylifecycle.h"
910
#include "pycore_pystate.h" // _PyThreadState_SetCurrent()
1011
#include "pycore_sysmodule.h" // _PySys_GetAttr()
@@ -354,11 +355,22 @@ static lockobject *newlockobject(PyObject *module);
354355
static PyObject *
355356
lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
356357
{
358+
// convert to AC?
359+
if (!_PyArg_NoKeywords("lock", kwargs)) {
360+
goto error;
361+
}
362+
if (!_PyArg_CheckPositional("lock", PyTuple_GET_SIZE(args), 0, 0)) {
363+
goto error;
364+
}
365+
357366
PyObject *module = PyType_GetModuleByDef(type, &thread_module);
358367
if (module == NULL) {
359368
return NULL;
360369
}
361370
return (PyObject *)newlockobject(module);
371+
372+
error:
373+
return NULL;
362374
}
363375

364376

0 commit comments

Comments
 (0)