diff --git a/Lib/subprocess.py b/Lib/subprocess.py index a14fede00c391c..d38cc756ec479f 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1688,8 +1688,9 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, close_fds = False if shell: - startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW - startupinfo.wShowWindow = _winapi.SW_HIDE + if not startupinfo.dwFlags & _winapi.STARTF_USESHOWWINDOW: + startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = _winapi.SW_HIDE if not executable: # gh-101283: without a fully-qualified path, before Windows # checks the system directories, it first looks in the diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index d1840e97d0f2f7..4cea07b3d2c774 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -3777,6 +3777,34 @@ def test_startupinfo_copy(self): self.assertEqual(startupinfo.wShowWindow, subprocess.SW_HIDE) self.assertEqual(startupinfo.lpAttributeList, {"handle_list": []}) + def test_startupinfo_shell_show_window(self): + # gh-85028: shell=True must not override wShowWindow set by the caller + import _winapi + SW_MAXIMIZE = 3 + used = [] + create_process = _winapi.CreateProcess + + def spy(*args): + # The startup info is the last argument of CreateProcess() + used.append(args[-1]) + return create_process(*args) + + startupinfo = subprocess.STARTUPINFO( + dwFlags=subprocess.STARTF_USESHOWWINDOW, + wShowWindow=SW_MAXIMIZE) + with mock.patch.object(_winapi, 'CreateProcess', spy): + rc = subprocess.call(ZERO_RETURN_CMD, shell=True, + startupinfo=startupinfo) + self.assertEqual(rc, 0) + rc = subprocess.call(ZERO_RETURN_CMD, shell=True) + self.assertEqual(rc, 0) + + requested, default = used + self.assertEqual(requested.wShowWindow, SW_MAXIMIZE) + # Without STARTF_USESHOWWINDOW the shell window is still hidden. + self.assertTrue(default.dwFlags & subprocess.STARTF_USESHOWWINDOW) + self.assertEqual(default.wShowWindow, subprocess.SW_HIDE) + # CREATE_NEW_CONSOLE creates a "popup" window. @support.requires_resource('gui') def test_creationflags(self): diff --git a/Misc/NEWS.d/next/Windows/2020-06-19-00-14-52.bpo-40851.0-3EJP.rst b/Misc/NEWS.d/next/Windows/2020-06-19-00-14-52.bpo-40851.0-3EJP.rst new file mode 100644 index 00000000000000..70f5efad398b03 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-06-19-00-14-52.bpo-40851.0-3EJP.rst @@ -0,0 +1,3 @@ +:class:`subprocess.Popen` with ``shell=True`` on Windows now honors +``wShowWindow`` of the *startupinfo* argument, so the console window of the +started program can be shown. Previously it was always hidden.