Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare process: Enhancements #849

Merged
merged 16 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions client/ayon_core/addon/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
47 changes: 32 additions & 15 deletions client/ayon_core/addon/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -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:
Expand All @@ -150,35 +150,52 @@ 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.
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

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.

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
if an error occurs. Defaults to True.
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
)
2 changes: 2 additions & 0 deletions client/ayon_core/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions client/ayon_core/lib/ayon_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down