Calling a python .exe package and immediately closing the calling script, without closing the called one


I have a rookie question:
I’m calling a python script (in my case a .exe package) from a python script.
I want the script to terminate immediately after starting the .exe.


import subprocess
import sys

if __name__ == "__main__":
    app_path = 'C:/app.exe")
    p = subprocess.Popen(app_path)

However, the calling script won’t terminate (sys.exit()) until the called script terminates.
If I use different calling methods, like os.system(), sys.exit() is reached and executed, also terminating the called script.

I tried os.system(), os.startfile(),, subprocess.Popen() and many more…

How can I deal with that?
ChatGPT doesn’t know the answer…

Thank you!

You probably want os.spawn*().

import os

if __name__ == "__main__":
    app_path = 'C:/app.exe'
    os.spawnl(os.P_DETACH, [app_path])  # P_DETACH is Windows only

i will try that on monday, thanks!

If “app.exe” is a GUI app, then using just subprocess.Popen(app_path) is fine. You should not call communicate() if you have no need to send standard input data or read standard output data or error data from the child process. communicate() obviously has to wait for the child process to exit. Unless it’s made to wait, a Popen instance does not automatically wait for the process to exit. On Windows, it doesn’t even raise a resource warning if the script doesn’t wait on the child process. Unlike using subprocess.Popen on a POSIX system (e.g. Linux or macOS), a script on Windows is not required to wait() for the process to exit in order to avoid wasting resources in a ‘zombie’ process.

When a Python subprocess.Popen instance is finalized on Windows, either explicitly or implicitly, the handle for the Process object gets closed. The lifetime of an object in the Windows kernel, such as a Process or File object, is determined by a reference count, which includes both pointer references in the kernel and handle references in user mode. Closing the handle is all that has to be done in order to ensure the kernel will automatically finalize the process (e.g. deallocate the process address space).

If “app.exe” is a console application, then you should not exit without waiting on the “app.exe” process if it’s attached to the current console session. Doing that likely creates a mess in which the parent process of Python – such as a shell like CMD, PowerShell, or bash – has to compete with the spawned console application to read from the console input buffer, and the output gets mixed up due to overlapping writes to the console screen buffer. If “app.exe” doesn’t have a user interface, then you can create it in a new windowless console session via the subprocess.Popen optional parameter creationflags=subprocess.CREATE_NO_WINDOW or create it without a console session at all via the option creationflags=subprocess.DETACHED_PROCESS. On the other hand, if “app.exe” requires an interactive console session, but you don’t want to wait for it to exit, then create it in a new interactive console session via the option creationflags=subprocess.CREATE_NEW_CONSOLE.

1 Like

I try to persuade people away from using the legacy C runtime functions os.system() and os.spawn*() on Windows. We have much better control via subprocess.Popen, such as the ability to inherit only specific handles when stdin, stdout, and stderr are redirected; explicit control over several startupinfo fields; and customization via creationflags.

The C runtime’s P_DETACH spawn mode is equivalent to using the subprocess.Popen optional argument creationflags=subprocess.DETACHED_PROCESS. The latter creation flag is rarely desired because it spawns a console application without attaching it to a console session (i.e. a console server running in a “conhost.exe” or “openconsole.exe” console host process), meaning that it has no standard I/O and all console API functions will fail. Via subprocess.Popen, it’s possible to redirect the child’s stdin, stdout, and stderr to a file, subprocess.PIPE, or subprocess.DEVNULL. This is sometimes enough to allow a console app to succeed even without an attached console session, but only if it doesn’t depend on particular console API functions. Generally it’s more reliable to use creationflags=subprocess.CREATE_NO_WINDOW if you need to a run a console app in the background with redirected standard I/O. This creates a new console session that’s functional in every way, except that it has no window. If you just want to spawn a console app asynchronously without having to wait on it, generally it’s best to create a new interactive console session for it via creationflags=subprocess.CREATE_NEW_CONSOLE.

1 Like

Thanks for the replies.

subprocess.Popen(app_path, creationflags=subprocess.CREATE_NEW_CONSOLE)
subprocess.Popen(app_path, creationflags=subprocess. DETACHED_PROCESS)

both detach the two processes in a way, that the first one doesn’t wait for the second to finish.
However, I can see the second process starting, but when the first one terminates, it also closes the second one.
All I want to achieve is a simulated double click on an exe performed by a python script.

os.spawnl(os.P_DETACH, [app_path])  # P_DETACH is Windows only

also doesn’t work.

Alternatively I just execute the first script after packing it to an .exe without console, so it’s invisible in the background.
It then just behaves to the user like desired and terminates eventually after termination of the second .exe.

It’s just remarkable for me that such a basic task is not so easy to achieve (at least for me).

The behavior that you’re describing doesn’t make sense to me. Try something simple and common like spawning Python’s interactive shell in a new console session:

import sys
import subprocess

subprocess.Popen(sys.executable, creationflags=subprocess.CREATE_NEW_CONSOLE)

You are right, in this case it behaves as expected.
I don’t know, why it doesn’t work in my case…

However, I think I can live with the invisible launcher process in the background.
Thank you!