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 483422f57e5d8c8bf8820fec29fc9b96bb15d4ef)
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 93635ee..00844bc 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -135,6 +135,19 @@
             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 @@
             # 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: