diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index 383703e2bc..7f0636ccca 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -20,6 +20,7 @@ Logger, is_dev_mode_enabled, get_launcher_storage_dir, + is_headless_mode_enabled, ) from ayon_core.settings import get_studio_settings @@ -80,36 +81,41 @@ class ProcessPreparationError(Exception): class ProcessContext: - """Context of child process. + """Hold context of process that is going to be started. - Notes: - This class is used to pass context to child process. It can be used - to use different behavior of addon based on information in - the context. - The context can be enhanced in future versions. + Right now the context is simple, having information about addon that wants + to trigger preparation and possibly project name for which it should + happen. + + Preparation for process can be required for ayon-core or any other addon. + It can be, change of environment variables, or request login to + a project management. + + At the moment of creation is 'ProcessContext' only data holder, but that + might change in future if there will be need. Args: - addon_name (Optional[str]): Addon name which triggered process. - addon_version (Optional[str]): Addon version which triggered process. + addon_name (str): Addon name which triggered process. + addon_version (str): Addon version which triggered process. project_name (Optional[str]): Project name. Can be filled in case process is triggered for specific project. Some addons can have - different behavior based on project. - headless (Optional[bool]): Is process running in headless mode. + different behavior based on project. Value is NOT autofilled. + headless (Optional[bool]): Is process running in headless mode. Value + is filled with value based on state set in AYON launcher. """ def __init__( self, - addon_name: Optional[str] = None, - addon_version: Optional[str] = None, + addon_name: str, + addon_version: str, project_name: Optional[str] = None, headless: Optional[bool] = None, **kwargs, ): if headless is None: - # TODO use lib function to get headless mode - headless = os.getenv("AYON_HEADLESS_MODE") == "1" - self.addon_name: Optional[str] = addon_name - self.addon_version: Optional[str] = addon_version + headless = is_headless_mode_enabled() + self.addon_name: str = addon_name + self.addon_version: str = addon_version self.project_name: Optional[str] = project_name self.headless: bool = headless diff --git a/client/ayon_core/addon/utils.py b/client/ayon_core/addon/utils.py index ac5ff25984..f983e37d3c 100644 --- a/client/ayon_core/addon/utils.py +++ b/client/ayon_core/addon/utils.py @@ -72,13 +72,17 @@ def ensure_addons_are_process_context_ready( process_context: ProcessContext, addons_manager: Optional[AddonsManager] = None, exit_on_failure: bool = True, -) -> Optional[Exception]: +) -> bool: """Ensure all enabled addons are ready to be used in the given context. Call this method only in AYON launcher process and as first thing to avoid possible clashes with preparation. For example 'QApplication' should not be created. + Todos: + Run all preparations and allow to "ignore" failed preparations. + Right now single addon can block using certain actions. + Args: process_context (ProcessContext): The context in which the addons should be prepared. @@ -88,14 +92,12 @@ def ensure_addons_are_process_context_ready( if an error occurs. Defaults to True. Returns: - Optional[Exception]: The exception that occurred during the - preparation, if any. + bool: True if all addons are ready, False otherwise. """ if addons_manager is None: addons_manager = AddonsManager() - exception = None message = None failed = False use_detail = False @@ -112,13 +114,11 @@ def ensure_addons_are_process_context_ready( addon.ensure_is_process_ready(process_context) addon_failed = False except ProcessPreparationError as exc: - exception = exc message = str(exc) print(f"Addon preparation failed: '{addon.name}'") print(message) - except BaseException as exc: - exception = exc + except BaseException: use_detail = True message = "An unexpected error occurred." formatted_traceback = "".join(traceback.format_exception( @@ -140,7 +140,7 @@ def ensure_addons_are_process_context_ready( if not failed: if not process_context.headless: _start_tray() - return None + return True detail = None if use_detail: @@ -150,16 +150,21 @@ def ensure_addons_are_process_context_ready( detail = output_str _handle_error(process_context, message, detail) - if not exit_on_failure: - return exception - sys.exit(1) + if exit_on_failure: + sys.exit(1) + return False def ensure_addons_are_process_ready( + addon_name: str, + addon_version: str, + project_name: Optional[str] = None, + headless: Optional[bool] = None, + *, addons_manager: Optional[AddonsManager] = None, exit_on_failure: bool = True, **kwargs, -) -> Optional[Exception]: +) -> bool: """Ensure all enabled addons are ready to be used in the given context. Call this method only in AYON launcher process and as first thing @@ -167,6 +172,13 @@ def ensure_addons_are_process_ready( should not be created. Args: + addon_name (str): Addon name which triggered process. + addon_version (str): Addon version which triggered process. + project_name (Optional[str]): Project name. Can be filled in case + process is triggered for specific project. Some addons can have + different behavior based on project. Value is NOT autofilled. + headless (Optional[bool]): Is process running in headless mode. Value + is filled with value based on state set in AYON launcher. addons_manager (Optional[AddonsManager]): The addons manager to use. If not provided, a new one will be created. exit_on_failure (bool, optional): If True, the process will exit @@ -174,11 +186,16 @@ def ensure_addons_are_process_ready( kwargs: The keyword arguments to pass to the ProcessContext. Returns: - Optional[Exception]: The exception that occurred during the - preparation, if any. + bool: True if all addons are ready, False otherwise. """ - context: ProcessContext = ProcessContext(**kwargs) + context: ProcessContext = ProcessContext( + addon_name, + addon_version, + project_name, + headless, + **kwargs + ) return ensure_addons_are_process_context_ready( context, addons_manager, exit_on_failure ) diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index d4b161031e..0074c4d2bd 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -132,6 +132,7 @@ is_in_ayon_launcher_process, is_running_from_build, is_using_ayon_console, + is_headless_mode_enabled, is_staging_enabled, is_dev_mode_enabled, is_in_tests, @@ -245,6 +246,7 @@ "is_in_ayon_launcher_process", "is_running_from_build", "is_using_ayon_console", + "is_headless_mode_enabled", "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 c4333fab95..7e194a824e 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -78,6 +78,10 @@ def is_using_ayon_console(): return "ayon_console" in executable_filename +def is_headless_mode_enabled(): + return os.getenv("AYON_HEADLESS_MODE") == "1" + + def is_staging_enabled(): return os.getenv("AYON_USE_STAGING") == "1"