diff --git a/Lib/_dummy_thread.py b/Lib/_dummy_thread.py --- a/Lib/_dummy_thread.py +++ b/Lib/_dummy_thread.py @@ -135,16 +135,24 @@ class LockType(object): if not self.locked_status: raise error self.locked_status = False return True def locked(self): return self.locked_status + def __repr__(self): + return "<%s %s.%s object at %s>" % ( + "locked" if self.locked_status else "unlocked", + self.__class__.__module__, + self.__class__.__qualname__, + hex(id(self)) + ) + # Used to signal that interrupt_main was called in a "thread" _interrupt = False # True when not executing in a "thread" _main = True def interrupt_main(): """Set _interrupt flag to True to have start_new_thread raise KeyboardInterrupt upon exiting.""" diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -77,17 +77,23 @@ class BaseLockTests(BaseTestCase): """ def test_constructor(self): lock = self.locktype() del lock def test_repr(self): lock = self.locktype() - repr(lock) + self.assertRegex(repr(lock), "") + del lock + + def test_locked_repr(self): + lock = self.locktype() + lock.acquire() + self.assertRegex(repr(lock), "") del lock def test_acquire_destroy(self): lock = self.locktype() lock.acquire() del lock def test_acquire_release(self): diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -26,16 +26,19 @@ if threading is not None: test_try_acquire = None test_try_acquire_contended = None # `with` unsupported test_with = None # acquire(timeout=...) unsupported test_timeout = None # _release_save() unsupported test_release_save_unacquired = None + # lock status in repr unsupported + test_repr = None + test_locked_repr = None LOCK_TYPES = {kind: splitinit._bootstrap._ModuleLock for kind, splitinit in init.items()} (Frozen_ModuleLockAsRLockTests, Source_ModuleLockAsRLockTests ) = test_util.test_both(ModuleLockAsRLockTests, lock_tests.RLockTests, LockType=LOCK_TYPES) diff --git a/Lib/threading.py b/Lib/threading.py --- a/Lib/threading.py +++ b/Lib/threading.py @@ -101,18 +101,24 @@ class _RLock: self._count = 0 def __repr__(self): owner = self._owner try: owner = _active[owner].name except KeyError: pass - return "<%s owner=%r count=%d>" % ( - self.__class__.__name__, owner, self._count) + return "<%s %s.%s object owner=%r count=%d at %s>" % ( + "locked" if self._block.locked() else "unlocked", + self.__class__.__module__, + self.__class__.__qualname__, + owner, + self._count, + hex(id(self)) + ) def acquire(self, blocking=True, timeout=-1): """Acquire a lock, blocking or non-blocking. When invoked without arguments: if this thread already owns the lock, increment the recursion level by one, and return immediately. Otherwise, if another thread owns the lock, block until the lock is unlocked. Once the lock is unlocked (not owned by any thread), then grab ownership, set diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -187,16 +187,23 @@ lock_locked_lock(lockobject *self) } PyDoc_STRVAR(locked_doc, "locked() -> bool\n\ (locked_lock() is an obsolete synonym)\n\ \n\ Return whether the lock is in the locked state."); +static PyObject * +lock_repr(lockobject *self) +{ + return PyUnicode_FromFormat("<%s %s object at %p>", + self->locked ? "locked" : "unlocked", Py_TYPE(self)->tp_name, self); +} + static PyMethodDef lock_methods[] = { {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, METH_VARARGS | METH_KEYWORDS, acquire_doc}, {"acquire", (PyCFunction)lock_PyThread_acquire_lock, METH_VARARGS | METH_KEYWORDS, acquire_doc}, {"release_lock", (PyCFunction)lock_PyThread_release_lock, METH_NOARGS, release_doc}, {"release", (PyCFunction)lock_PyThread_release_lock, @@ -218,17 +225,17 @@ static PyTypeObject Locktype = { sizeof(lockobject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)lock_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_reserved*/ - 0, /*tp_repr*/ + (reprfunc)lock_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ @@ -470,18 +477,20 @@ rlock_new(PyTypeObject *type, PyObject * } return (PyObject *) self; } static PyObject * rlock_repr(rlockobject *self) { - return PyUnicode_FromFormat("<%s owner=%ld count=%lu>", - Py_TYPE(self)->tp_name, self->rlock_owner, self->rlock_count); + return PyUnicode_FromFormat("<%s %s object owner=%ld count=%lu at %p>", + self->rlock_count ? "locked" : "unlocked", + Py_TYPE(self)->tp_name, self->rlock_owner, + self->rlock_count, self); } static PyMethodDef rlock_methods[] = { {"acquire", (PyCFunction)rlock_acquire, METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, {"release", (PyCFunction)rlock_release, METH_NOARGS, rlock_release_doc},