[Python-checkins] bpo-34044: subprocess.Popen copies startupinfo (GH-8090)

Victor Stinner webhook-mailer at python.org
Thu Jul 5 16:54:22 EDT 2018


https://github.com/python/cpython/commit/483422f57e5d8c8bf8820fec29fc9b96bb15d4ef
commit: 483422f57e5d8c8bf8820fec29fc9b96bb15d4ef
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2018-07-05T22:54:17+02:00
summary:

bpo-34044: subprocess.Popen copies startupinfo (GH-8090)

subprocess.Popen now copies the startupinfo argument to leave it
unchanged: it will modify the copy, so that the same STARTUPINFO
object can be used multiple times.

Add subprocess.STARTUPINFO.copy() method.

files:
A Misc/NEWS.d/next/Library/2018-07-04-17-14-26.bpo-34044.KWAu4y.rst
M Lib/subprocess.py
M Lib/test/test_subprocess.py

diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 93635ee61f7e..e070011d980e 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -135,6 +135,19 @@ def __init__(self, *, dwFlags=0, hStdInput=None, hStdOutput=None,
             self.hStdError = hStdError
             self.wShowWindow = wShowWindow
             self.lpAttributeList = lpAttributeList or {"handle_list": []}
+
+        def copy(self):
+            attr_list = self.lpAttributeList.copy()
+            if 'handle_list' in attr_list:
+                attr_list['handle_list'] = list(attr_list['handle_list'])
+
+            return STARTUPINFO(dwFlags=self.dwFlags,
+                               hStdInput=self.hStdInput,
+                               hStdOutput=self.hStdOutput,
+                               hStdError=self.hStdError,
+                               wShowWindow=self.wShowWindow,
+                               lpAttributeList=attr_list)
+
 else:
     import _posixsubprocess
     import select
@@ -1102,6 +1115,10 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
             # Process startup details
             if startupinfo is None:
                 startupinfo = STARTUPINFO()
+            else:
+                # bpo-34044: Copy STARTUPINFO since it is modified above,
+                # so the caller can reuse it multiple times.
+                startupinfo = startupinfo.copy()
 
             use_std_handles = -1 not in (p2cread, c2pwrite, errwrite)
             if use_std_handles:
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 4b089f525c15..73b57b21db2c 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -2822,6 +2822,33 @@ def test_startupinfo_keywords(self):
         subprocess.call([sys.executable, "-c", "import sys; sys.exit(0)"],
                         startupinfo=startupinfo)
 
+    def test_startupinfo_copy(self):
+        # bpo-34044: Popen must not modify input STARTUPINFO structure
+        startupinfo = subprocess.STARTUPINFO()
+        startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
+        startupinfo.wShowWindow = subprocess.SW_HIDE
+
+        # Call Popen() twice with the same startupinfo object to make sure
+        # that it's not modified
+        for _ in range(2):
+            cmd = [sys.executable, "-c", "pass"]
+            with open(os.devnull, 'w') as null:
+                proc = subprocess.Popen(cmd,
+                                        stdout=null,
+                                        stderr=subprocess.STDOUT,
+                                        startupinfo=startupinfo)
+                with proc:
+                    proc.communicate()
+                self.assertEqual(proc.returncode, 0)
+
+            self.assertEqual(startupinfo.dwFlags,
+                             subprocess.STARTF_USESHOWWINDOW)
+            self.assertIsNone(startupinfo.hStdInput)
+            self.assertIsNone(startupinfo.hStdOutput)
+            self.assertIsNone(startupinfo.hStdError)
+            self.assertEqual(startupinfo.wShowWindow, subprocess.SW_HIDE)
+            self.assertEqual(startupinfo.lpAttributeList, {"handle_list": []})
+
     def test_creationflags(self):
         # creationflags argument
         CREATE_NEW_CONSOLE = 16
diff --git a/Misc/NEWS.d/next/Library/2018-07-04-17-14-26.bpo-34044.KWAu4y.rst b/Misc/NEWS.d/next/Library/2018-07-04-17-14-26.bpo-34044.KWAu4y.rst
new file mode 100644
index 000000000000..42a6ffbf84af
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-07-04-17-14-26.bpo-34044.KWAu4y.rst
@@ -0,0 +1,3 @@
+``subprocess.Popen`` now copies the *startupinfo* argument to leave it
+unchanged: it will modify the copy, so that the same ``STARTUPINFO`` object can
+be used multiple times.



More information about the Python-checkins mailing list