diff options
author | Charlie Zhao <68189100+CharlieZhao95@users.noreply.github.com> | 2022-02-26 04:17:13 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-26 04:17:13 (GMT) |
commit | e466faa9df9a1bd377d9725de5484471bc4af8d0 (patch) | |
tree | fc1e208390aab53f622971b10c8799bc4159ef1c | |
parent | 5ab745fc51e159ead28b523414e52f0bcc1ef353 (diff) | |
download | cpython-e466faa9df9a1bd377d9725de5484471bc4af8d0.zip cpython-e466faa9df9a1bd377d9725de5484471bc4af8d0.tar.gz cpython-e466faa9df9a1bd377d9725de5484471bc4af8d0.tar.bz2 |
bpo-45735: Promise the long-time truth that `args=list` works (GH-30982)
For threads, and for multiprocessing, it's always been the case that ``args=list`` works fine when passed to ``Process()`` or ``Thread()``, and such code is common in the wild. But, according to the docs, only a tuple can be used. This brings the docs into synch with reality.
Doc changes by Charlie Zhao.
Co-authored-by: Tim Peters <tim.peters@gmail.com>
-rw-r--r-- | Doc/library/multiprocessing.rst | 17 | ||||
-rw-r--r-- | Doc/library/threading.rst | 15 | ||||
-rw-r--r-- | Lib/test/_test_multiprocessing.py | 24 | ||||
-rw-r--r-- | Lib/test/test_threading.py | 26 | ||||
-rw-r--r-- | Lib/threading.py | 2 | ||||
-rw-r--r-- | Misc/ACKS | 1 |
6 files changed, 82 insertions, 3 deletions
diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index cbbe184..ee40688 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -485,7 +485,9 @@ The :mod:`multiprocessing` package mostly replicates the API of the to ``True`` or ``False``. If ``None`` (the default), this flag will be inherited from the creating process. - By default, no arguments are passed to *target*. + By default, no arguments are passed to *target*. The *args* argument, + which defaults to ``()``, can be used to specify a list or tuple of the arguments + to pass to *target*. If a subclass overrides the constructor, it must make sure it invokes the base class constructor (:meth:`Process.__init__`) before doing anything else @@ -503,6 +505,19 @@ The :mod:`multiprocessing` package mostly replicates the API of the the target argument, if any, with sequential and keyword arguments taken from the *args* and *kwargs* arguments, respectively. + Using a list or tuple as the *args* argument passed to :class:`Process` + achieves the same effect. + + Example:: + + >>> from multiprocessing import Process + >>> p = Process(target=print, args=[1]) + >>> p.run() + 1 + >>> p = Process(target=print, args=(1,)) + >>> p.run() + 1 + .. method:: start() Start the process's activity. diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 8c76643..2bcb72b 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -314,7 +314,7 @@ since it is impossible to detect the termination of alien threads. or "Thread-*N* (target)" where "target" is ``target.__name__`` if the *target* argument is specified. - *args* is the argument tuple for the target invocation. Defaults to ``()``. + *args* is a list or tuple of arguments for the target invocation. Defaults to ``()``. *kwargs* is a dictionary of keyword arguments for the target invocation. Defaults to ``{}``. @@ -353,6 +353,19 @@ since it is impossible to detect the termination of alien threads. the *target* argument, if any, with positional and keyword arguments taken from the *args* and *kwargs* arguments, respectively. + Using list or tuple as the *args* argument which passed to the :class:`Thread` + could achieve the same effect. + + Example:: + + >>> from threading import Thread + >>> t = Thread(target=print, args=[1]) + >>> t.run() + 1 + >>> t = Thread(target=print, args=(1,)) + >>> t.run() + 1 + .. method:: join(timeout=None) Wait until the thread terminates. This blocks the calling thread until diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index b2d656a..6b1b167 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -247,6 +247,30 @@ class _TestProcess(BaseTestCase): self.assertEqual(current.ident, os.getpid()) self.assertEqual(current.exitcode, None) + def test_args_argument(self): + # bpo-45735: Using list or tuple as *args* in constructor could + # achieve the same effect. + args_cases = (1, "str", [1], (1,)) + args_types = (list, tuple) + + test_cases = itertools.product(args_cases, args_types) + + for args, args_type in test_cases: + with self.subTest(args=args, args_type=args_type): + q = self.Queue(1) + # pass a tuple or list as args + p = self.Process(target=self._test_args, args=args_type((q, args))) + p.daemon = True + p.start() + child_args = q.get() + self.assertEqual(child_args, args) + p.join() + close_queue(q) + + @classmethod + def _test_args(cls, q, arg): + q.put(arg) + def test_daemon_argument(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 48305714..16c6934 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -123,6 +123,32 @@ class ThreadTests(BaseTestCase): thread = threading.Thread(target=func) self.assertEqual(thread.name, "Thread-5 (func)") + def test_args_argument(self): + # bpo-45735: Using list or tuple as *args* in constructor could + # achieve the same effect. + num_list = [1] + num_tuple = (1,) + + str_list = ["str"] + str_tuple = ("str",) + + list_in_tuple = ([1],) + tuple_in_list = [(1,)] + + test_cases = ( + (num_list, lambda arg: self.assertEqual(arg, 1)), + (num_tuple, lambda arg: self.assertEqual(arg, 1)), + (str_list, lambda arg: self.assertEqual(arg, "str")), + (str_tuple, lambda arg: self.assertEqual(arg, "str")), + (list_in_tuple, lambda arg: self.assertEqual(arg, [1])), + (tuple_in_list, lambda arg: self.assertEqual(arg, (1,))) + ) + + for args, target in test_cases: + with self.subTest(target=target, args=args): + t = threading.Thread(target=target, args=args) + t.start() + @cpython_only def test_disallow_instantiation(self): # Ensure that the type disallows instantiation (bpo-43916) diff --git a/Lib/threading.py b/Lib/threading.py index 6068d06..642f93e 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -852,7 +852,7 @@ class Thread: *name* is the thread name. By default, a unique name is constructed of the form "Thread-N" where N is a small decimal number. - *args* is the argument tuple for the target invocation. Defaults to (). + *args* is a list or tuple of arguments for the target invocation. Defaults to (). *kwargs* is a dictionary of keyword arguments for the target invocation. Defaults to {}. @@ -2004,6 +2004,7 @@ Yuxiao Zeng Uwe Zessin Cheng Zhang George Zhang +Charlie Zhao Kai Zhu Tarek Ziadé Jelle Zijlstra |