bpo-34044: subprocess.Popen copies startupinfo (GH-8090) (GH-8121)
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() private method.
Python 3.7 backport from master makes the copy() private: renamed to
_copy().
(cherry picked from commit 483422f57e
)
This commit is contained in:
parent
f5770f354c
commit
29be3bd3c9
|
@ -135,6 +135,19 @@ if _mswindows:
|
|||
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 @@ class Popen(object):
|
|||
# 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:
|
||||
|
|
|
@ -2822,6 +2822,33 @@ class Win32ProcessTestCase(BaseTestCase):
|
|||
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
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue