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>
This commit is contained in:
Charlie Zhao 2022-02-26 12:17:13 +08:00 committed by GitHub
parent 5ab745fc51
commit e466faa9df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 3 deletions

View File

@ -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.

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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 {}.

View File

@ -2004,6 +2004,7 @@ Yuxiao Zeng
Uwe Zessin
Cheng Zhang
George Zhang
Charlie Zhao
Kai Zhu
Tarek Ziadé
Jelle Zijlstra