From 2b1e31001c74a6dd263236e0f7a5c0600c372249 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:52:11 +0100 Subject: [PATCH 01/16] duplicated non python host launch script for photoshop --- .../hosts/photoshop/api/launch_script.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 client/ayon_core/hosts/photoshop/api/launch_script.py diff --git a/client/ayon_core/hosts/photoshop/api/launch_script.py b/client/ayon_core/hosts/photoshop/api/launch_script.py new file mode 100644 index 0000000000..c036b63c46 --- /dev/null +++ b/client/ayon_core/hosts/photoshop/api/launch_script.py @@ -0,0 +1,93 @@ +"""Script wraps launch mechanism of Photoshop implementations. + +Arguments passed to the script are passed to launch function in host +implementation. In all cases requires host app executable and may contain +workfile or others. +""" + +import os +import sys + +from ayon_core.hosts.photoshop.api.lib import main + +# Get current file to locate start point of sys.argv +CURRENT_FILE = os.path.abspath(__file__) + + +def show_error_messagebox(title, message, detail_message=None): + """Function will show message and process ends after closing it.""" + from qtpy import QtWidgets, QtCore + from ayon_core import style + + app = QtWidgets.QApplication([]) + app.setStyleSheet(style.load_stylesheet()) + + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle(title) + msgbox.setText(message) + + if detail_message: + msgbox.setDetailedText(detail_message) + + msgbox.setWindowModality(QtCore.Qt.ApplicationModal) + msgbox.show() + + sys.exit(app.exec_()) + + +def on_invalid_args(script_not_found): + """Show to user message box saying that something went wrong. + + Tell user that arguments to launch implementation are invalid with + arguments details. + + Args: + script_not_found (bool): Use different message based on this value. + """ + + title = "Invalid arguments" + joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) + if script_not_found: + submsg = "Where couldn't find script path:\n\"{}\"" + else: + submsg = "Expected Host executable after script path:\n\"{}\"" + + message = "BUG: Got invalid arguments so can't launch Host application." + detail_message = "Process was launched with arguments:\n{}\n\n{}".format( + joined_args, + submsg.format(CURRENT_FILE) + ) + + show_error_messagebox(title, message, detail_message) + + +def main(argv): + # Modify current file path to find match in sys.argv which may be different + # on windows (different letter cases and slashes). + modified_current_file = CURRENT_FILE.replace("\\", "/").lower() + + # Create a copy of sys argv + sys_args = list(argv) + after_script_idx = None + # Find script path in sys.argv to know index of argv where host + # executable should be. + for idx, item in enumerate(sys_args): + if item.replace("\\", "/").lower() == modified_current_file: + after_script_idx = idx + 1 + break + + # Validate that there is at least one argument after script path + launch_args = None + if after_script_idx is not None: + launch_args = sys_args[after_script_idx:] + + if launch_args: + # Launch host implementation + main(*launch_args) + else: + # Show message box + on_invalid_args(after_script_idx is None) + + +if __name__ == "__main__": + main(sys.argv) From fee6a9b4485de958af128c9db0f7da8f4dbfbfa6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:52:52 +0100 Subject: [PATCH 02/16] renamed 'PHOTOSHOP_HOST_DIR' to 'PHOTOSHOP_ADDON_ROOT' --- client/ayon_core/hosts/photoshop/__init__.py | 4 ++-- client/ayon_core/hosts/photoshop/addon.py | 2 +- client/ayon_core/hosts/photoshop/api/pipeline.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/__init__.py b/client/ayon_core/hosts/photoshop/__init__.py index 773f73d624..0c79afaab8 100644 --- a/client/ayon_core/hosts/photoshop/__init__.py +++ b/client/ayon_core/hosts/photoshop/__init__.py @@ -1,10 +1,10 @@ from .addon import ( + PHOTOSHOP_ADDON_ROOT, PhotoshopAddon, - PHOTOSHOP_HOST_DIR, ) __all__ = ( + "PHOTOSHOP_ADDON_ROOT", "PhotoshopAddon", - "PHOTOSHOP_HOST_DIR", ) diff --git a/client/ayon_core/hosts/photoshop/addon.py b/client/ayon_core/hosts/photoshop/addon.py index 3016912960..28833121c4 100644 --- a/client/ayon_core/hosts/photoshop/addon.py +++ b/client/ayon_core/hosts/photoshop/addon.py @@ -1,7 +1,7 @@ import os from ayon_core.addon import AYONAddon, IHostAddon -PHOTOSHOP_HOST_DIR = os.path.dirname(os.path.abspath(__file__)) +PHOTOSHOP_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__)) class PhotoshopAddon(AYONAddon, IHostAddon): diff --git a/client/ayon_core/hosts/photoshop/api/pipeline.py b/client/ayon_core/hosts/photoshop/api/pipeline.py index 32f66cf7fb..27cfa5a7b5 100644 --- a/client/ayon_core/hosts/photoshop/api/pipeline.py +++ b/client/ayon_core/hosts/photoshop/api/pipeline.py @@ -21,14 +21,14 @@ from ayon_core.host import ( ) from ayon_core.pipeline.load import any_outdated_containers -from ayon_core.hosts.photoshop import PHOTOSHOP_HOST_DIR +from ayon_core.hosts.photoshop import PHOTOSHOP_ADDON_ROOT from ayon_core.tools.utils import get_ayon_qt_app from . import lib log = Logger.get_logger(__name__) -PLUGINS_DIR = os.path.join(PHOTOSHOP_HOST_DIR, "plugins") +PLUGINS_DIR = os.path.join(PHOTOSHOP_ADDON_ROOT, "plugins") PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") LOAD_PATH = os.path.join(PLUGINS_DIR, "load") CREATE_PATH = os.path.join(PLUGINS_DIR, "create") From aaea1e14859799dd8c443c832dce2ffa75be8e3f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:53:17 +0100 Subject: [PATCH 03/16] photoshop has own prelaunch hook to modify launch arguments --- .../hooks/pre_non_python_host_launch.py | 2 +- client/ayon_core/hosts/photoshop/__init__.py | 2 + client/ayon_core/hosts/photoshop/addon.py | 14 +++ .../hosts/photoshop/hooks/pre_launch_args.py | 95 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py diff --git a/client/ayon_core/hooks/pre_non_python_host_launch.py b/client/ayon_core/hooks/pre_non_python_host_launch.py index fed4c99447..3d3c24b308 100644 --- a/client/ayon_core/hooks/pre_non_python_host_launch.py +++ b/client/ayon_core/hooks/pre_non_python_host_launch.py @@ -17,7 +17,7 @@ class NonPythonHostHook(PreLaunchHook): python script which launch the host. For these cases it is necessary to prepend python (or ayon) executable and script path before application's. """ - app_groups = {"harmony", "photoshop", "aftereffects"} + app_groups = {"harmony", "aftereffects"} order = 20 launch_types = {LaunchTypes.local} diff --git a/client/ayon_core/hosts/photoshop/__init__.py b/client/ayon_core/hosts/photoshop/__init__.py index 0c79afaab8..cf21b7df75 100644 --- a/client/ayon_core/hosts/photoshop/__init__.py +++ b/client/ayon_core/hosts/photoshop/__init__.py @@ -1,10 +1,12 @@ from .addon import ( PHOTOSHOP_ADDON_ROOT, PhotoshopAddon, + get_launch_script_path, ) __all__ = ( "PHOTOSHOP_ADDON_ROOT", "PhotoshopAddon", + "get_launch_script_path", ) diff --git a/client/ayon_core/hosts/photoshop/addon.py b/client/ayon_core/hosts/photoshop/addon.py index 28833121c4..65fe6a7cd1 100644 --- a/client/ayon_core/hosts/photoshop/addon.py +++ b/client/ayon_core/hosts/photoshop/addon.py @@ -20,3 +20,17 @@ class PhotoshopAddon(AYONAddon, IHostAddon): def get_workfile_extensions(self): return [".psd", ".psb"] + + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(PHOTOSHOP_ADDON_ROOT, "hooks") + ] + + +def get_launch_script_path(): + return os.path.join( + PHOTOSHOP_ADDON_ROOT, "api", "launch_script.py" + ) + diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py new file mode 100644 index 0000000000..0689a6c2f0 --- /dev/null +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -0,0 +1,95 @@ +import os +import platform +import subprocess + +from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib.applications import ( + PreLaunchHook, + LaunchTypes, +) +from ayon_core.hosts.photoshop import get_launch_script_path + + +def get_launch_kwargs(kwargs): + """Explicit setting of kwargs for Popen for Photoshop. + + Expected behavior + - ayon_console opens window with logs + - ayon has stdout/stderr available for capturing + + Args: + kwargs (Union[dict, None]): Current kwargs or None. + + """ + if kwargs is None: + kwargs = {} + + if platform.system().lower() != "windows": + return kwargs + + executable_path = os.environ.get("AYON_EXECUTABLE") + + executable_filename = "" + if executable_path: + executable_filename = os.path.basename(executable_path) + + is_gui_executable = "ayon_console" not in executable_filename + if is_gui_executable: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + else: + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + return kwargs + + +class PhotoshopPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and script path to Photoshop implementation + before Photoshop executable and add last workfile path to launch arguments. + + Existence of last workfile is checked. If workfile does not exists tries + to copy templated workfile from predefined path. + """ + app_groups = {"photoshop"} + + order = 20 + launch_types = {LaunchTypes.local} + + def execute(self): + # Pop executable + executable_path = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + script_path = get_launch_script_path() + + new_launch_args = get_ayon_launcher_args( + "run", script_path, executable_path + ) + # Add workfile path if exists + workfile_path = self.data["last_workfile_path"] + if ( + self.data.get("start_last_workfile") + and workfile_path + and os.path.exists(workfile_path) + ): + new_launch_args.append(workfile_path) + + # Append as whole list as these arguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.launch_context.launch_args.extend(remainders) + + self.launch_context.kwargs = get_launch_kwargs( + self.launch_context.kwargs + ) From 0d75102d8a255237b5d8fa180e13d0febe250c31 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:53:54 +0100 Subject: [PATCH 04/16] aftereffects have own launch script --- .../hosts/aftereffects/api/launch_script.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 client/ayon_core/hosts/aftereffects/api/launch_script.py diff --git a/client/ayon_core/hosts/aftereffects/api/launch_script.py b/client/ayon_core/hosts/aftereffects/api/launch_script.py new file mode 100644 index 0000000000..ad4e779bd0 --- /dev/null +++ b/client/ayon_core/hosts/aftereffects/api/launch_script.py @@ -0,0 +1,93 @@ +"""Script wraps launch mechanism of AfterEffects implementations. + +Arguments passed to the script are passed to launch function in host +implementation. In all cases requires host app executable and may contain +workfile or others. +""" + +import os +import sys + +from ayon_core.hosts.aftereffects.api.launch_logic import main + +# Get current file to locate start point of sys.argv +CURRENT_FILE = os.path.abspath(__file__) + + +def show_error_messagebox(title, message, detail_message=None): + """Function will show message and process ends after closing it.""" + from qtpy import QtWidgets, QtCore + from ayon_core import style + + app = QtWidgets.QApplication([]) + app.setStyleSheet(style.load_stylesheet()) + + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle(title) + msgbox.setText(message) + + if detail_message: + msgbox.setDetailedText(detail_message) + + msgbox.setWindowModality(QtCore.Qt.ApplicationModal) + msgbox.show() + + sys.exit(app.exec_()) + + +def on_invalid_args(script_not_found): + """Show to user message box saying that something went wrong. + + Tell user that arguments to launch implementation are invalid with + arguments details. + + Args: + script_not_found (bool): Use different message based on this value. + """ + + title = "Invalid arguments" + joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) + if script_not_found: + submsg = "Where couldn't find script path:\n\"{}\"" + else: + submsg = "Expected Host executable after script path:\n\"{}\"" + + message = "BUG: Got invalid arguments so can't launch Host application." + detail_message = "Process was launched with arguments:\n{}\n\n{}".format( + joined_args, + submsg.format(CURRENT_FILE) + ) + + show_error_messagebox(title, message, detail_message) + + +def main(argv): + # Modify current file path to find match in sys.argv which may be different + # on windows (different letter cases and slashes). + modified_current_file = CURRENT_FILE.replace("\\", "/").lower() + + # Create a copy of sys argv + sys_args = list(argv) + after_script_idx = None + # Find script path in sys.argv to know index of argv where host + # executable should be. + for idx, item in enumerate(sys_args): + if item.replace("\\", "/").lower() == modified_current_file: + after_script_idx = idx + 1 + break + + # Validate that there is at least one argument after script path + launch_args = None + if after_script_idx is not None: + launch_args = sys_args[after_script_idx:] + + if launch_args: + # Launch host implementation + main(*launch_args) + else: + # Show message box + on_invalid_args(after_script_idx is None) + + +if __name__ == "__main__": + main(sys.argv) From 720de823cf973dab3d75bede890cf49dc7fb7efc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 15:54:36 +0100 Subject: [PATCH 05/16] aftereffects has own prelaunch hook to modify launch arguments --- .../hooks/pre_non_python_host_launch.py | 2 +- .../ayon_core/hosts/aftereffects/__init__.py | 8 +- client/ayon_core/hosts/aftereffects/addon.py | 17 ++++ .../hosts/aftereffects/api/launch_logic.py | 1 - .../aftereffects/hooks/pre_launch_args.py | 95 +++++++++++++++++++ 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py diff --git a/client/ayon_core/hooks/pre_non_python_host_launch.py b/client/ayon_core/hooks/pre_non_python_host_launch.py index 3d3c24b308..a65e0ecba4 100644 --- a/client/ayon_core/hooks/pre_non_python_host_launch.py +++ b/client/ayon_core/hooks/pre_non_python_host_launch.py @@ -17,7 +17,7 @@ class NonPythonHostHook(PreLaunchHook): python script which launch the host. For these cases it is necessary to prepend python (or ayon) executable and script path before application's. """ - app_groups = {"harmony", "aftereffects"} + app_groups = {"harmony"} order = 20 launch_types = {LaunchTypes.local} diff --git a/client/ayon_core/hosts/aftereffects/__init__.py b/client/ayon_core/hosts/aftereffects/__init__.py index ae750d05b6..02ab287629 100644 --- a/client/ayon_core/hosts/aftereffects/__init__.py +++ b/client/ayon_core/hosts/aftereffects/__init__.py @@ -1,6 +1,12 @@ -from .addon import AfterEffectsAddon +from .addon import ( + AFTEREFFECTS_ADDON_ROOT, + AfterEffectsAddon, + get_launch_script_path, +) __all__ = ( + "AFTEREFFECTS_ADDON_ROOT", "AfterEffectsAddon", + "get_launch_script_path", ) diff --git a/client/ayon_core/hosts/aftereffects/addon.py b/client/ayon_core/hosts/aftereffects/addon.py index 46d0818247..fc54043c1d 100644 --- a/client/ayon_core/hosts/aftereffects/addon.py +++ b/client/ayon_core/hosts/aftereffects/addon.py @@ -1,5 +1,9 @@ +import os + from ayon_core.addon import AYONAddon, IHostAddon +AFTEREFFECTS_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__)) + class AfterEffectsAddon(AYONAddon, IHostAddon): name = "aftereffects" @@ -17,3 +21,16 @@ class AfterEffectsAddon(AYONAddon, IHostAddon): def get_workfile_extensions(self): return [".aep"] + + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(AFTEREFFECTS_ADDON_ROOT, "hooks") + ] + + +def get_launch_script_path(): + return os.path.join( + AFTEREFFECTS_ADDON_ROOT, "api", "launch_script.py" + ) diff --git a/client/ayon_core/hosts/aftereffects/api/launch_logic.py b/client/ayon_core/hosts/aftereffects/api/launch_logic.py index d0e4e8beae..5a23f2cb35 100644 --- a/client/ayon_core/hosts/aftereffects/api/launch_logic.py +++ b/client/ayon_core/hosts/aftereffects/api/launch_logic.py @@ -7,7 +7,6 @@ import asyncio import functools import traceback - from wsrpc_aiohttp import ( WebSocketRoute, WebSocketAsync diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py new file mode 100644 index 0000000000..2c5baa3b68 --- /dev/null +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -0,0 +1,95 @@ +import os +import platform +import subprocess + +from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib.applications import ( + PreLaunchHook, + LaunchTypes, +) +from ayon_core.hosts.aftereffects import get_launch_script_path + + +def get_launch_kwargs(kwargs): + """Explicit setting of kwargs for Popen for AfterEffects. + + Expected behavior + - ayon_console opens window with logs + - ayon has stdout/stderr available for capturing + + Args: + kwargs (Union[dict, None]): Current kwargs or None. + + """ + if kwargs is None: + kwargs = {} + + if platform.system().lower() != "windows": + return kwargs + + executable_path = os.environ.get("AYON_EXECUTABLE") + + executable_filename = "" + if executable_path: + executable_filename = os.path.basename(executable_path) + + is_in_ui_launcher = "ayon_console" not in executable_filename + if is_in_ui_launcher: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + else: + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + return kwargs + + +class AEPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and script path to AE implementation before + AE executable and add last workfile path to launch arguments. + + Existence of last workfile is checked. If workfile does not exists tries + to copy templated workfile from predefined path. + """ + app_groups = {"aftereffects"} + + order = 20 + launch_types = {LaunchTypes.local} + + def execute(self): + # Pop executable + executable_path = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + script_path = get_launch_script_path() + + new_launch_args = get_ayon_launcher_args( + "run", script_path, executable_path + ) + # Add workfile path if exists + workfile_path = self.data["last_workfile_path"] + if ( + self.data.get("start_last_workfile") + and workfile_path + and os.path.exists(workfile_path) + ): + new_launch_args.append(workfile_path) + + # Append as whole list as these arguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.launch_context.launch_args.extend(remainders) + + self.launch_context.kwargs = get_launch_kwargs( + self.launch_context.kwargs + ) From 96aac985c2545b5e8e5bf74a96e448a3b667463c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:23:23 +0100 Subject: [PATCH 06/16] copied non python launch script to harmony --- .../hosts/harmony/api/launch_script.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 client/ayon_core/hosts/harmony/api/launch_script.py diff --git a/client/ayon_core/hosts/harmony/api/launch_script.py b/client/ayon_core/hosts/harmony/api/launch_script.py new file mode 100644 index 0000000000..1f2c36b7e6 --- /dev/null +++ b/client/ayon_core/hosts/harmony/api/launch_script.py @@ -0,0 +1,93 @@ +"""Script wraps launch mechanism of Harmony implementations. + +Arguments passed to the script are passed to launch function in host +implementation. In all cases requires host app executable and may contain +workfile or others. +""" + +import os +import sys + +from ayon_core.hosts.harmony.api.lib import main + +# Get current file to locate start point of sys.argv +CURRENT_FILE = os.path.abspath(__file__) + + +def show_error_messagebox(title, message, detail_message=None): + """Function will show message and process ends after closing it.""" + from qtpy import QtWidgets, QtCore + from ayon_core import style + + app = QtWidgets.QApplication([]) + app.setStyleSheet(style.load_stylesheet()) + + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle(title) + msgbox.setText(message) + + if detail_message: + msgbox.setDetailedText(detail_message) + + msgbox.setWindowModality(QtCore.Qt.ApplicationModal) + msgbox.show() + + sys.exit(app.exec_()) + + +def on_invalid_args(script_not_found): + """Show to user message box saying that something went wrong. + + Tell user that arguments to launch implementation are invalid with + arguments details. + + Args: + script_not_found (bool): Use different message based on this value. + """ + + title = "Invalid arguments" + joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) + if script_not_found: + submsg = "Where couldn't find script path:\n\"{}\"" + else: + submsg = "Expected Host executable after script path:\n\"{}\"" + + message = "BUG: Got invalid arguments so can't launch Host application." + detail_message = "Process was launched with arguments:\n{}\n\n{}".format( + joined_args, + submsg.format(CURRENT_FILE) + ) + + show_error_messagebox(title, message, detail_message) + + +def main(argv): + # Modify current file path to find match in sys.argv which may be different + # on windows (different letter cases and slashes). + modified_current_file = CURRENT_FILE.replace("\\", "/").lower() + + # Create a copy of sys argv + sys_args = list(argv) + after_script_idx = None + # Find script path in sys.argv to know index of argv where host + # executable should be. + for idx, item in enumerate(sys_args): + if item.replace("\\", "/").lower() == modified_current_file: + after_script_idx = idx + 1 + break + + # Validate that there is at least one argument after script path + launch_args = None + if after_script_idx is not None: + launch_args = sys_args[after_script_idx:] + + if launch_args: + # Launch host implementation + main(*launch_args) + else: + # Show message box + on_invalid_args(after_script_idx is None) + + +if __name__ == "__main__": + main(sys.argv) From da395ae5305cb63cd19140cc2b48296facc94809 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:23:57 +0100 Subject: [PATCH 07/16] renamed 'HARMONY_HOST_DIR' to 'HARMONY_ADDON_ROOT' --- client/ayon_core/hosts/harmony/__init__.py | 4 ++-- client/ayon_core/hosts/harmony/addon.py | 4 ++-- client/ayon_core/hosts/harmony/api/pipeline.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/harmony/__init__.py b/client/ayon_core/hosts/harmony/__init__.py index 9177eaa285..449bb96a4f 100644 --- a/client/ayon_core/hosts/harmony/__init__.py +++ b/client/ayon_core/hosts/harmony/__init__.py @@ -1,10 +1,10 @@ from .addon import ( - HARMONY_HOST_DIR, + HARMONY_ADDON_ROOT, HarmonyAddon, ) __all__ = ( - "HARMONY_HOST_DIR", + "HARMONY_ADDON_ROOT", "HarmonyAddon", ) diff --git a/client/ayon_core/hosts/harmony/addon.py b/client/ayon_core/hosts/harmony/addon.py index 476d569415..7f8776cd74 100644 --- a/client/ayon_core/hosts/harmony/addon.py +++ b/client/ayon_core/hosts/harmony/addon.py @@ -1,7 +1,7 @@ import os from ayon_core.addon import AYONAddon, IHostAddon -HARMONY_HOST_DIR = os.path.dirname(os.path.abspath(__file__)) +HARMONY_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__)) class HarmonyAddon(AYONAddon, IHostAddon): @@ -11,7 +11,7 @@ class HarmonyAddon(AYONAddon, IHostAddon): def add_implementation_envs(self, env, _app): """Modify environments to contain all required for implementation.""" openharmony_path = os.path.join( - HARMONY_HOST_DIR, "vendor", "OpenHarmony" + HARMONY_ADDON_ROOT, "vendor", "OpenHarmony" ) # TODO check if is already set? What to do if is already set? env["LIB_OPENHARMONY_PATH"] = openharmony_path diff --git a/client/ayon_core/hosts/harmony/api/pipeline.py b/client/ayon_core/hosts/harmony/api/pipeline.py index a753a32ebb..d842ccd414 100644 --- a/client/ayon_core/hosts/harmony/api/pipeline.py +++ b/client/ayon_core/hosts/harmony/api/pipeline.py @@ -15,13 +15,13 @@ from ayon_core.pipeline import ( from ayon_core.pipeline.load import get_outdated_containers from ayon_core.pipeline.context_tools import get_current_project_folder -from ayon_core.hosts.harmony import HARMONY_HOST_DIR +from ayon_core.hosts.harmony import HARMONY_ADDON_ROOT import ayon_core.hosts.harmony.api as harmony log = logging.getLogger("ayon_core.hosts.harmony") -PLUGINS_DIR = os.path.join(HARMONY_HOST_DIR, "plugins") +PLUGINS_DIR = os.path.join(HARMONY_ADDON_ROOT, "plugins") PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") LOAD_PATH = os.path.join(PLUGINS_DIR, "load") CREATE_PATH = os.path.join(PLUGINS_DIR, "create") From 2119e735828c44ea23fc4a9d463382e474f7367a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:25:14 +0100 Subject: [PATCH 08/16] harmony has own prelaunch hook --- .../hooks/pre_non_python_host_launch.py | 58 ----------- client/ayon_core/hosts/harmony/__init__.py | 2 + client/ayon_core/hosts/harmony/addon.py | 13 +++ .../hosts/harmony/hooks/pre_launch_args.py | 95 +++++++++++++++++++ 4 files changed, 110 insertions(+), 58 deletions(-) delete mode 100644 client/ayon_core/hooks/pre_non_python_host_launch.py create mode 100644 client/ayon_core/hosts/harmony/hooks/pre_launch_args.py diff --git a/client/ayon_core/hooks/pre_non_python_host_launch.py b/client/ayon_core/hooks/pre_non_python_host_launch.py deleted file mode 100644 index a65e0ecba4..0000000000 --- a/client/ayon_core/hooks/pre_non_python_host_launch.py +++ /dev/null @@ -1,58 +0,0 @@ -import os - -from ayon_core.lib import get_ayon_launcher_args -from ayon_core.lib.applications import ( - get_non_python_host_kwargs, - PreLaunchHook, - LaunchTypes, -) - -from ayon_core import AYON_CORE_ROOT - - -class NonPythonHostHook(PreLaunchHook): - """Launch arguments preparation. - - Non python host implementation do not launch host directly but use - python script which launch the host. For these cases it is necessary to - prepend python (or ayon) executable and script path before application's. - """ - app_groups = {"harmony"} - - order = 20 - launch_types = {LaunchTypes.local} - - def execute(self): - # Pop executable - executable_path = self.launch_context.launch_args.pop(0) - - # Pop rest of launch arguments - There should not be other arguments! - remainders = [] - while self.launch_context.launch_args: - remainders.append(self.launch_context.launch_args.pop(0)) - - script_path = os.path.join( - AYON_CORE_ROOT, - "scripts", - "non_python_host_launch.py" - ) - - new_launch_args = get_ayon_launcher_args( - "run", script_path, executable_path - ) - # Add workfile path if exists - workfile_path = self.data["last_workfile_path"] - if ( - self.data.get("start_last_workfile") - and workfile_path - and os.path.exists(workfile_path)): - new_launch_args.append(workfile_path) - - # Append as whole list as these areguments should not be separated - self.launch_context.launch_args.append(new_launch_args) - - if remainders: - self.launch_context.launch_args.extend(remainders) - - self.launch_context.kwargs = \ - get_non_python_host_kwargs(self.launch_context.kwargs) diff --git a/client/ayon_core/hosts/harmony/__init__.py b/client/ayon_core/hosts/harmony/__init__.py index 449bb96a4f..6454d6f9d7 100644 --- a/client/ayon_core/hosts/harmony/__init__.py +++ b/client/ayon_core/hosts/harmony/__init__.py @@ -1,10 +1,12 @@ from .addon import ( HARMONY_ADDON_ROOT, HarmonyAddon, + get_launch_script_path, ) __all__ = ( "HARMONY_ADDON_ROOT", "HarmonyAddon", + "get_launch_script_path", ) diff --git a/client/ayon_core/hosts/harmony/addon.py b/client/ayon_core/hosts/harmony/addon.py index 7f8776cd74..1915a7eb6f 100644 --- a/client/ayon_core/hosts/harmony/addon.py +++ b/client/ayon_core/hosts/harmony/addon.py @@ -18,3 +18,16 @@ class HarmonyAddon(AYONAddon, IHostAddon): def get_workfile_extensions(self): return [".zip"] + + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(HARMONY_ADDON_ROOT, "hooks") + ] + + +def get_launch_script_path(): + return os.path.join( + HARMONY_ADDON_ROOT, "api", "launch_script.py" + ) diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py new file mode 100644 index 0000000000..1a36c890a7 --- /dev/null +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -0,0 +1,95 @@ +import os +import platform +import subprocess + +from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib.applications import ( + PreLaunchHook, + LaunchTypes, +) +from ayon_core.hosts.harmony import get_launch_script_path + + +def get_launch_kwargs(kwargs): + """Explicit setting of kwargs for Popen for Harmony. + + Expected behavior + - ayon_console opens window with logs + - ayon has stdout/stderr available for capturing + + Args: + kwargs (Union[dict, None]): Current kwargs or None. + + """ + if kwargs is None: + kwargs = {} + + if platform.system().lower() != "windows": + return kwargs + + executable_path = os.environ.get("AYON_EXECUTABLE") + + executable_filename = "" + if executable_path: + executable_filename = os.path.basename(executable_path) + + is_gui_executable = "ayon_console" not in executable_filename + if is_gui_executable: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + else: + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + return kwargs + + +class HarmonyPrelaunchHook(PreLaunchHook): + """Launch arguments preparation. + + Hook add python executable and script path to Harmony implementation + before Harmony executable and add last workfile path to launch arguments. + + Existence of last workfile is checked. If workfile does not exists tries + to copy templated workfile from predefined path. + """ + app_groups = {"harmony"} + + order = 20 + launch_types = {LaunchTypes.local} + + def execute(self): + # Pop executable + executable_path = self.launch_context.launch_args.pop(0) + + # Pop rest of launch arguments - There should not be other arguments! + remainders = [] + while self.launch_context.launch_args: + remainders.append(self.launch_context.launch_args.pop(0)) + + script_path = get_launch_script_path() + + new_launch_args = get_ayon_launcher_args( + "run", script_path, executable_path + ) + # Add workfile path if exists + workfile_path = self.data["last_workfile_path"] + if ( + self.data.get("start_last_workfile") + and workfile_path + and os.path.exists(workfile_path) + ): + new_launch_args.append(workfile_path) + + # Append as whole list as these arguments should not be separated + self.launch_context.launch_args.append(new_launch_args) + + if remainders: + self.launch_context.launch_args.extend(remainders) + + self.launch_context.kwargs = get_launch_kwargs( + self.launch_context.kwargs + ) From 4fb898615bff67a20925622e297859b369db6477 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:28:59 +0100 Subject: [PATCH 09/16] remove unused global script --- .../scripts/non_python_host_launch.py | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 client/ayon_core/scripts/non_python_host_launch.py diff --git a/client/ayon_core/scripts/non_python_host_launch.py b/client/ayon_core/scripts/non_python_host_launch.py deleted file mode 100644 index 4c18fd0ccc..0000000000 --- a/client/ayon_core/scripts/non_python_host_launch.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Script wraps launch mechanism of non python host implementations. - -Arguments passed to the script are passed to launch function in host -implementation. In all cases requires host app executable and may contain -workfile or others. -""" - -import os -import sys - -# Get current file to locate start point of sys.argv -CURRENT_FILE = os.path.abspath(__file__) - - -def show_error_messagebox(title, message, detail_message=None): - """Function will show message and process ends after closing it.""" - from qtpy import QtWidgets, QtCore - from ayon_core import style - - app = QtWidgets.QApplication([]) - app.setStyleSheet(style.load_stylesheet()) - - msgbox = QtWidgets.QMessageBox() - msgbox.setWindowTitle(title) - msgbox.setText(message) - - if detail_message: - msgbox.setDetailedText(detail_message) - - msgbox.setWindowModality(QtCore.Qt.ApplicationModal) - msgbox.show() - - sys.exit(app.exec_()) - - -def on_invalid_args(script_not_found): - """Show to user message box saying that something went wrong. - - Tell user that arguments to launch implementation are invalid with - arguments details. - - Args: - script_not_found (bool): Use different message based on this value. - """ - - title = "Invalid arguments" - joined_args = ", ".join("\"{}\"".format(arg) for arg in sys.argv) - if script_not_found: - submsg = "Where couldn't find script path:\n\"{}\"" - else: - submsg = "Expected Host executable after script path:\n\"{}\"" - - message = "BUG: Got invalid arguments so can't launch Host application." - detail_message = "Process was launched with arguments:\n{}\n\n{}".format( - joined_args, - submsg.format(CURRENT_FILE) - ) - - show_error_messagebox(title, message, detail_message) - - -def main(argv): - # Modify current file path to find match in sys.argv which may be different - # on windows (different letter cases and slashes). - modified_current_file = CURRENT_FILE.replace("\\", "/").lower() - - # Create a copy of sys argv - sys_args = list(argv) - after_script_idx = None - # Find script path in sys.argv to know index of argv where host - # executable should be. - for idx, item in enumerate(sys_args): - if item.replace("\\", "/").lower() == modified_current_file: - after_script_idx = idx + 1 - break - - # Validate that there is at least one argument after script path - launch_args = None - if after_script_idx is not None: - launch_args = sys_args[after_script_idx:] - - host_name = os.environ["AYON_HOST_NAME"].lower() - if host_name == "photoshop": - # TODO refactor launch logic according to AE - from ayon_core.hosts.photoshop.api.lib import main - elif host_name == "aftereffects": - from ayon_core.hosts.aftereffects.api.launch_logic import main - elif host_name == "harmony": - from ayon_core.hosts.harmony.api.lib import main - else: - title = "Unknown host name" - message = ( - "BUG: Environment variable AYON_HOST_NAME contains unknown" - " host name \"{}\"" - ).format(host_name) - show_error_messagebox(title, message) - return - - if launch_args: - # Launch host implementation - main(*launch_args) - else: - # Show message box - on_invalid_args(after_script_idx is None) - - -if __name__ == "__main__": - main(sys.argv) From 2c07fb45032d28162c32661cd977c36f20058aec Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:29:16 +0100 Subject: [PATCH 10/16] removed unused 'get_non_python_host_kwargs' --- client/ayon_core/lib/applications.py | 39 ---------------------------- 1 file changed, 39 deletions(-) diff --git a/client/ayon_core/lib/applications.py b/client/ayon_core/lib/applications.py index 4bf0c31d93..68fc3ea201 100644 --- a/client/ayon_core/lib/applications.py +++ b/client/ayon_core/lib/applications.py @@ -1985,42 +1985,3 @@ def should_workfile_tool_start( if output is None: return default_output return output - - -def get_non_python_host_kwargs(kwargs, allow_console=True): - """Explicit setting of kwargs for Popen for AE/PS/Harmony. - - Expected behavior - - ayon_console opens window with logs - - ayon has stdout/stderr available for capturing - - Args: - kwargs (dict) or None - allow_console (bool): use False for inner Popen opening app itself or - it will open additional console (at least for Harmony) - """ - - if kwargs is None: - kwargs = {} - - if platform.system().lower() != "windows": - return kwargs - - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_gui_executable = "ayon_console" not in executable_filename - if is_gui_executable: - kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL - }) - elif allow_console: - kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE - }) - return kwargs From ca39fc429130942c85883d471dd5604efad235dd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:31:11 +0100 Subject: [PATCH 11/16] don't use 'get_non_python_host_kwargs' when launching harmony --- client/ayon_core/hosts/harmony/api/lib.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index bc73e19066..34cb14636d 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """Utility functions used for Avalon - Harmony integration.""" +import platform import subprocess import threading import os @@ -22,7 +23,6 @@ from .server import Server from ayon_core.tools.stdout_broker.app import StdOutBroker from ayon_core.tools.utils import host_tools from ayon_core import style -from ayon_core.lib.applications import get_non_python_host_kwargs # Setup logging. log = logging.getLogger(__name__) @@ -324,7 +324,18 @@ def launch_zip_file(filepath): return print("Launching {}".format(scene_path)) - kwargs = get_non_python_host_kwargs({}, False) + kwargs = {} + if platform.system().lower() == "windows": + executable_filename = os.path.basename( + os.getenv("AYON_EXECUTABLE", "") + ) + if "ayon_console" not in executable_filename: + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) + process = subprocess.Popen( [ProcessContext.application_path, scene_path], **kwargs From 508d4b559e7ed8db2da14c3e1959b403efb33b43 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:32:17 +0100 Subject: [PATCH 12/16] added comment with question --- client/ayon_core/hosts/harmony/api/lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index 34cb14636d..b78fe468b5 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -324,6 +324,7 @@ def launch_zip_file(filepath): return print("Launching {}".format(scene_path)) + # QUESTION Could we use 'run_detached_process' from 'ayon_core.lib'? kwargs = {} if platform.system().lower() == "windows": executable_filename = os.path.basename( From 562730fa65384e89fe1c72cd981c0e997200641a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:33:02 +0100 Subject: [PATCH 13/16] added helper function to determine if ui executable is used --- .../aftereffects/hooks/pre_launch_args.py | 14 +++----- client/ayon_core/hosts/harmony/api/lib.py | 25 +++++++------- .../hosts/harmony/hooks/pre_launch_args.py | 14 +++----- .../hosts/photoshop/hooks/pre_launch_args.py | 14 +++----- client/ayon_core/lib/__init__.py | 2 ++ client/ayon_core/lib/ayon_info.py | 34 ++++++++++++++++++- 6 files changed, 63 insertions(+), 40 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py index 2c5baa3b68..76ccd2bd4f 100644 --- a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -2,7 +2,10 @@ import os import platform import subprocess -from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib import ( + get_ayon_launcher_args, + is_using_ui_executable, +) from ayon_core.lib.applications import ( PreLaunchHook, LaunchTypes, @@ -27,14 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_in_ui_launcher = "ayon_console" not in executable_filename - if is_in_ui_launcher: + if is_using_ui_executable(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index b78fe468b5..828ee3863e 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -15,15 +15,17 @@ import json import signal import time from uuid import uuid4 -from qtpy import QtWidgets, QtCore, QtGui import collections -from .server import Server +from qtpy import QtWidgets, QtCore, QtGui +from ayon_core.lib import is_using_ui_executable from ayon_core.tools.stdout_broker.app import StdOutBroker from ayon_core.tools.utils import host_tools from ayon_core import style +from .server import Server + # Setup logging. log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) @@ -326,16 +328,15 @@ def launch_zip_file(filepath): print("Launching {}".format(scene_path)) # QUESTION Could we use 'run_detached_process' from 'ayon_core.lib'? kwargs = {} - if platform.system().lower() == "windows": - executable_filename = os.path.basename( - os.getenv("AYON_EXECUTABLE", "") - ) - if "ayon_console" not in executable_filename: - kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL - }) + if ( + platform.system().lower() == "windows" + and is_using_ui_executable() + ): + kwargs.update({ + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL + }) process = subprocess.Popen( [ProcessContext.application_path, scene_path], diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py index 1a36c890a7..c2c667c1d8 100644 --- a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -2,7 +2,10 @@ import os import platform import subprocess -from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib import ( + get_ayon_launcher_args, + is_using_ui_executable, +) from ayon_core.lib.applications import ( PreLaunchHook, LaunchTypes, @@ -27,14 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_gui_executable = "ayon_console" not in executable_filename - if is_gui_executable: + if is_using_ui_executable(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py index 0689a6c2f0..228413e1ea 100644 --- a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -2,7 +2,10 @@ import os import platform import subprocess -from ayon_core.lib import get_ayon_launcher_args +from ayon_core.lib import ( + get_ayon_launcher_args, + is_using_ui_executable, +) from ayon_core.lib.applications import ( PreLaunchHook, LaunchTypes, @@ -27,14 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - executable_path = os.environ.get("AYON_EXECUTABLE") - - executable_filename = "" - if executable_path: - executable_filename = os.path.basename(executable_path) - - is_gui_executable = "ayon_console" not in executable_filename - if is_gui_executable: + if is_using_ui_executable(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index f69fd10b07..d82fb0de0e 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -155,6 +155,7 @@ from .path_tools import ( from .ayon_info import ( is_running_from_build, + is_using_ui_executable, is_staging_enabled, is_dev_mode_enabled, is_in_tests, @@ -275,6 +276,7 @@ __all__ = [ "Logger", "is_running_from_build", + "is_using_ui_executable", "is_staging_enabled", "is_dev_mode_enabled", "is_in_tests", diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index ec37d735d8..3d4c38c99a 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -10,6 +10,12 @@ from .local_settings import get_local_site_id def get_ayon_launcher_version(): + """Get AYON launcher version. + + Returns: + str: Version string. + + """ version_filepath = os.path.join(os.environ["AYON_ROOT"], "version.py") if not os.path.exists(version_filepath): return None @@ -24,8 +30,8 @@ def is_running_from_build(): Returns: bool: True if running from build. - """ + """ executable_path = os.environ["AYON_EXECUTABLE"] executable_filename = os.path.basename(executable_path) if "python" in executable_filename.lower(): @@ -33,6 +39,32 @@ def is_running_from_build(): return True +def is_using_ui_executable(): + """AYON launcher UI windows executable is used. + + This function make sense only on Windows platform. For other platforms + always returns False. False is also returned if process is running from + code. + + AYON launcher on windows has 2 executable files. First 'ayon_console.exe' + works as 'python.exe' executable, the second 'ayon.exe' works as + 'pythonw.exe' executable. The difference is way how stdout/stderr is + handled (especially when calling subprocess). + + Returns: + bool: True if UI executable is used. + + """ + if ( + platform.system().lower() != "windows" + or is_running_from_build() + ): + return False + executable_path = os.environ["AYON_EXECUTABLE"] + executable_filename = os.path.basename(executable_path) + return "ayon_console" not in executable_filename + + def is_staging_enabled(): return os.getenv("AYON_USE_STAGING") == "1" From 5cb07274d1fea5818c0e431c99aa0b3c36a7918c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:45:27 +0100 Subject: [PATCH 14/16] renamed 'is_using_ui_executable' to 'is_using_ayon_console' --- client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py | 4 ++-- client/ayon_core/hosts/harmony/api/lib.py | 4 ++-- client/ayon_core/hosts/harmony/hooks/pre_launch_args.py | 4 ++-- client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py | 4 ++-- client/ayon_core/lib/__init__.py | 4 ++-- client/ayon_core/lib/ayon_info.py | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py index 76ccd2bd4f..959df0eb39 100644 --- a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -4,7 +4,7 @@ import subprocess from ayon_core.lib import ( get_ayon_launcher_args, - is_using_ui_executable, + is_using_ayon_console, ) from ayon_core.lib.applications import ( PreLaunchHook, @@ -30,7 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ui_executable(): + if is_using_ayon_console(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index 828ee3863e..d9c17c6214 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -19,7 +19,7 @@ import collections from qtpy import QtWidgets, QtCore, QtGui -from ayon_core.lib import is_using_ui_executable +from ayon_core.lib import is_using_ayon_console from ayon_core.tools.stdout_broker.app import StdOutBroker from ayon_core.tools.utils import host_tools from ayon_core import style @@ -330,7 +330,7 @@ def launch_zip_file(filepath): kwargs = {} if ( platform.system().lower() == "windows" - and is_using_ui_executable() + and is_using_ayon_console() ): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py index c2c667c1d8..66bc0a80d8 100644 --- a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -4,7 +4,7 @@ import subprocess from ayon_core.lib import ( get_ayon_launcher_args, - is_using_ui_executable, + is_using_ayon_console, ) from ayon_core.lib.applications import ( PreLaunchHook, @@ -30,7 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ui_executable(): + if is_using_ayon_console(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py index 228413e1ea..7fe6e7437f 100644 --- a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -4,7 +4,7 @@ import subprocess from ayon_core.lib import ( get_ayon_launcher_args, - is_using_ui_executable, + is_using_ayon_console, ) from ayon_core.lib.applications import ( PreLaunchHook, @@ -30,7 +30,7 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ui_executable(): + if is_using_ayon_console(): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index d82fb0de0e..10e290360d 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -155,7 +155,7 @@ from .path_tools import ( from .ayon_info import ( is_running_from_build, - is_using_ui_executable, + is_using_ayon_console, is_staging_enabled, is_dev_mode_enabled, is_in_tests, @@ -276,7 +276,7 @@ __all__ = [ "Logger", "is_running_from_build", - "is_using_ui_executable", + "is_using_ayon_console", "is_staging_enabled", "is_dev_mode_enabled", "is_in_tests", diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index 3d4c38c99a..c3bda70834 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -39,7 +39,7 @@ def is_running_from_build(): return True -def is_using_ui_executable(): +def is_using_ayon_console(): """AYON launcher UI windows executable is used. This function make sense only on Windows platform. For other platforms From e15460d851dd56869cbcdbec52dde1d72aef25bb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Mar 2024 16:51:03 +0100 Subject: [PATCH 15/16] fix the reversed logic --- .../hosts/aftereffects/hooks/pre_launch_args.py | 8 ++++---- client/ayon_core/hosts/harmony/api/lib.py | 2 +- .../ayon_core/hosts/harmony/hooks/pre_launch_args.py | 8 ++++---- .../ayon_core/hosts/photoshop/hooks/pre_launch_args.py | 10 +++++----- client/ayon_core/lib/ayon_info.py | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py index 959df0eb39..979d9ff3e5 100644 --- a/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/aftereffects/hooks/pre_launch_args.py @@ -32,13 +32,13 @@ def get_launch_kwargs(kwargs): if is_using_ayon_console(): kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL + "creationflags": subprocess.CREATE_NEW_CONSOLE }) else: kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL }) return kwargs diff --git a/client/ayon_core/hosts/harmony/api/lib.py b/client/ayon_core/hosts/harmony/api/lib.py index d9c17c6214..3c833c7b69 100644 --- a/client/ayon_core/hosts/harmony/api/lib.py +++ b/client/ayon_core/hosts/harmony/api/lib.py @@ -330,7 +330,7 @@ def launch_zip_file(filepath): kwargs = {} if ( platform.system().lower() == "windows" - and is_using_ayon_console() + and not is_using_ayon_console() ): kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, diff --git a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py index 66bc0a80d8..bbad14084a 100644 --- a/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/harmony/hooks/pre_launch_args.py @@ -32,13 +32,13 @@ def get_launch_kwargs(kwargs): if is_using_ayon_console(): kwargs.update({ - "creationflags": subprocess.CREATE_NO_WINDOW, - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL + "creationflags": subprocess.CREATE_NEW_CONSOLE }) else: kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE + "creationflags": subprocess.CREATE_NO_WINDOW, + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL }) return kwargs diff --git a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py index 7fe6e7437f..8358c11ca1 100644 --- a/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py +++ b/client/ayon_core/hosts/photoshop/hooks/pre_launch_args.py @@ -30,16 +30,16 @@ def get_launch_kwargs(kwargs): if platform.system().lower() != "windows": return kwargs - if is_using_ayon_console(): + if not is_using_ayon_console(): + kwargs.update({ + "creationflags": subprocess.CREATE_NEW_CONSOLE + }) + else: kwargs.update({ "creationflags": subprocess.CREATE_NO_WINDOW, "stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL }) - else: - kwargs.update({ - "creationflags": subprocess.CREATE_NEW_CONSOLE - }) return kwargs diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index c3bda70834..adb3c1befc 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -40,10 +40,10 @@ def is_running_from_build(): def is_using_ayon_console(): - """AYON launcher UI windows executable is used. + """AYON launcher console executable is used. This function make sense only on Windows platform. For other platforms - always returns False. False is also returned if process is running from + always returns True. True is also returned if process is running from code. AYON launcher on windows has 2 executable files. First 'ayon_console.exe' @@ -52,17 +52,17 @@ def is_using_ayon_console(): handled (especially when calling subprocess). Returns: - bool: True if UI executable is used. + bool: True if console executable is used. """ if ( platform.system().lower() != "windows" or is_running_from_build() ): - return False + return True executable_path = os.environ["AYON_EXECUTABLE"] executable_filename = os.path.basename(executable_path) - return "ayon_console" not in executable_filename + return "ayon_console" in executable_filename def is_staging_enabled(): From cd0a3005d3d2cfc9418c4d32fa1fbac296b4d93b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 26 Mar 2024 17:27:31 +0100 Subject: [PATCH 16/16] Fix - differentiate between host main and main --- client/ayon_core/hosts/aftereffects/api/launch_script.py | 4 ++-- client/ayon_core/hosts/harmony/api/launch_script.py | 4 ++-- client/ayon_core/hosts/photoshop/api/launch_script.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/aftereffects/api/launch_script.py b/client/ayon_core/hosts/aftereffects/api/launch_script.py index ad4e779bd0..87926c022b 100644 --- a/client/ayon_core/hosts/aftereffects/api/launch_script.py +++ b/client/ayon_core/hosts/aftereffects/api/launch_script.py @@ -8,7 +8,7 @@ workfile or others. import os import sys -from ayon_core.hosts.aftereffects.api.launch_logic import main +from ayon_core.hosts.aftereffects.api.launch_logic import main as host_main # Get current file to locate start point of sys.argv CURRENT_FILE = os.path.abspath(__file__) @@ -83,7 +83,7 @@ def main(argv): if launch_args: # Launch host implementation - main(*launch_args) + host_main(*launch_args) else: # Show message box on_invalid_args(after_script_idx is None) diff --git a/client/ayon_core/hosts/harmony/api/launch_script.py b/client/ayon_core/hosts/harmony/api/launch_script.py index 1f2c36b7e6..3c809e210f 100644 --- a/client/ayon_core/hosts/harmony/api/launch_script.py +++ b/client/ayon_core/hosts/harmony/api/launch_script.py @@ -8,7 +8,7 @@ workfile or others. import os import sys -from ayon_core.hosts.harmony.api.lib import main +from ayon_core.hosts.harmony.api.lib import main as host_main # Get current file to locate start point of sys.argv CURRENT_FILE = os.path.abspath(__file__) @@ -83,7 +83,7 @@ def main(argv): if launch_args: # Launch host implementation - main(*launch_args) + host_main(*launch_args) else: # Show message box on_invalid_args(after_script_idx is None) diff --git a/client/ayon_core/hosts/photoshop/api/launch_script.py b/client/ayon_core/hosts/photoshop/api/launch_script.py index c036b63c46..bb4de80086 100644 --- a/client/ayon_core/hosts/photoshop/api/launch_script.py +++ b/client/ayon_core/hosts/photoshop/api/launch_script.py @@ -8,7 +8,7 @@ workfile or others. import os import sys -from ayon_core.hosts.photoshop.api.lib import main +from ayon_core.hosts.photoshop.api.lib import main as host_main # Get current file to locate start point of sys.argv CURRENT_FILE = os.path.abspath(__file__) @@ -83,7 +83,7 @@ def main(argv): if launch_args: # Launch host implementation - main(*launch_args) + host_main(*launch_args) else: # Show message box on_invalid_args(after_script_idx is None)