From dfa715e941f4e6dd96bfef7252cdfb45d3d4823e Mon Sep 17 00:00:00 2001 From: MattiHayes <122877654+MattiHayes@users.noreply.github.com> Date: Sat, 22 Mar 2025 16:28:16 +0000 Subject: [PATCH 1/7] Add test to check that invalid 'args' input in the threading.Thread constructor. --- Lib/test/test_threading.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 214e1ba0b53dd2..ece71861fa94c6 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -174,6 +174,21 @@ def test_args_argument(self): t.start() t.join() + def test_args_invalid_arguments_raises_typeerror(self): + def task(x): + pass + + invalid_args = ( + "test string", + 1, + b"Bytes" + ) + + for args in invalid_args: + with self.subTest(args=args): + with self.assertRaises(TypeError): + threading.Thread(target=task, args=args) + def test_lock_no_args(self): threading.Lock() # works self.assertRaises(TypeError, threading.Lock, 1) From a1fd32244cfa09401a0eb471db41b7f134acd957 Mon Sep 17 00:00:00 2001 From: MattiHayes <122877654+MattiHayes@users.noreply.github.com> Date: Sat, 22 Mar 2025 16:31:18 +0000 Subject: [PATCH 2/7] Add checks for threading.Thread 'args' parameter in the constructor. --- Lib/threading.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/threading.py b/Lib/threading.py index da9cdf0b09d83c..f1f77be2cfeabb 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -909,6 +909,10 @@ class is implemented. self._target = target self._name = name + + if isinstance(args, (str, bytes)) or not hasattr(args, '__iter__'): + raise TypeError(f"'args' must be a non-string iterable like a tuple or list, not {type(args).__name__}") + self._args = args self._kwargs = kwargs if daemon is not None: From b78b776a6798134df43a4089beab16c2a0a7b8c0 Mon Sep 17 00:00:00 2001 From: MattiHayes <122877654+MattiHayes@users.noreply.github.com> Date: Sat, 22 Mar 2025 17:10:00 +0000 Subject: [PATCH 3/7] Trim trailing whitespace --- Lib/threading.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/threading.py b/Lib/threading.py index f1f77be2cfeabb..8f6e7ae75e456a 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -909,10 +909,8 @@ class is implemented. self._target = target self._name = name - if isinstance(args, (str, bytes)) or not hasattr(args, '__iter__'): raise TypeError(f"'args' must be a non-string iterable like a tuple or list, not {type(args).__name__}") - self._args = args self._kwargs = kwargs if daemon is not None: From 6f2edc33fbb40ffe3e24677796f4c8e29ccd8713 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 17:20:32 +0000 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst diff --git a/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst b/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst new file mode 100644 index 00000000000000..3c141422c38431 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst @@ -0,0 +1 @@ +Added validation in threading.Thread to ensure that 'args' is a tuple or list. Raises TypeError if a string, bytes, or non-iterable is passed. From 650ccdebca8de21d92a9c42ba9ff255751b22858 Mon Sep 17 00:00:00 2001 From: MattiHayes <122877654+MattiHayes@users.noreply.github.com> Date: Sat, 29 Mar 2025 08:43:10 +0000 Subject: [PATCH 5/7] Change check on `args` to allow strings in `threading.Thread` --- Lib/threading.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/threading.py b/Lib/threading.py index 8f6e7ae75e456a..60ec730b4ef71e 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -909,8 +909,8 @@ class is implemented. self._target = target self._name = name - if isinstance(args, (str, bytes)) or not hasattr(args, '__iter__'): - raise TypeError(f"'args' must be a non-string iterable like a tuple or list, not {type(args).__name__}") + if not hasattr(args, '__iter__'): + raise TypeError(f"'args' must be a iterable like a tuple or list, not {type(args).__name__}") self._args = args self._kwargs = kwargs if daemon is not None: From e719ce57cee8f61b4a12c86f7f00c42961192fe3 Mon Sep 17 00:00:00 2001 From: MattiHayes <122877654+MattiHayes@users.noreply.github.com> Date: Sat, 29 Mar 2025 09:23:31 +0000 Subject: [PATCH 6/7] Adjust testing args for `threading.Thread` to allow any object with `__iter__` --- Lib/test/test_threading.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index ece71861fa94c6..9d8bb2a3061856 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -179,9 +179,10 @@ def task(x): pass invalid_args = ( - "test string", 1, - b"Bytes" + 3.14, + None, + object(), ) for args in invalid_args: @@ -189,11 +190,17 @@ def task(x): with self.assertRaises(TypeError): threading.Thread(target=task, args=args) - def test_lock_no_args(self): - threading.Lock() # works - self.assertRaises(TypeError, threading.Lock, 1) - self.assertRaises(TypeError, threading.Lock, a=1) - self.assertRaises(TypeError, threading.Lock, 1, 2, a=1, b=2) + def test_args_valid_inputs_does_not_raise(self): + def task(): + pass + + class CustomIter: + def __iter__(self): + yield + try: + t = threading.Thread(target=task, args=CustomIter()) + except TypeError as e: + self.fail(f"Thread raised an exception with an object that has the '__iter__' attribute with error {e}") def test_lock_no_subclass(self): # Intentionally disallow subclasses of threading.Lock because they have From 98c16048f1013c1d094560bdd7d28183855ea2d6 Mon Sep 17 00:00:00 2001 From: MattiHayes <122877654+MattiHayes@users.noreply.github.com> Date: Sat, 29 Mar 2025 09:30:29 +0000 Subject: [PATCH 7/7] Update news entry --- .../Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst b/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst index 3c141422c38431..65cbedd910e942 100644 --- a/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst +++ b/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst @@ -1 +1,2 @@ -Added validation in threading.Thread to ensure that 'args' is a tuple or list. Raises TypeError if a string, bytes, or non-iterable is passed. +threading.Thread now validates that 'args' is iterable. Raises TypeError if it is not. +