From 45294938fbaea1de8049934962f334fd6ab5815a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 20 Feb 2023 15:42:33 +0300 Subject: [PATCH 1/8] gh-102029: Sync `__new__` sigs of `_CRLock` and `_PyRLock` in `threading` --- Doc/whatsnew/3.12.rst | 6 +++++ Lib/test/lock_tests.py | 27 ++++++++++++++++++- Lib/test/test_threading.py | 2 +- Lib/threading.py | 6 ++--- ...-02-20-15-41-59.gh-issue-102029.9ZPG99.rst | 2 ++ Modules/_threadmodule.c | 16 +++++++++++ 6 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index c62f462a19a2df..17338c5518edd7 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -332,6 +332,12 @@ threading profiling functions in all running threads in addition to the calling one. (Contributed by Pablo Galindo in :gh:`93503`.) +* Passing any arguments to :func:`threading.RLock` is now restricted. + Previously C version allowed any numbers of args and kwargs, + but they were just ignored. Python version never allowed any arguments. + Now, these two implementation match. + (Contributed by Nikita Sobolev in :gh:`102029`.) + unicodedata ----------- diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index a4f52cb20ad301..bfcb3b6b9840f6 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -293,7 +293,7 @@ def use_lock(lock): use_lock(lock2) -class RLockTests(BaseLockTests): +class RLockAPITests(BaseLockTests): """ Tests for recursive locks. """ @@ -360,6 +360,31 @@ def f(): self.assertFalse(lock._is_owned()) +class RLockTests(RLockAPITests): + def test_signature(self): # gh-102029 + # 0 args are fine: + self.locktype() + self.locktype(*(), **{}) + + # no other args are allowed: + arg_types = [ + ((1,), {}), + ((), {'a': 1}), + ((1, 2), {'a': 1}), + ] + for args, kwargs in arg_types: + with self.subTest(args=args, kwargs=kwargs): + with self.assertRaises(TypeError): + self.locktype(*args, **kwargs) + + # Subtypes with custom `__init__` are allowed (but, not recommended): + class CustomRLock(self.locktype): + def __init__(self, a, *, b) -> None: + super().__init__() + + CustomRLock(1, b=2) + + class EventTests(BaseTestCase): """ Tests for Event objects. diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 7fea2d38673eff..72c554e0f347cf 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1722,7 +1722,7 @@ class CRLockTests(lock_tests.RLockTests): class EventTests(lock_tests.EventTests): eventtype = staticmethod(threading.Event) -class ConditionAsRLockTests(lock_tests.RLockTests): +class ConditionAsRLockTests(lock_tests.RLockAPITests): # Condition uses an RLock by default and exports its API. locktype = staticmethod(threading.Condition) diff --git a/Lib/threading.py b/Lib/threading.py index df273870fa4273..496e84b7edb437 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -107,7 +107,7 @@ def gettrace(): Lock = _allocate_lock -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 @@ -117,8 +117,8 @@ def RLock(*args, **kwargs): """ 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/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst b/Misc/NEWS.d/next/Library/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst new file mode 100644 index 00000000000000..a3258747f6a2bd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst @@ -0,0 +1,2 @@ +Sync ``__new__`` signatures of ``threading._CRLock`` and +``threading._PyRLock``. Now they both require 0 arguments. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 9c12c696757439..9f1265c7a24117 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -505,6 +505,22 @@ For internal use by `threading.Condition`."); static PyObject * rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + // Can later be converted to AC: + if (type->tp_init == PyBaseObject_Type.tp_init) { + int rc = 0; + if (args != NULL) + rc = PyObject_IsTrue(args); + if (rc == 0 && kwds != NULL) + rc = PyObject_IsTrue(kwds); + if (rc != 0) { + if (rc > 0) { + PyErr_SetString(PyExc_TypeError, + "Initialization arguments are not supported"); + } + return NULL; + } + } + rlockobject *self = (rlockobject *) type->tp_alloc(type, 0); if (self == NULL) { return NULL; From 9fa3bd3aa50b369fc9c1c6b74a3a0092e810d800 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 20 Feb 2023 16:14:37 +0300 Subject: [PATCH 2/8] Fix tests --- Lib/test/lock_tests.py | 4 ++-- Lib/test/test_threading.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index bfcb3b6b9840f6..e82e3a0951a418 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -293,7 +293,7 @@ def use_lock(lock): use_lock(lock2) -class RLockAPITests(BaseLockTests): +class RLockTests(BaseLockTests): """ Tests for recursive locks. """ @@ -360,7 +360,7 @@ def f(): self.assertFalse(lock._is_owned()) -class RLockTests(RLockAPITests): +class RLockTypeTests(RLockTests): def test_signature(self): # gh-102029 # 0 args are fine: self.locktype() diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 72c554e0f347cf..9c6a943105d436 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1712,17 +1712,17 @@ def _callback_spy(self, *args, **kwargs): class LockTests(lock_tests.LockTests): locktype = staticmethod(threading.Lock) -class PyRLockTests(lock_tests.RLockTests): +class PyRLockTests(lock_tests.RLockTypeTests): locktype = staticmethod(threading._PyRLock) @unittest.skipIf(threading._CRLock is None, 'RLock not implemented in C') -class CRLockTests(lock_tests.RLockTests): +class CRLockTests(lock_tests.RLockTypeTests): locktype = staticmethod(threading._CRLock) class EventTests(lock_tests.EventTests): eventtype = staticmethod(threading.Event) -class ConditionAsRLockTests(lock_tests.RLockAPITests): +class ConditionAsRLockTests(lock_tests.RLockTests): # Condition uses an RLock by default and exports its API. locktype = staticmethod(threading.Condition) From fa6c0e0ed0f4d4d092c9df2a93b9176b5ff01f3e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 1 Jun 2023 10:20:51 +0300 Subject: [PATCH 3/8] Pre-merge --- Doc/whatsnew/3.13.rst | 13 +++++-------- Lib/test/lock_tests.py | 2 +- Lib/threading.py | 13 ++++++++++--- ...023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst | 3 +-- Modules/_threadmodule.c | 16 ---------------- 5 files changed, 17 insertions(+), 30 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 78e61e33ee8a63..bb93ca95d3e466 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -97,14 +97,6 @@ pathlib :meth:`~pathlib.Path.rglob`. (Contributed by Barney Gale in :gh:`77609`.) -threading ---------- - -* Passing any arguments to :func:`threading.RLock` is now restricted. - Previously C version allowed any numbers of args and kwargs, - but they were just ignored. Python version never allowed any arguments. - Now, these two implementation match. - (Contributed by Nikita Sobolev in :gh:`102029`.) Optimizations ============= @@ -115,6 +107,11 @@ Optimizations Deprecated ========== +* Passing any arguments to :func:`threading.RLock` is now deprecated. + C version allows any numbers of args and kwargs, + but they are just ignored. Python version does not allow any arguments. + All arguments will be removed from :func:`threading.RLock` in Python 3.13. + (Contributed by Nikita Sobolev in :gh:`102029`.) Removed diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index e82e3a0951a418..62c7fce38a8b85 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -374,7 +374,7 @@ def test_signature(self): # gh-102029 ] for args, kwargs in arg_types: with self.subTest(args=args, kwargs=kwargs): - with self.assertRaises(TypeError): + with self.assertWarns(DeprecationWarning): self.locktype(*args, **kwargs) # Subtypes with custom `__init__` are allowed (but, not recommended): diff --git a/Lib/threading.py b/Lib/threading.py index 496e84b7edb437..bfbc210267464b 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -4,6 +4,7 @@ import sys as _sys import _thread import functools +import warnings from time import monotonic as _time from _weakrefset import WeakSet @@ -107,7 +108,7 @@ def gettrace(): Lock = _allocate_lock -def RLock(): +def RLock(*args, **kwargs): """Factory function that returns a new reentrant lock. A reentrant lock must be released by the thread that acquired it. Once a @@ -116,9 +117,15 @@ def RLock(): acquired it. """ + if args or kwargs: + warnings.warn( + 'Passing arguments to RLock is deprecated and will be removed in 3.13', + DeprecationWarning, + stacklevel=2, + ) if _CRLock is None: - return _PyRLock() - return _CRLock() + return _PyRLock(*args, **kwargs) + return _CRLock(*args, **kwargs) class _RLock: """This class implements reentrant lock objects. diff --git a/Misc/NEWS.d/next/Library/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst b/Misc/NEWS.d/next/Library/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst index a3258747f6a2bd..4d6f05f2524528 100644 --- a/Misc/NEWS.d/next/Library/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst +++ b/Misc/NEWS.d/next/Library/2023-02-20-15-41-59.gh-issue-102029.9ZPG99.rst @@ -1,2 +1 @@ -Sync ``__new__`` signatures of ``threading._CRLock`` and -``threading._PyRLock``. Now they both require 0 arguments. +Deprecate passing any arguments to :func:`threading.RLock`. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index af2115b93523ca..5d753b4a0ebc5e 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -505,22 +505,6 @@ For internal use by `threading.Condition`."); static PyObject * rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - // Can later be converted to AC: - if (type->tp_init == PyBaseObject_Type.tp_init) { - int rc = 0; - if (args != NULL) - rc = PyObject_IsTrue(args); - if (rc == 0 && kwds != NULL) - rc = PyObject_IsTrue(kwds); - if (rc != 0) { - if (rc > 0) { - PyErr_SetString(PyExc_TypeError, - "Initialization arguments are not supported"); - } - return NULL; - } - } - rlockobject *self = (rlockobject *) type->tp_alloc(type, 0); if (self == NULL) { return NULL; From 70904e8918f11f5fe60d15b478cd724a623d545b Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 1 Jun 2023 10:34:05 +0300 Subject: [PATCH 4/8] Change to DeprecationWarning --- Lib/test/lock_tests.py | 25 ------------------------- Lib/test/test_threading.py | 26 ++++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index 62c7fce38a8b85..a4f52cb20ad301 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -360,31 +360,6 @@ def f(): self.assertFalse(lock._is_owned()) -class RLockTypeTests(RLockTests): - def test_signature(self): # gh-102029 - # 0 args are fine: - self.locktype() - self.locktype(*(), **{}) - - # no other args are allowed: - arg_types = [ - ((1,), {}), - ((), {'a': 1}), - ((1, 2), {'a': 1}), - ] - for args, kwargs in arg_types: - with self.subTest(args=args, kwargs=kwargs): - with self.assertWarns(DeprecationWarning): - self.locktype(*args, **kwargs) - - # Subtypes with custom `__init__` are allowed (but, not recommended): - class CustomRLock(self.locktype): - def __init__(self, a, *, b) -> None: - super().__init__() - - CustomRLock(1, b=2) - - class EventTests(BaseTestCase): """ Tests for Event objects. diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index d085ffc9e3ac82..7e0e26d695ebbd 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1745,13 +1745,35 @@ def _callback_spy(self, *args, **kwargs): class LockTests(lock_tests.LockTests): locktype = staticmethod(threading.Lock) -class PyRLockTests(lock_tests.RLockTypeTests): +class PyRLockTests(lock_tests.RLockTests): locktype = staticmethod(threading._PyRLock) @unittest.skipIf(threading._CRLock is None, 'RLock not implemented in C') -class CRLockTests(lock_tests.RLockTypeTests): +class CRLockTests(lock_tests.RLockTests): locktype = staticmethod(threading._CRLock) + def test_signature(self): # gh-102029 + with warnings.catch_warnings(record=True) as warnings_log: + threading.RLock() + self.assertEqual(warnings_log, []) + + arg_types = [ + ((1,), {}), + ((), {'a': 1}), + ((1, 2), {'a': 1}), + ] + for args, kwargs in arg_types: + with self.subTest(args=args, kwargs=kwargs): + with self.assertWarns(DeprecationWarning): + threading.RLock(*args, **kwargs) + + # Subtypes with custom `__init__` are allowed (but, not recommended): + class CustomRLock(self.locktype): + def __init__(self, a, *, b) -> None: + super().__init__() + + CustomRLock(1, b=2) + class EventTests(lock_tests.EventTests): eventtype = staticmethod(threading.Event) From 1f744dfb115814a71af4b927f06a214f5c886039 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 1 Jun 2023 10:35:34 +0300 Subject: [PATCH 5/8] Update Doc/whatsnew/3.13.rst --- Doc/whatsnew/3.13.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 3f5476169d2e9b..184aaeb115f26e 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -106,7 +106,6 @@ pathlib :meth:`~pathlib.Path.rglob`. (Contributed by Barney Gale in :gh:`77609`.) - Optimizations ============= From 849c2e3492f8784f39cc0c6639999dd7b75cb5d5 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 11 Aug 2023 11:34:53 +0300 Subject: [PATCH 6/8] Fixup --- Doc/whatsnew/3.13.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 571c11bee06a7f..3e4048e5dd1c19 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -522,7 +522,6 @@ although there is currently no date scheduled for their removal. * :meth:`zipimport.zipimporter.load_module` is deprecated: use :meth:`~zipimport.zipimporter.exec_module` instead. ->>>>>>> main Removed From acb531c0284e5550dad34f1b36fedd2d820a9c85 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 11 Aug 2023 11:35:53 +0300 Subject: [PATCH 7/8] Improve test --- Lib/test/test_threading.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 7dcdd1b270874a..6465a446565844 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1768,7 +1768,9 @@ class CustomRLock(self.locktype): def __init__(self, a, *, b) -> None: super().__init__() - CustomRLock(1, b=2) + with warnings.catch_warnings(record=True) as warnings_log: + CustomRLock(1, b=2) + self.assertEqual(warnings_log, []) class EventTests(lock_tests.EventTests): eventtype = staticmethod(threading.Event) From f0c7244299517762b44814eb9dc3ab2520e22279 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 11 Aug 2023 11:37:39 +0300 Subject: [PATCH 8/8] Update message --- Lib/threading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/threading.py b/Lib/threading.py index 5cf290b7a7a391..f6bbdb0d0c80ee 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -119,7 +119,7 @@ def RLock(*args, **kwargs): """ if args or kwargs: warnings.warn( - 'Passing arguments to RLock is deprecated and will be removed in 3.13', + 'Passing arguments to RLock is deprecated and will be removed in 3.15', DeprecationWarning, stacklevel=2, )