summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCharlie Zhao <68189100+CharlieZhao95@users.noreply.github.com>2022-02-26 04:17:13 (GMT)
committerGitHub <noreply@github.com>2022-02-26 04:17:13 (GMT)
commite466faa9df9a1bd377d9725de5484471bc4af8d0 (patch)
treefc1e208390aab53f622971b10c8799bc4159ef1c
parent5ab745fc51e159ead28b523414e52f0bcc1ef353 (diff)
downloadcpython-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.rst17
-rw-r--r--Doc/library/threading.rst15
-rw-r--r--Lib/test/_test_multiprocessing.py24
-rw-r--r--Lib/test/test_threading.py26
-rw-r--r--Lib/threading.py2
-rw-r--r--Misc/ACKS1
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 {}.
diff --git a/Misc/ACKS b/Misc/ACKS
index bab04b4..da2c826 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -2004,6 +2004,7 @@ Yuxiao Zeng
Uwe Zessin
Cheng Zhang
George Zhang
+Charlie Zhao
Kai Zhu
Tarek Ziadé
Jelle Zijlstra