diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 214e1ba0b53dd2..9d8bb2a3061856 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -174,11 +174,33 @@ def test_args_argument(self): t.start() t.join() - 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_invalid_arguments_raises_typeerror(self): + def task(x): + pass + + invalid_args = ( + 1, + 3.14, + None, + object(), + ) + + for args in invalid_args: + with self.subTest(args=args): + with self.assertRaises(TypeError): + threading.Thread(target=task, args=args) + + 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 diff --git a/Lib/threading.py b/Lib/threading.py index da9cdf0b09d83c..60ec730b4ef71e 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -909,6 +909,8 @@ class is implemented. self._target = target self._name = 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: 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..65cbedd910e942 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-22-17-20-31.gh-issue-131540.1mRP-a.rst @@ -0,0 +1,2 @@ +threading.Thread now validates that 'args' is iterable. Raises TypeError if it is not. +