[issue45735] Promise that the long-time truth that `args=list` works

New submission from Tim Peters <tim@python.org>: A number of contexts allow specifying a tuple of arguments to be passed later to a function. The Thread constructor is a fine example, and happened to come up (again! for me) here today: https://stackoverflow.com/questions/69858950/why-do-we-have-to-add-comma-in-... This often confuses especially newbies, because the function they intend to parallelize often takes only a single argument, and Python's syntax for a 1-element tuple actually _requires_ parentheses in the context of an argument list, with a naked trailing comma: t = threading.Thread(target=access, args=(thread_number,)) It "looks weird" to people. I'm not suggesting to change that, but instead to officially bless the workaround I've seen very often in real code: use a list instead. t = threading.Thread(target=access, args=[thread_number]) Nobody scratches their head over what that means. CPython's implementations typically couldn't care less what kind of sequence is used, and none that I'm aware of verify that it's specifically a tuple. The implementations just go on to do some simple variation of self.target(*self.args) Tuple or list makes no real difference. I'm not really keen to immortalize the "any sequence type whatsoever that just happens to work" implementation behavior, but am keen to promise that a list specifically will work. A lot of code already relies on it. ---------- assignee: docs@python components: Documentation keywords: easy messages: 405846 nosy: docs@python, tim.peters priority: low severity: normal stage: needs patch status: open title: Promise that the long-time truth that `args=list` works type: behavior versions: Python 3.11 _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Change by Tim Peters <tim@python.org>: ---------- title: Promise that the long-time truth that `args=list` works -> Promise the long-time truth that `args=list` works _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Raymond Hettinger <raymond.hettinger@gmail.com> added the comment: I relied on this for many years. So, yet it would be nice to guarantee it :-) ---------- nosy: +rhettinger _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Change by Raymond Hettinger <raymond.hettinger@gmail.com>: ---------- keywords: +patch pull_requests: +27691 stage: needs patch -> patch review pull_request: https://github.com/python/cpython/pull/29437 _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Change by Raymond Hettinger <raymond.hettinger@gmail.com>: ---------- Removed message: https://bugs.python.org/msg405847 _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Serhiy Storchaka <storchaka+cpython@gmail.com> added the comment: There is a difference if you modify the arguments list after creating a thread. args = [1] t = threading.Thread(target=access, args=args) args[0] = 2 t.start() Would it call access(1) or access(2)? ---------- nosy: +serhiy.storchaka _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Tim Peters <tim@python.org> added the comment: Serhiy, we haven't documented such stuff, and, indeed, I've been burned by it but much more often in the case of multiprocessing.Process. But note that I'm SWAPPING the order of your last two lines. In the original, you mutated the argument _before_ starting any parallel work, so "of course" the new worker will see the mutation: def access(xs): print(xs) args = ([1],) t = multiprocessing.Process(target=access, args=args) t.start() # start parallel work before mutating args[0][0] = 2 Does that print [1] or [2]? Passing a tuple in no way prevents mutations to mutable objects the tuple contains. When the docs are silent, "implementation defined" rules. Whether you use threading or multiprocessing in the altered example above, the result printed simply isn't defined - it's a race between the main thread doing the mutation and the "parallel part" accessing the mutated object. This is subtler in the multiprocessing context, though, because the relevant "parallel part" is really the hidden thread that pickles the argument list to send to the worker. That effectively makes a deep copy. But it's still a race, just not one visible from staring at the Python code. In the threading case, no copies are made. ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Tim Peters <tim@python.org> added the comment: Changed stage back to "needs patch", since Raymond appears to have closed his PR. Raymond, what's up with that? ---------- stage: patch review -> needs patch _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Yu Zhao <zhaoyu_hit@qq.com> added the comment: I'd like to work on this issue recently if it's still needed. According to suggestions in PR:https://github.com/python/cpython/pull/29437, I will: * Add doc example for Thread function; * Add some test cases for checking the validity of list args; * Repeat the above works on multiprocessing module. ---------- nosy: +CharlieZhao _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Tim Peters <tim@python.org> added the comment: Charliz, please do! I have no idea why Raymond just stopped. He even deleted his initial message here, saying "I relied on this for many years. So, yet it would be nice to guarantee it :-)". Best I can tell, nothing has changed: lots of people have relied on this behavior for years, and the docs should say it's fine. ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Change by Charlie Zhao <zhaoyu_hit@qq.com>: ---------- pull_requests: +29161 stage: needs patch -> patch review pull_request: https://github.com/python/cpython/pull/30982 _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________

Tim Peters <tim@python.org> added the comment: New changeset e466faa9df9a1bd377d9725de5484471bc4af8d0 by Charlie Zhao in branch 'main': bpo-45735: Promise the long-time truth that `args=list` works (GH-30982) https://github.com/python/cpython/commit/e466faa9df9a1bd377d9725de5484471bc4... ---------- _______________________________________ Python tracker <report@bugs.python.org> <https://bugs.python.org/issue45735> _______________________________________
participants (5)
-
Charlie Zhao
-
Raymond Hettinger
-
Serhiy Storchaka
-
Tim Peters
-
Yu Zhao