From cd7f54a8f51a3e79f6ac01bb4e1333c175dc06e5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 21 Feb 2022 17:24:05 +0100 Subject: [PATCH] created function to run detached process --- openpype/lib/__init__.py | 2 ++ openpype/lib/execute.py | 78 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index ebe7648ad7..8c3ebc8a3c 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -29,6 +29,7 @@ from .execute import ( get_linux_launcher_args, execute, run_subprocess, + run_detached_process, run_openpype_process, clean_envs_for_openpype_process, path_to_subprocess_arg, @@ -188,6 +189,7 @@ __all__ = [ "get_linux_launcher_args", "execute", "run_subprocess", + "run_detached_process", "run_openpype_process", "clean_envs_for_openpype_process", "path_to_subprocess_arg", diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index afde844f2d..f2eb97c5f5 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -1,5 +1,9 @@ import os +import sys import subprocess +import platform +import json +import tempfile import distutils.spawn from .log import PypeLogger as Logger @@ -181,6 +185,80 @@ def run_openpype_process(*args, **kwargs): return run_subprocess(args, env=env, **kwargs) +def run_detached_process(args, **kwargs): + """Execute process with passed arguments as separated process. + + Values from 'os.environ' are used for environments if are not passed. + They are cleaned using 'clean_envs_for_openpype_process' function. + + Example: + ``` + run_detached_openpype_process("run", "") + ``` + + Args: + *args (tuple): OpenPype cli arguments. + **kwargs (dict): Keyword arguments for for subprocess.Popen. + + Returns: + subprocess.Popen: Pointer to launched process but it is possible that + launched process is already killed (on linux). + """ + env = kwargs.pop("env", None) + # Keep env untouched if are passed and not empty + if not env: + env = os.environ + + # Create copy of passed env + kwargs["env"] = {k: v for k, v in env.items()} + + low_platform = platform.system().lower() + if low_platform == "darwin": + new_args = ["open", "-na", args.pop(0), "--args"] + new_args.extend(args) + args = new_args + + elif low_platform == "windows": + flags = ( + subprocess.CREATE_NEW_PROCESS_GROUP + | subprocess.DETACHED_PROCESS + ) + kwargs["creationflags"] = flags + + if not sys.stdout: + kwargs["stdout"] = subprocess.DEVNULL + kwargs["stderr"] = subprocess.DEVNULL + + elif low_platform == "linux" and get_linux_launcher_args() is not None: + json_data = { + "args": args, + "env": kwargs.pop("env") + } + json_temp = tempfile.NamedTemporaryFile( + mode="w", prefix="op_app_args", suffix=".json", delete=False + ) + json_temp.close() + json_temp_filpath = json_temp.name + with open(json_temp_filpath, "w") as stream: + json.dump(json_data, stream) + + new_args = get_linux_launcher_args() + new_args.append(json_temp_filpath) + + # Create mid-process which will launch application + process = subprocess.Popen(new_args, **kwargs) + # Wait until the process finishes + # - This is important! The process would stay in "open" state. + process.wait() + # Remove the temp file + os.remove(json_temp_filpath) + # Return process which is already terminated + return process + + process = subprocess.Popen(args, **kwargs) + return process + + def path_to_subprocess_arg(path): """Prepare path for subprocess arguments.