Skip to content

Instantly share code, notes, and snippets.

@paulwinex
Last active February 6, 2025 15:42
Show Gist options
  • Save paulwinex/dc24cfb68faf23363641036663ac1535 to your computer and use it in GitHub Desktop.
Save paulwinex/dc24cfb68faf23363641036663ac1535 to your computer and use it in GitHub Desktop.
"""
Start process with different options
- detached
- with new console
- with custom envs
Example:
# python start_process.py --new-console --env PYTHONPATH=/my/path python myscript.py
"""
import subprocess
import os
import sys
import argparse
def start_process(
command: list[str] | str,
envs: dict = None,
detached: bool = False,
new_console: bool = False,
non_blocking: bool = False,
output_file=None
):
"""
Universal function to start a process with different modes.
:param command: List of arguments (e.g., ["python", "script.py"]).
:param envs: Dictionary of environment variables (e.g., {"PYTHONPATH": "/new/path"}).
:param detached: If True, the process will be detached from the parent.
:param new_console: If True, the process will start in a new terminal window.
:param non_blocking: If True, the process runs as a child but does not block the parent.
:param output_file: If provided, redirects the output of a detached process to this file.
"""
new_env = os.environ.copy()
if envs:
new_env.update(envs)
creationflags = 0
stdin = None
start_new_session = False
use_shell = False
wait_for_process = not detached and not non_blocking # Wait only if not detached or non-blocking
stdout = None
stderr = None
if detached and output_file:
stdout = open(output_file, "a")
stderr = subprocess.STDOUT
elif detached:
stdout = open(os.devnull, "w")
stderr = subprocess.STDOUT
if sys.platform == "win32":
if detached:
creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
if new_console:
creationflags |= subprocess.CREATE_NEW_CONSOLE
stdin = subprocess.DEVNULL if detached else None
else:
if detached:
start_new_session = True
stdin = open(os.devnull, "r")
if new_console:
terminal = os.environ.get("TERMINAL", "x-terminal-emulator")
if terminal in ["gnome-terminal", "konsole", "xfce4-terminal"]:
command = [terminal, "--", *command]
else:
command = f'{terminal} -e "{subprocess.list2cmdline(command)}"'
use_shell = True
process = subprocess.Popen(
command,
env=new_env,
stdout=stdout,
stderr=stderr,
stdin=stdin,
close_fds=True if sys.platform != "win32" else False,
start_new_session=start_new_session,
creationflags=creationflags,
shell=use_shell
)
if detached and output_file:
stdout.close()
if wait_for_process:
process.wait()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Start a process with different execution modes")
parser.add_argument("--detached", action="store_true", help="Run the process in the background")
parser.add_argument("--new-console", action="store_true", help="Run the process in a new terminal window")
parser.add_argument("--non-blocking", action="store_true", help="Run the process as a child without blocking the parent")
parser.add_argument("--env", action="append", help="Environment variables in the format KEY=VALUE", default=[])
parser.add_argument("--output-file", help="File to redirect output of a detached process")
parser.add_argument("command", nargs=argparse.REMAINDER, help="Command to execute")
args = parser.parse_args()
if not args.command:
parser.error("No command provided for execution")
env_vars = {key: value for key, value in (env.split("=", 1) for env in args.env)} if args.env else None
start_process(
args.command,
envs=env_vars,
detached=args.detached,
new_console=args.new_console,
non_blocking=args.non_blocking,
output_file=args.output_file
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment