From 04a2f08fea82ce4f546f673d4859f6a6116712ae Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 3 Apr 2025 18:05:54 +0200 Subject: [PATCH 01/64] Added new Pre and Post Loader Hooks Added functions to register/deregister them. --- client/ayon_core/pipeline/__init__.py | 24 ++++++++ client/ayon_core/pipeline/load/__init__.py | 24 ++++++++ client/ayon_core/pipeline/load/plugins.py | 66 ++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index 41bcd0dbd1..c6903f1b8e 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -42,6 +42,18 @@ register_loader_plugin_path, deregister_loader_plugin, + discover_loader_pre_hook_plugin, + register_loader_pre_hook_plugin, + deregister_loader_pre_hook_plugin, + register_loader_pre_hook_plugin_path, + deregister_loader_pre_hook_plugin_path, + + discover_loader_post_hook_plugin, + register_loader_post_hook_plugin, + deregister_loader_post_hook_plugin, + register_loader_post_hook_plugin_path, + deregister_loader_post_hook_plugin_path, + load_container, remove_container, update_container, @@ -160,6 +172,18 @@ "register_loader_plugin_path", "deregister_loader_plugin", + "discover_loader_pre_hook_plugin", + "register_loader_pre_hook_plugin", + "deregister_loader_pre_hook_plugin", + "register_loader_pre_hook_plugin_path", + "deregister_loader_pre_hook_plugin_path", + + "discover_loader_post_hook_plugin", + "register_loader_post_hook_plugin", + "deregister_loader_post_hook_plugin", + "register_loader_post_hook_plugin_path", + "deregister_loader_post_hook_plugin_path", + "load_container", "remove_container", "update_container", diff --git a/client/ayon_core/pipeline/load/__init__.py b/client/ayon_core/pipeline/load/__init__.py index bdc5ece620..fdbcaec1b9 100644 --- a/client/ayon_core/pipeline/load/__init__.py +++ b/client/ayon_core/pipeline/load/__init__.py @@ -49,6 +49,18 @@ deregister_loader_plugin_path, register_loader_plugin_path, deregister_loader_plugin, + + discover_loader_pre_hook_plugin, + register_loader_pre_hook_plugin, + deregister_loader_pre_hook_plugin, + register_loader_pre_hook_plugin_path, + deregister_loader_pre_hook_plugin_path, + + discover_loader_post_hook_plugin, + register_loader_post_hook_plugin, + deregister_loader_post_hook_plugin, + register_loader_post_hook_plugin_path, + deregister_loader_post_hook_plugin_path, ) @@ -103,4 +115,16 @@ "deregister_loader_plugin_path", "register_loader_plugin_path", "deregister_loader_plugin", + + "discover_loader_pre_hook_plugin", + "register_loader_pre_hook_plugin", + "deregister_loader_pre_hook_plugin", + "register_loader_pre_hook_plugin_path", + "deregister_loader_pre_hook_plugin_path", + + "discover_loader_post_hook_plugin", + "register_loader_post_hook_plugin", + "deregister_loader_post_hook_plugin", + "register_loader_post_hook_plugin_path", + "deregister_loader_post_hook_plugin_path", ) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index b601914acd..553e88c2f7 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,5 +1,6 @@ import os import logging +from typing import ClassVar from ayon_core.settings import get_project_settings from ayon_core.pipeline.plugin_discover import ( @@ -264,6 +265,30 @@ class ProductLoaderPlugin(LoaderPlugin): """ +class PreLoadHookPlugin: + """Plugin that should be run before any Loaders in 'loaders' + + Should be used as non-invasive method to enrich core loading process. + Any external studio might want to modify loaded data before or afte + they are loaded without need to override existing core plugins. + """ + loaders: ClassVar[set[str]] + + def process(self, context, name=None, namespace=None, options=None): + pass + +class PostLoadHookPlugin: + """Plugin that should be run after any Loaders in 'loaders' + + Should be used as non-invasive method to enrich core loading process. + Any external studio might want to modify loaded data before or afte + they are loaded without need to override existing core plugins. + loaders: ClassVar[set[str]] + """ + def process(self, context, name=None, namespace=None, options=None): + pass + + def discover_loader_plugins(project_name=None): from ayon_core.lib import Logger from ayon_core.pipeline import get_current_project_name @@ -300,3 +325,44 @@ def deregister_loader_plugin_path(path): def register_loader_plugin_path(path): return register_plugin_path(LoaderPlugin, path) + + +def discover_loader_pre_hook_plugin(project_name=None): + plugins = discover(PreLoadHookPlugin) + return plugins + +def register_loader_pre_hook_plugin(plugin): + return register_plugin(PreLoadHookPlugin, plugin) + + +def deregister_loader_pre_hook_plugin(plugin): + deregister_plugin(PreLoadHookPlugin, plugin) + + +def register_loader_pre_hook_plugin_path(path): + return register_plugin_path(PreLoadHookPlugin, path) + + +def deregister_loader_pre_hook_plugin_path(path): + deregister_plugin_path(PreLoadHookPlugin, path) + + +def discover_loader_post_hook_plugin(): + plugins = discover(PostLoadHookPlugin) + return plugins + + +def register_loader_post_hook_plugin(plugin): + return register_plugin(PostLoadHookPlugin, plugin) + + +def deregister_loader_post_hook_plugin(plugin): + deregister_plugin(PostLoadHookPlugin, plugin) + + +def register_loader_post_hook_plugin_path(path): + return register_plugin_path(PostLoadHookPlugin, path) + + +def deregister_loader_post_hook_plugin_path(path): + deregister_plugin_path(PostLoadHookPlugin, path) From 7ec99b3715e800643a373eb20ccc9df7301ca2ec Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 3 Apr 2025 18:06:31 +0200 Subject: [PATCH 02/64] Initial implementation of pre/post loader hooks --- .../ayon_core/tools/loader/models/actions.py | 110 ++++++++++++++++-- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index cfe91cadab..c188bf1609 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -9,6 +9,7 @@ from ayon_core.lib import NestedCacheItem from ayon_core.pipeline.load import ( discover_loader_plugins, + discover_loader_pre_hook_plugin, ProductLoaderPlugin, filter_repre_contexts_by_loader, get_loader_identifier, @@ -17,6 +18,8 @@ load_with_product_contexts, LoadError, IncompatibleLoaderError, + get_loaders_by_name + ) from ayon_core.tools.loader.abstract import ActionItem @@ -50,6 +53,8 @@ def __init__(self, controller): levels=1, lifetime=self.loaders_cache_lifetime) self._repre_loaders = NestedCacheItem( levels=1, lifetime=self.loaders_cache_lifetime) + self._hook_loaders_by_identifier = NestedCacheItem( + levels=1, lifetime=self.loaders_cache_lifetime) def reset(self): """Reset the model with all cached items.""" @@ -58,6 +63,7 @@ def reset(self): self._loaders_by_identifier.reset() self._product_loaders.reset() self._repre_loaders.reset() + self._hook_loaders_by_identifier.reset() def get_versions_action_items(self, project_name, version_ids): """Get action items for given version ids. @@ -143,12 +149,14 @@ def trigger_action_item( ACTIONS_MODEL_SENDER, ) loader = self._get_loader_by_identifier(project_name, identifier) + hooks = self._get_hook_loaders_by_identifier(project_name, identifier) if representation_ids is not None: error_info = self._trigger_representation_loader( loader, options, project_name, representation_ids, + hooks ) elif version_ids is not None: error_info = self._trigger_version_loader( @@ -156,6 +164,7 @@ def trigger_action_item( options, project_name, version_ids, + hooks ) else: raise NotImplementedError( @@ -307,22 +316,40 @@ def _get_loaders(self, project_name): we want to show loaders for? Returns: - tuple[list[ProductLoaderPlugin], list[LoaderPlugin]]: Discovered - loader plugins. + tuple( + list[ProductLoaderPlugin], + list[LoaderPlugin], + ): Discovered loader plugins. """ loaders_by_identifier_c = self._loaders_by_identifier[project_name] product_loaders_c = self._product_loaders[project_name] repre_loaders_c = self._repre_loaders[project_name] + hook_loaders_by_identifier_c = self._hook_loaders_by_identifier[project_name] if loaders_by_identifier_c.is_valid: - return product_loaders_c.get_data(), repre_loaders_c.get_data() + return ( + product_loaders_c.get_data(), + repre_loaders_c.get_data(), + hook_loaders_by_identifier_c.get_data() + ) # Get all representation->loader combinations available for the # index under the cursor, so we can list the user the options. available_loaders = self._filter_loaders_by_tool_name( project_name, discover_loader_plugins(project_name) ) - + hook_loaders_by_identifier = {} + pre_load_hook_plugins = discover_loader_pre_hook_plugin(project_name) + loaders_by_name = get_loaders_by_name() + for hook_plugin in pre_load_hook_plugins: + for load_plugin_name in hook_plugin.loaders: + load_plugin = loaders_by_name.get(load_plugin_name) + if not load_plugin: + continue + if not load_plugin.enabled: + continue + identifier = get_loader_identifier(load_plugin) + hook_loaders_by_identifier.setdefault(identifier, {}).setdefault("pre", []).append(hook_plugin) repre_loaders = [] product_loaders = [] loaders_by_identifier = {} @@ -340,6 +367,8 @@ def _get_loaders(self, project_name): loaders_by_identifier_c.update_data(loaders_by_identifier) product_loaders_c.update_data(product_loaders) repre_loaders_c.update_data(repre_loaders) + hook_loaders_by_identifier_c.update_data(hook_loaders_by_identifier) + return product_loaders, repre_loaders def _get_loader_by_identifier(self, project_name, identifier): @@ -349,6 +378,13 @@ def _get_loader_by_identifier(self, project_name, identifier): loaders_by_identifier = loaders_by_identifier_c.get_data() return loaders_by_identifier.get(identifier) + def _get_hook_loaders_by_identifier(self, project_name, identifier): + if not self._hook_loaders_by_identifier[project_name].is_valid: + self._get_loaders(project_name) + hook_loaders_by_identifier_c = self._hook_loaders_by_identifier[project_name] + hook_loaders_by_identifier_c = hook_loaders_by_identifier_c.get_data() + return hook_loaders_by_identifier_c.get(identifier) + def _actions_sorter(self, action_item): """Sort the Loaders by their order and then their name. @@ -606,6 +642,7 @@ def _trigger_version_loader( options, project_name, version_ids, + hooks=None ): """Trigger version loader. @@ -655,7 +692,7 @@ def _trigger_version_loader( }) return self._load_products_by_loader( - loader, product_contexts, options + loader, product_contexts, options, hooks=hooks ) def _trigger_representation_loader( @@ -664,6 +701,7 @@ def _trigger_representation_loader( options, project_name, representation_ids, + hooks ): """Trigger representation loader. @@ -716,10 +754,16 @@ def _trigger_representation_loader( }) return self._load_representations_by_loader( - loader, repre_contexts, options + loader, repre_contexts, options, hooks ) - def _load_representations_by_loader(self, loader, repre_contexts, options): + def _load_representations_by_loader( + self, + loader, + repre_contexts, + options, + hooks=None + ): """Loops through list of repre_contexts and loads them with one loader Args: @@ -737,12 +781,26 @@ def _load_representations_by_loader(self, loader, repre_contexts, options): if version < 0: version = "Hero" try: + for hook_plugin in hooks.get("pre", []): + hook_plugin.process( + loader, + repre_context, + options=options + ) + load_with_repre_context( loader, repre_context, options=options ) + for hook_plugin in hooks.get("post", []): + hook_plugin.process( + loader, + repre_context, + options=options + ) + except IncompatibleLoaderError as exc: print(exc) error_info.append(( @@ -770,7 +828,13 @@ def _load_representations_by_loader(self, loader, repre_contexts, options): )) return error_info - def _load_products_by_loader(self, loader, version_contexts, options): + def _load_products_by_loader( + self, + loader, + version_contexts, + options, + hooks=None + ): """Triggers load with ProductLoader type of loaders. Warning: @@ -791,12 +855,26 @@ def _load_products_by_loader(self, loader, version_contexts, options): product_name = context.get("product", {}).get("name") or "N/A" product_names.append(product_name) try: + for hook_plugin in hooks.get("pre", []): + hook_plugin.process( + loader, + version_contexts, + options=options + ) + load_with_product_contexts( loader, version_contexts, - options=options + options=options, ) + for hook_plugin in hooks.get("post", []): + hook_plugin.process( + loader, + version_contexts, + options=options + ) + except Exception as exc: formatted_traceback = None if not isinstance(exc, LoadError): @@ -817,12 +895,26 @@ def _load_products_by_loader(self, loader, version_contexts, options): version_context.get("product", {}).get("name") or "N/A" ) try: + for hook_plugin in hooks.get("pre", []): + hook_plugin.process( + loader, + version_contexts, + options=options + ) + load_with_product_context( loader, version_context, options=options ) + for hook_plugin in hooks.get("post", []): + hook_plugin.process( + loader, + version_context, + options=options + ) + except Exception as exc: formatted_traceback = None if not isinstance(exc, LoadError): From e76691ea6e62473a923e9ae576d161a418f5e288 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 15:39:32 +0200 Subject: [PATCH 03/64] Moved usage of hooks out of UI actions to utils It is assumed that utils could be used in some kind 'load API', `actions` are tightly bound to UI loading. --- client/ayon_core/pipeline/load/utils.py | 74 +++++++++++++++++-- .../ayon_core/tools/loader/models/actions.py | 49 ++---------- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index de8e1676e7..d280aa3407 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -288,7 +288,13 @@ def get_representation_context(project_name, representation): def load_with_repre_context( - Loader, repre_context, namespace=None, name=None, options=None, **kwargs + Loader, + repre_context, + namespace=None, + name=None, + options=None, + hooks=None, + **kwargs ): # Ensure the Loader is compatible for the representation @@ -322,11 +328,24 @@ def load_with_repre_context( # Deprecated - to be removed in OpenPype 3.16.6 or 3.17.0. loader._fname = get_representation_path_from_context(repre_context) - return loader.load(repre_context, name, namespace, options) + return _load_context( + Loader, + repre_context, + name, + namespace, + options, + hooks + ) def load_with_product_context( - Loader, product_context, namespace=None, name=None, options=None, **kwargs + Loader, + product_context, + namespace=None, + name=None, + options=None, + hooks=None, + **kwargs ): # Ensure options is a dictionary when no explicit options provided @@ -344,12 +363,24 @@ def load_with_product_context( Loader.__name__, product_context["folder"]["path"] ) ) - - return Loader().load(product_context, name, namespace, options) + return _load_context( + Loader, + product_context, + name, + namespace, + options, + hooks + ) def load_with_product_contexts( - Loader, product_contexts, namespace=None, name=None, options=None, **kwargs + Loader, + product_contexts, + namespace=None, + name=None, + options=None, + hooks=None, + **kwargs ): # Ensure options is a dictionary when no explicit options provided @@ -371,8 +402,37 @@ def load_with_product_contexts( Loader.__name__, joined_product_names ) ) + return _load_context( + Loader, + product_contexts, + name, + namespace, + options, + hooks + ) + + +def _load_context(Loader, contexts, hooks, name, namespace, options): + """Helper function to wrap hooks around generic load function. - return Loader().load(product_contexts, name, namespace, options) + Only dynamic part is different context(s) to be loaded. + """ + for hook_plugin in hooks.get("pre", []): + hook_plugin.process( + contexts, + name, + namespace, + options, + ) + load_return = Loader().load(contexts, name, namespace, options) + for hook_plugin in hooks.get("post", []): + hook_plugin.process( + contexts, + name, + namespace, + options, + ) + return load_return def load_container( diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index c188bf1609..7151dbdaec 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -781,26 +781,14 @@ def _load_representations_by_loader( if version < 0: version = "Hero" try: - for hook_plugin in hooks.get("pre", []): - hook_plugin.process( - loader, - repre_context, - options=options - ) load_with_repre_context( loader, repre_context, - options=options + options=options, + hooks=hooks ) - for hook_plugin in hooks.get("post", []): - hook_plugin.process( - loader, - repre_context, - options=options - ) - except IncompatibleLoaderError as exc: print(exc) error_info.append(( @@ -855,26 +843,12 @@ def _load_products_by_loader( product_name = context.get("product", {}).get("name") or "N/A" product_names.append(product_name) try: - for hook_plugin in hooks.get("pre", []): - hook_plugin.process( - loader, - version_contexts, - options=options - ) - load_with_product_contexts( loader, version_contexts, options=options, + hooks=hooks ) - - for hook_plugin in hooks.get("post", []): - hook_plugin.process( - loader, - version_contexts, - options=options - ) - except Exception as exc: formatted_traceback = None if not isinstance(exc, LoadError): @@ -895,26 +869,13 @@ def _load_products_by_loader( version_context.get("product", {}).get("name") or "N/A" ) try: - for hook_plugin in hooks.get("pre", []): - hook_plugin.process( - loader, - version_contexts, - options=options - ) - load_with_product_context( loader, version_context, - options=options + options=options, + hooks=hooks ) - for hook_plugin in hooks.get("post", []): - hook_plugin.process( - loader, - version_context, - options=options - ) - except Exception as exc: formatted_traceback = None if not isinstance(exc, LoadError): From f10564ffb83dce99bd623aa10b5cb622f6d9ffa5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 16:25:59 +0200 Subject: [PATCH 04/64] Removed unnecessary functions --- client/ayon_core/pipeline/load/__init__.py | 4 ---- client/ayon_core/pipeline/load/plugins.py | 9 --------- 2 files changed, 13 deletions(-) diff --git a/client/ayon_core/pipeline/load/__init__.py b/client/ayon_core/pipeline/load/__init__.py index fdbcaec1b9..7f9734f94e 100644 --- a/client/ayon_core/pipeline/load/__init__.py +++ b/client/ayon_core/pipeline/load/__init__.py @@ -50,13 +50,11 @@ register_loader_plugin_path, deregister_loader_plugin, - discover_loader_pre_hook_plugin, register_loader_pre_hook_plugin, deregister_loader_pre_hook_plugin, register_loader_pre_hook_plugin_path, deregister_loader_pre_hook_plugin_path, - discover_loader_post_hook_plugin, register_loader_post_hook_plugin, deregister_loader_post_hook_plugin, register_loader_post_hook_plugin_path, @@ -116,13 +114,11 @@ "register_loader_plugin_path", "deregister_loader_plugin", - "discover_loader_pre_hook_plugin", "register_loader_pre_hook_plugin", "deregister_loader_pre_hook_plugin", "register_loader_pre_hook_plugin_path", "deregister_loader_pre_hook_plugin_path", - "discover_loader_post_hook_plugin", "register_loader_post_hook_plugin", "deregister_loader_post_hook_plugin", "register_loader_post_hook_plugin_path", diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 553e88c2f7..6b1591221a 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -327,10 +327,6 @@ def register_loader_plugin_path(path): return register_plugin_path(LoaderPlugin, path) -def discover_loader_pre_hook_plugin(project_name=None): - plugins = discover(PreLoadHookPlugin) - return plugins - def register_loader_pre_hook_plugin(plugin): return register_plugin(PreLoadHookPlugin, plugin) @@ -347,11 +343,6 @@ def deregister_loader_pre_hook_plugin_path(path): deregister_plugin_path(PreLoadHookPlugin, path) -def discover_loader_post_hook_plugin(): - plugins = discover(PostLoadHookPlugin) - return plugins - - def register_loader_post_hook_plugin(plugin): return register_plugin(PostLoadHookPlugin, plugin) From 92600726b3e9e08a5f3f7f7d63d3919cf436b011 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 16:27:50 +0200 Subject: [PATCH 05/64] Extracted get_hook_loaders_by_identifier to utils --- client/ayon_core/pipeline/load/__init__.py | 2 ++ client/ayon_core/pipeline/load/utils.py | 32 +++++++++++++++++++ .../ayon_core/tools/loader/models/actions.py | 16 ++-------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/pipeline/load/__init__.py b/client/ayon_core/pipeline/load/__init__.py index 7f9734f94e..eaba8cd78d 100644 --- a/client/ayon_core/pipeline/load/__init__.py +++ b/client/ayon_core/pipeline/load/__init__.py @@ -24,6 +24,7 @@ get_loader_identifier, get_loaders_by_name, + get_hook_loaders_by_identifier, get_representation_path_from_context, get_representation_path, @@ -89,6 +90,7 @@ "get_loader_identifier", "get_loaders_by_name", + "get_hook_loaders_by_identifier", "get_representation_path_from_context", "get_representation_path", diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index d280aa3407..9d3635a186 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -16,6 +16,7 @@ ) from ayon_core.pipeline import ( Anatomy, + discover ) log = logging.getLogger(__name__) @@ -1149,3 +1150,34 @@ def filter_containers(containers, project_name): uptodate_containers.append(container) return output + + +def get_hook_loaders_by_identifier(): + """Discovers pre/post hooks for loader plugins. + + Returns: + (dict) {"LoaderName": {"pre": ["PreLoader1"], "post":["PreLoader2]} + """ + # beware of circular imports! + from .plugins import PreLoadHookPlugin, PostLoadHookPlugin + hook_loaders_by_identifier = {} + _get_hook_loaders(hook_loaders_by_identifier, PreLoadHookPlugin, "pre") + _get_hook_loaders(hook_loaders_by_identifier, PostLoadHookPlugin, "post") + return hook_loaders_by_identifier + + +def _get_hook_loaders(hook_loaders_by_identifier, loader_plugin, loader_type): + load_hook_plugins = discover(loader_plugin) + loaders_by_name = get_loaders_by_name() + for hook_plugin in load_hook_plugins: + for load_plugin_name in hook_plugin.loaders: + load_plugin = loaders_by_name.get(load_plugin_name) + if not load_plugin: + continue + if not load_plugin.enabled: + continue + identifier = get_loader_identifier(load_plugin) + (hook_loaders_by_identifier.setdefault(identifier, {}) + .setdefault(loader_type, []).append( + hook_plugin) + ) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 7151dbdaec..71bd0c1289 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -18,7 +18,8 @@ load_with_product_contexts, LoadError, IncompatibleLoaderError, - get_loaders_by_name + get_loaders_by_name, + get_hook_loaders_by_identifier ) from ayon_core.tools.loader.abstract import ActionItem @@ -338,18 +339,7 @@ def _get_loaders(self, project_name): available_loaders = self._filter_loaders_by_tool_name( project_name, discover_loader_plugins(project_name) ) - hook_loaders_by_identifier = {} - pre_load_hook_plugins = discover_loader_pre_hook_plugin(project_name) - loaders_by_name = get_loaders_by_name() - for hook_plugin in pre_load_hook_plugins: - for load_plugin_name in hook_plugin.loaders: - load_plugin = loaders_by_name.get(load_plugin_name) - if not load_plugin: - continue - if not load_plugin.enabled: - continue - identifier = get_loader_identifier(load_plugin) - hook_loaders_by_identifier.setdefault(identifier, {}).setdefault("pre", []).append(hook_plugin) + hook_loaders_by_identifier = get_hook_loaders_by_identifier() repre_loaders = [] product_loaders = [] loaders_by_identifier = {} From 969358d37a73abcbaf09df63137260b089a732df Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 16:30:38 +0200 Subject: [PATCH 06/64] Removed missed unnecessary functions --- client/ayon_core/pipeline/__init__.py | 4 ---- client/ayon_core/tools/loader/models/actions.py | 1 - 2 files changed, 5 deletions(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index c6903f1b8e..beb5fa5ac2 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -42,13 +42,11 @@ register_loader_plugin_path, deregister_loader_plugin, - discover_loader_pre_hook_plugin, register_loader_pre_hook_plugin, deregister_loader_pre_hook_plugin, register_loader_pre_hook_plugin_path, deregister_loader_pre_hook_plugin_path, - discover_loader_post_hook_plugin, register_loader_post_hook_plugin, deregister_loader_post_hook_plugin, register_loader_post_hook_plugin_path, @@ -172,13 +170,11 @@ "register_loader_plugin_path", "deregister_loader_plugin", - "discover_loader_pre_hook_plugin", "register_loader_pre_hook_plugin", "deregister_loader_pre_hook_plugin", "register_loader_pre_hook_plugin_path", "deregister_loader_pre_hook_plugin_path", - "discover_loader_post_hook_plugin", "register_loader_post_hook_plugin", "deregister_loader_post_hook_plugin", "register_loader_post_hook_plugin_path", diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 71bd0c1289..07195f4b05 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -9,7 +9,6 @@ from ayon_core.lib import NestedCacheItem from ayon_core.pipeline.load import ( discover_loader_plugins, - discover_loader_pre_hook_plugin, ProductLoaderPlugin, filter_repre_contexts_by_loader, get_loader_identifier, From 42d869973c40ae8508d620fc904ce29ea5909e10 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 16:42:23 +0200 Subject: [PATCH 07/64] Fixed circular import --- client/ayon_core/pipeline/load/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 9d3635a186..910ffb2eac 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -16,7 +16,6 @@ ) from ayon_core.pipeline import ( Anatomy, - discover ) log = logging.getLogger(__name__) @@ -413,7 +412,7 @@ def load_with_product_contexts( ) -def _load_context(Loader, contexts, hooks, name, namespace, options): +def _load_context(Loader, contexts, name, namespace, options, hooks): """Helper function to wrap hooks around generic load function. Only dynamic part is different context(s) to be loaded. @@ -1160,6 +1159,7 @@ def get_hook_loaders_by_identifier(): """ # beware of circular imports! from .plugins import PreLoadHookPlugin, PostLoadHookPlugin + hook_loaders_by_identifier = {} _get_hook_loaders(hook_loaders_by_identifier, PreLoadHookPlugin, "pre") _get_hook_loaders(hook_loaders_by_identifier, PostLoadHookPlugin, "post") @@ -1167,6 +1167,8 @@ def get_hook_loaders_by_identifier(): def _get_hook_loaders(hook_loaders_by_identifier, loader_plugin, loader_type): + from ..plugin_discover import discover + load_hook_plugins = discover(loader_plugin) loaders_by_name = get_loaders_by_name() for hook_plugin in load_hook_plugins: From 3961f8a5e2282834d6852d49192527365d0e8b8c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:07:53 +0200 Subject: [PATCH 08/64] Added get_hook_loaders_by_identifier to pipeline API --- client/ayon_core/pipeline/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index beb5fa5ac2..0a805b16dc 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -61,6 +61,7 @@ get_representation_path, get_representation_context, get_repres_contexts, + get_hook_loaders_by_identifier ) from .publish import ( @@ -240,6 +241,8 @@ "register_workfile_build_plugin_path", "deregister_workfile_build_plugin_path", + "get_hook_loaders_by_identifier", + # Backwards compatible function names "install", "uninstall", From a963f8c137896ec326695c01fdefa509cde9d510 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:08:13 +0200 Subject: [PATCH 09/64] Added get_hook_loaders_by_identifier SceneInventory controller --- client/ayon_core/tools/sceneinventory/control.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 60d9bc77a9..acbd67b14d 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -5,6 +5,7 @@ from ayon_core.pipeline import ( registered_host, get_current_context, + get_hook_loaders_by_identifier ) from ayon_core.tools.common_models import HierarchyModel, ProjectsModel @@ -35,6 +36,8 @@ def __init__(self, host=None): self._projects_model = ProjectsModel(self) self._event_system = self._create_event_system() + self._hooks_by_identifier = get_hook_loaders_by_identifier() + def get_host(self) -> HostBase: return self._host @@ -115,6 +118,10 @@ def get_version_items(self, project_name, product_ids): return self._containers_model.get_version_items( project_name, product_ids) + def get_hook_loaders_by_identifier(self): + """Returns lists of pre|post hooks per Loader identifier.""" + return self._hooks_by_identifier + # Site Sync methods def is_sitesync_enabled(self): return self._sitesync_model.is_sitesync_enabled() From 761ef4b4af343496c42a2236b373069210b0758f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:08:33 +0200 Subject: [PATCH 10/64] Added update methods for Loader Hooks --- client/ayon_core/pipeline/load/plugins.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 6b1591221a..af38cf4c45 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -277,6 +277,10 @@ class PreLoadHookPlugin: def process(self, context, name=None, namespace=None, options=None): pass + def update(self, container, context): + pass + + class PostLoadHookPlugin: """Plugin that should be run after any Loaders in 'loaders' @@ -288,6 +292,9 @@ class PostLoadHookPlugin: def process(self, context, name=None, namespace=None, options=None): pass + def update(self, container, context): + pass + def discover_loader_plugins(project_name=None): from ayon_core.lib import Logger From f44b81a7e58c645481b9653378ece6a72f09f154 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:09:06 +0200 Subject: [PATCH 11/64] Pass hook_loaders_by_id in SceneInventory view --- client/ayon_core/tools/sceneinventory/view.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index bb95e37d4e..75d3d9a680 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -1100,11 +1100,16 @@ def _update_containers(self, item_ids, versions): containers_by_id = self._controller.get_containers_by_item_ids( item_ids ) + hook_loaders_by_id = self._controller.get_hook_loaders_by_identifier() try: for item_id, item_version in zip(item_ids, versions): container = containers_by_id[item_id] try: - update_container(container, item_version) + update_container( + container, + item_version, + hook_loaders_by_id + ) except AssertionError: log.warning("Update failed", exc_info=True) self._show_version_error_dialog( From a5c10767fd3352aa2c7772d16b0e04d094b198fd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:09:32 +0200 Subject: [PATCH 12/64] Added loader hooks to update_container --- client/ayon_core/pipeline/load/utils.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 910ffb2eac..abef1bc500 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -520,7 +520,7 @@ def remove_container(container): return Loader().remove(container) -def update_container(container, version=-1): +def update_container(container, version=-1, hooks_by_identifier=None): """Update a container""" from ayon_core.pipeline import get_current_project_name @@ -616,7 +616,20 @@ def update_container(container, version=-1): if not path or not os.path.exists(path): raise ValueError("Path {} doesn't exist".format(path)) - return Loader().update(container, context) + loader_identifier = get_loader_identifier(Loader) + hooks = hooks_by_identifier.get(loader_identifier, {}) + for hook_plugin in hooks.get("pre", []): + hook_plugin.update( + context, + container + ) + update_return = Loader().update(container, context) + for hook_plugin in hooks.get("post", []): + hook_plugin.update( + context, + container + ) + return update_return def switch_container(container, representation, loader_plugin=None): From 82ae29dc12ce92ee748b9c19feab40dc80c4b877 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:10:47 +0200 Subject: [PATCH 13/64] Lazy initialization of get_hook_loaders_by_identifier --- client/ayon_core/tools/sceneinventory/control.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index acbd67b14d..e6fb459129 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -36,7 +36,7 @@ def __init__(self, host=None): self._projects_model = ProjectsModel(self) self._event_system = self._create_event_system() - self._hooks_by_identifier = get_hook_loaders_by_identifier() + self._hooks_by_identifier = None def get_host(self) -> HostBase: return self._host @@ -120,6 +120,8 @@ def get_version_items(self, project_name, product_ids): def get_hook_loaders_by_identifier(self): """Returns lists of pre|post hooks per Loader identifier.""" + if self._hooks_by_identifier is None: + self._hooks_by_identifier = get_hook_loaders_by_identifier() return self._hooks_by_identifier # Site Sync methods From f609d2f5c44e2caf18b7f512e207b2eb6aeab856 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:19:24 +0200 Subject: [PATCH 14/64] Added switch methods to Loader Hooks --- client/ayon_core/pipeline/load/plugins.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index af38cf4c45..e930f034ce 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -280,6 +280,9 @@ def process(self, context, name=None, namespace=None, options=None): def update(self, container, context): pass + def switch(self, container, context): + pass + class PostLoadHookPlugin: """Plugin that should be run after any Loaders in 'loaders' @@ -295,6 +298,9 @@ def process(self, context, name=None, namespace=None, options=None): def update(self, container, context): pass + def switch(self, container, context): + pass + def discover_loader_plugins(project_name=None): from ayon_core.lib import Logger From 3a2d831ca632fdbfc93bf2789bdc7501424d5262 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Apr 2025 17:20:09 +0200 Subject: [PATCH 15/64] Added Loader Hooks to switch_container --- client/ayon_core/pipeline/load/utils.py | 27 ++++++++++++++++--- .../sceneinventory/switch_dialog/dialog.py | 8 +++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index abef1bc500..94e7ad53a6 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -632,15 +632,22 @@ def update_container(container, version=-1, hooks_by_identifier=None): return update_return -def switch_container(container, representation, loader_plugin=None): +def switch_container( + container, + representation, + loader_plugin=None, + hooks_by_identifier=None +): """Switch a container to representation Args: container (dict): container information representation (dict): representation entity + loader_plugin (LoaderPlugin) + hooks_by_identifier (dict): {"pre": [PreHookPlugin1], "post":[]} Returns: - function call + return from function call """ from ayon_core.pipeline import get_current_project_name @@ -679,7 +686,21 @@ def switch_container(container, representation, loader_plugin=None): loader = loader_plugin(context) - return loader.switch(container, context) + loader_identifier = get_loader_identifier(loader) + hooks = hooks_by_identifier.get(loader_identifier, {}) + for hook_plugin in hooks.get("pre", []): + hook_plugin.switch( + context, + container + ) + switch_return = loader.switch(container, context) + for hook_plugin in hooks.get("post", []): + hook_plugin.switch( + context, + container + ) + + return switch_return def _fix_representation_context_compatibility(repre_context): diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py index a6d88ed44a..c878cad079 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py @@ -1339,8 +1339,14 @@ def _switch_container( repre_entity = repres_by_name[container_repre_name] error = None + hook_loaders_by_id = self._controller.get_hook_loaders_by_identifier() try: - switch_container(container, repre_entity, loader) + switch_container( + container, + repre_entity, + loader, + hook_loaders_by_id + ) except ( LoaderSwitchNotImplementedError, IncompatibleLoaderError, From 0070edb34b7ff68d79d3bb50593c478c1ab39774 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 7 Apr 2025 09:56:37 +0200 Subject: [PATCH 16/64] Updated docstrings --- client/ayon_core/tools/sceneinventory/control.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index e6fb459129..c4e59699c3 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -119,7 +119,11 @@ def get_version_items(self, project_name, product_ids): project_name, product_ids) def get_hook_loaders_by_identifier(self): - """Returns lists of pre|post hooks per Loader identifier.""" + """Returns lists of pre|post hooks per Loader identifier. + + Returns: + (dict) {"LoaderName": {"pre": ["PreLoader1"], "post":["PreLoader2]} + """ if self._hooks_by_identifier is None: self._hooks_by_identifier = get_hook_loaders_by_identifier() return self._hooks_by_identifier From 310174fd1a0fba4b0713475bc89e28385fe18e0e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 7 Apr 2025 10:18:19 +0200 Subject: [PATCH 17/64] Fix calling hook --- client/ayon_core/pipeline/load/utils.py | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 94e7ad53a6..d251dff06f 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -417,16 +417,16 @@ def _load_context(Loader, contexts, name, namespace, options, hooks): Only dynamic part is different context(s) to be loaded. """ - for hook_plugin in hooks.get("pre", []): - hook_plugin.process( + for hook_plugin_cls in hooks.get("pre", []): + hook_plugin_cls().process( contexts, name, namespace, options, ) load_return = Loader().load(contexts, name, namespace, options) - for hook_plugin in hooks.get("post", []): - hook_plugin.process( + for hook_plugin_cls in hooks.get("post", []): + hook_plugin_cls().process( contexts, name, namespace, @@ -618,14 +618,14 @@ def update_container(container, version=-1, hooks_by_identifier=None): loader_identifier = get_loader_identifier(Loader) hooks = hooks_by_identifier.get(loader_identifier, {}) - for hook_plugin in hooks.get("pre", []): - hook_plugin.update( + for hook_plugin_cls in hooks.get("pre", []): + hook_plugin_cls().update( context, container ) update_return = Loader().update(container, context) - for hook_plugin in hooks.get("post", []): - hook_plugin.update( + for hook_plugin_cls in hooks.get("post", []): + hook_plugin_cls().update( context, container ) @@ -688,14 +688,14 @@ def switch_container( loader_identifier = get_loader_identifier(loader) hooks = hooks_by_identifier.get(loader_identifier, {}) - for hook_plugin in hooks.get("pre", []): - hook_plugin.switch( + for hook_plugin_cls in hooks.get("pre", []): + hook_plugin_cls().switch( context, container ) switch_return = loader.switch(container, context) - for hook_plugin in hooks.get("post", []): - hook_plugin.switch( + for hook_plugin_cls in hooks.get("post", []): + hook_plugin_cls().switch( context, container ) @@ -1205,8 +1205,8 @@ def _get_hook_loaders(hook_loaders_by_identifier, loader_plugin, loader_type): load_hook_plugins = discover(loader_plugin) loaders_by_name = get_loaders_by_name() - for hook_plugin in load_hook_plugins: - for load_plugin_name in hook_plugin.loaders: + for hook_plugin_cls in load_hook_plugins: + for load_plugin_name in hook_plugin_cls.loaders: load_plugin = loaders_by_name.get(load_plugin_name) if not load_plugin: continue @@ -1215,5 +1215,5 @@ def _get_hook_loaders(hook_loaders_by_identifier, loader_plugin, loader_type): identifier = get_loader_identifier(load_plugin) (hook_loaders_by_identifier.setdefault(identifier, {}) .setdefault(loader_type, []).append( - hook_plugin) + hook_plugin_cls) ) From a0002774301c859b5b6906dc630500e117fed32f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 7 Apr 2025 10:57:58 +0200 Subject: [PATCH 18/64] Update naming Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index e930f034ce..9f9c6c223f 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -284,7 +284,7 @@ def switch(self, container, context): pass -class PostLoadHookPlugin: +class PostLoaderHookPlugin: """Plugin that should be run after any Loaders in 'loaders' Should be used as non-invasive method to enrich core loading process. From d11a8d2731c2e22abf8da7c45103690b6b660535 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 7 Apr 2025 10:58:07 +0200 Subject: [PATCH 19/64] Update naming Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 9f9c6c223f..40a5eeb1a8 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -265,7 +265,7 @@ class ProductLoaderPlugin(LoaderPlugin): """ -class PreLoadHookPlugin: +class PreLoaderHookPlugin: """Plugin that should be run before any Loaders in 'loaders' Should be used as non-invasive method to enrich core loading process. From 70cf7fe5d41d282a23c8e974479ef32112d0081a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 7 Apr 2025 10:58:25 +0200 Subject: [PATCH 20/64] Import annotations Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/load/plugins.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 40a5eeb1a8..4ac1b3076d 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,3 +1,4 @@ +from __future__ import annotations import os import logging from typing import ClassVar From 44fc6f75ab1352cc8106543d612cbea64db0d03d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Apr 2025 10:40:52 +0200 Subject: [PATCH 21/64] Typo --- client/ayon_core/pipeline/load/plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index e930f034ce..4a503d6f1e 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -269,7 +269,7 @@ class PreLoadHookPlugin: """Plugin that should be run before any Loaders in 'loaders' Should be used as non-invasive method to enrich core loading process. - Any external studio might want to modify loaded data before or afte + Any external studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. """ loaders: ClassVar[set[str]] @@ -288,7 +288,7 @@ class PostLoadHookPlugin: """Plugin that should be run after any Loaders in 'loaders' Should be used as non-invasive method to enrich core loading process. - Any external studio might want to modify loaded data before or afte + Any external studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. loaders: ClassVar[set[str]] """ From 2fe9381471c619c2bff46ea0dfb50d4d5f8dc9ce Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Apr 2025 10:44:21 +0200 Subject: [PATCH 22/64] Added loaded container to Post process arguments --- client/ayon_core/pipeline/load/plugins.py | 9 ++++++++- client/ayon_core/pipeline/load/utils.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 4a503d6f1e..1719ee8c07 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -292,7 +292,14 @@ class PostLoadHookPlugin: they are loaded without need to override existing core plugins. loaders: ClassVar[set[str]] """ - def process(self, context, name=None, namespace=None, options=None): + def process( + self, + container, + context, + name=None, + namespace=None, + options=None + ): pass def update(self, container, context): diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index d251dff06f..471d93bb63 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -427,6 +427,7 @@ def _load_context(Loader, contexts, name, namespace, options, hooks): load_return = Loader().load(contexts, name, namespace, options) for hook_plugin_cls in hooks.get("post", []): hook_plugin_cls().process( + load_return, contexts, name, namespace, From fb1879a645e6f16f0bb9dd81349b3cd052b7d91c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Apr 2025 10:46:11 +0200 Subject: [PATCH 23/64] Updated names --- client/ayon_core/pipeline/load/utils.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 471d93bb63..718316a4c2 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -424,16 +424,16 @@ def _load_context(Loader, contexts, name, namespace, options, hooks): namespace, options, ) - load_return = Loader().load(contexts, name, namespace, options) + loaded_container = Loader().load(contexts, name, namespace, options) for hook_plugin_cls in hooks.get("post", []): hook_plugin_cls().process( - load_return, + loaded_container, contexts, name, namespace, options, ) - return load_return + return loaded_container def load_container( @@ -624,13 +624,13 @@ def update_container(container, version=-1, hooks_by_identifier=None): context, container ) - update_return = Loader().update(container, context) + updated_container = Loader().update(container, context) for hook_plugin_cls in hooks.get("post", []): hook_plugin_cls().update( context, container ) - return update_return + return updated_container def switch_container( @@ -694,14 +694,14 @@ def switch_container( context, container ) - switch_return = loader.switch(container, context) + switched_container = loader.switch(container, context) for hook_plugin_cls in hooks.get("post", []): hook_plugin_cls().switch( context, container ) - return switch_return + return switched_container def _fix_representation_context_compatibility(repre_context): From 1e5f5446a77d5b0872a02498d31b4925a9c7cefa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Apr 2025 10:49:04 +0200 Subject: [PATCH 24/64] Renamed loaders to more descriptive --- client/ayon_core/pipeline/load/plugins.py | 5 +++-- client/ayon_core/pipeline/load/utils.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 1719ee8c07..26363a40d9 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -272,7 +272,7 @@ class PreLoadHookPlugin: Any external studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. """ - loaders: ClassVar[set[str]] + loader_identifiers: ClassVar[set[str]] def process(self, context, name=None, namespace=None, options=None): pass @@ -290,8 +290,9 @@ class PostLoadHookPlugin: Should be used as non-invasive method to enrich core loading process. Any external studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. - loaders: ClassVar[set[str]] """ + loader_identifiers: ClassVar[set[str]] + def process( self, container, diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 718316a4c2..c61d743d05 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -1207,7 +1207,7 @@ def _get_hook_loaders(hook_loaders_by_identifier, loader_plugin, loader_type): load_hook_plugins = discover(loader_plugin) loaders_by_name = get_loaders_by_name() for hook_plugin_cls in load_hook_plugins: - for load_plugin_name in hook_plugin_cls.loaders: + for load_plugin_name in hook_plugin_cls.loader_identifiers: load_plugin = loaders_by_name.get(load_plugin_name) if not load_plugin: continue From 8c90fac2b5568cd1e5f38417216d062eab1254bc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Apr 2025 12:36:21 +0200 Subject: [PATCH 25/64] Fix renamed class names --- client/ayon_core/pipeline/load/plugins.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 02efe5b43a..d069508bfa 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -350,32 +350,32 @@ def register_loader_plugin_path(path): def register_loader_pre_hook_plugin(plugin): - return register_plugin(PreLoadHookPlugin, plugin) + return register_plugin(PreLoaderHookPlugin, plugin) def deregister_loader_pre_hook_plugin(plugin): - deregister_plugin(PreLoadHookPlugin, plugin) + deregister_plugin(PreLoaderHookPlugin, plugin) def register_loader_pre_hook_plugin_path(path): - return register_plugin_path(PreLoadHookPlugin, path) + return register_plugin_path(PreLoaderHookPlugin, path) def deregister_loader_pre_hook_plugin_path(path): - deregister_plugin_path(PreLoadHookPlugin, path) + deregister_plugin_path(PreLoaderHookPlugin, path) def register_loader_post_hook_plugin(plugin): - return register_plugin(PostLoadHookPlugin, plugin) + return register_plugin(PostLoaderHookPlugin, plugin) def deregister_loader_post_hook_plugin(plugin): - deregister_plugin(PostLoadHookPlugin, plugin) + deregister_plugin(PostLoaderHookPlugin, plugin) def register_loader_post_hook_plugin_path(path): - return register_plugin_path(PostLoadHookPlugin, path) + return register_plugin_path(PostLoaderHookPlugin, path) def deregister_loader_post_hook_plugin_path(path): - deregister_plugin_path(PostLoadHookPlugin, path) + deregister_plugin_path(PostLoaderHookPlugin, path) From 6e095b8c188b09e3f067538b40652aeaa1089821 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 14:35:23 +0200 Subject: [PATCH 26/64] Updated docstring --- client/ayon_core/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 62d376c6d1..ed26e5fe74 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -257,7 +257,7 @@ class PreLoaderHookPlugin: """Plugin that should be run before any Loaders in 'loaders' Should be used as non-invasive method to enrich core loading process. - Any external studio might want to modify loaded data before or after + Any studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. """ loader_identifiers: ClassVar[set[str]] From 0ac277404c953e26770e3a1aaf6d645ba85af2ed Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 14:37:03 +0200 Subject: [PATCH 27/64] Updated docstring --- client/ayon_core/tools/loader/models/actions.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 07195f4b05..c2444da456 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -316,10 +316,8 @@ def _get_loaders(self, project_name): we want to show loaders for? Returns: - tuple( - list[ProductLoaderPlugin], - list[LoaderPlugin], - ): Discovered loader plugins. + tuple[list[ProductLoaderPlugin], list[LoaderPlugin]]: Discovered + loader plugins. """ loaders_by_identifier_c = self._loaders_by_identifier[project_name] From ee96cdc2c38f1e04b9ba2ccffe4b18ecbe83c6e8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 14:44:14 +0200 Subject: [PATCH 28/64] Remove methods for pre/post loader hooks from higher api This "hides" a bit methods that are not completely relevant from high level API. --- client/ayon_core/pipeline/__init__.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index 0a805b16dc..41bcd0dbd1 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -42,16 +42,6 @@ register_loader_plugin_path, deregister_loader_plugin, - register_loader_pre_hook_plugin, - deregister_loader_pre_hook_plugin, - register_loader_pre_hook_plugin_path, - deregister_loader_pre_hook_plugin_path, - - register_loader_post_hook_plugin, - deregister_loader_post_hook_plugin, - register_loader_post_hook_plugin_path, - deregister_loader_post_hook_plugin_path, - load_container, remove_container, update_container, @@ -61,7 +51,6 @@ get_representation_path, get_representation_context, get_repres_contexts, - get_hook_loaders_by_identifier ) from .publish import ( @@ -171,16 +160,6 @@ "register_loader_plugin_path", "deregister_loader_plugin", - "register_loader_pre_hook_plugin", - "deregister_loader_pre_hook_plugin", - "register_loader_pre_hook_plugin_path", - "deregister_loader_pre_hook_plugin_path", - - "register_loader_post_hook_plugin", - "deregister_loader_post_hook_plugin", - "register_loader_post_hook_plugin_path", - "deregister_loader_post_hook_plugin_path", - "load_container", "remove_container", "update_container", @@ -241,8 +220,6 @@ "register_workfile_build_plugin_path", "deregister_workfile_build_plugin_path", - "get_hook_loaders_by_identifier", - # Backwards compatible function names "install", "uninstall", From 4457a432cb382e7a21fa8202e3ab1b2f524b7239 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:19:19 +0200 Subject: [PATCH 29/64] Merged pre/post hooks into single class --- client/ayon_core/pipeline/load/__init__.py | 27 +++++----------- client/ayon_core/pipeline/load/plugins.py | 36 ++++++---------------- 2 files changed, 18 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/pipeline/load/__init__.py b/client/ayon_core/pipeline/load/__init__.py index eaba8cd78d..2a33fa119b 100644 --- a/client/ayon_core/pipeline/load/__init__.py +++ b/client/ayon_core/pipeline/load/__init__.py @@ -24,7 +24,6 @@ get_loader_identifier, get_loaders_by_name, - get_hook_loaders_by_identifier, get_representation_path_from_context, get_representation_path, @@ -51,15 +50,10 @@ register_loader_plugin_path, deregister_loader_plugin, - register_loader_pre_hook_plugin, - deregister_loader_pre_hook_plugin, - register_loader_pre_hook_plugin_path, - deregister_loader_pre_hook_plugin_path, - - register_loader_post_hook_plugin, - deregister_loader_post_hook_plugin, - register_loader_post_hook_plugin_path, - deregister_loader_post_hook_plugin_path, + register_loader_hook_plugin, + deregister_loader_hook_plugin, + register_loader_hook_plugin_path, + deregister_loader_hook_plugin_path, ) @@ -90,7 +84,6 @@ "get_loader_identifier", "get_loaders_by_name", - "get_hook_loaders_by_identifier", "get_representation_path_from_context", "get_representation_path", @@ -116,13 +109,9 @@ "register_loader_plugin_path", "deregister_loader_plugin", - "register_loader_pre_hook_plugin", - "deregister_loader_pre_hook_plugin", - "register_loader_pre_hook_plugin_path", - "deregister_loader_pre_hook_plugin_path", + "register_loader_hook_plugin", + "deregister_loader_hook_plugin", + "register_loader_hook_plugin_path", + "deregister_loader_hook_plugin_path", - "register_loader_post_hook_plugin", - "deregister_loader_post_hook_plugin", - "register_loader_post_hook_plugin_path", - "deregister_loader_post_hook_plugin_path", ) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index ed26e5fe74..39133bc342 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -253,8 +253,8 @@ class ProductLoaderPlugin(LoaderPlugin): """ -class PreLoaderHookPlugin: - """Plugin that should be run before any Loaders in 'loaders' +class PrePostLoaderHookPlugin: + """Plugin that should be run before or post specific Loader in 'loaders' Should be used as non-invasive method to enrich core loading process. Any studio might want to modify loaded data before or after @@ -336,33 +336,17 @@ def register_loader_plugin_path(path): return register_plugin_path(LoaderPlugin, path) -def register_loader_pre_hook_plugin(plugin): - return register_plugin(PreLoaderHookPlugin, plugin) +def register_loader_hook_plugin(plugin): + return register_plugin(PrePostLoaderHookPlugin, plugin) -def deregister_loader_pre_hook_plugin(plugin): - deregister_plugin(PreLoaderHookPlugin, plugin) +def deregister_loader_hook_plugin(plugin): + deregister_plugin(PrePostLoaderHookPlugin, plugin) -def register_loader_pre_hook_plugin_path(path): - return register_plugin_path(PreLoaderHookPlugin, path) +def register_loader_hook_plugin_path(path): + return register_plugin_path(PrePostLoaderHookPlugin, path) -def deregister_loader_pre_hook_plugin_path(path): - deregister_plugin_path(PreLoaderHookPlugin, path) - - -def register_loader_post_hook_plugin(plugin): - return register_plugin(PostLoaderHookPlugin, plugin) - - -def deregister_loader_post_hook_plugin(plugin): - deregister_plugin(PostLoaderHookPlugin, plugin) - - -def register_loader_post_hook_plugin_path(path): - return register_plugin_path(PostLoaderHookPlugin, path) - - -def deregister_loader_post_hook_plugin_path(path): - deregister_plugin_path(PostLoaderHookPlugin, path) +def deregister_loader_hook_plugin_path(path): + deregister_plugin_path(PrePostLoaderHookPlugin, path) From ca768aeddf81a0cb7e1e76fd7326ca10ea1e2c5d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:21:56 +0200 Subject: [PATCH 30/64] Removed get_hook_loaders_by_identifier Replaced by monkeypatch load method --- client/ayon_core/pipeline/load/utils.py | 37 ------------------- .../ayon_core/tools/loader/models/actions.py | 25 +------------ .../ayon_core/tools/sceneinventory/control.py | 13 ------- .../sceneinventory/switch_dialog/dialog.py | 2 - client/ayon_core/tools/sceneinventory/view.py | 2 - 5 files changed, 2 insertions(+), 77 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 186cc8f0d7..fba4a8d2a8 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -293,7 +293,6 @@ def load_with_repre_context( namespace=None, name=None, options=None, - hooks=None, **kwargs ): @@ -338,7 +337,6 @@ def load_with_product_context( namespace=None, name=None, options=None, - hooks=None, **kwargs ): @@ -373,7 +371,6 @@ def load_with_product_contexts( namespace=None, name=None, options=None, - hooks=None, **kwargs ): @@ -1178,37 +1175,3 @@ def filter_containers(containers, project_name): uptodate_containers.append(container) return output - - -def get_hook_loaders_by_identifier(): - """Discovers pre/post hooks for loader plugins. - - Returns: - (dict) {"LoaderName": {"pre": ["PreLoader1"], "post":["PreLoader2]} - """ - # beware of circular imports! - from .plugins import PreLoadHookPlugin, PostLoadHookPlugin - - hook_loaders_by_identifier = {} - _get_hook_loaders(hook_loaders_by_identifier, PreLoadHookPlugin, "pre") - _get_hook_loaders(hook_loaders_by_identifier, PostLoadHookPlugin, "post") - return hook_loaders_by_identifier - - -def _get_hook_loaders(hook_loaders_by_identifier, loader_plugin, loader_type): - from ..plugin_discover import discover - - load_hook_plugins = discover(loader_plugin) - loaders_by_name = get_loaders_by_name() - for hook_plugin_cls in load_hook_plugins: - for load_plugin_name in hook_plugin_cls.loader_identifiers: - load_plugin = loaders_by_name.get(load_plugin_name) - if not load_plugin: - continue - if not load_plugin.enabled: - continue - identifier = get_loader_identifier(load_plugin) - (hook_loaders_by_identifier.setdefault(identifier, {}) - .setdefault(loader_type, []).append( - hook_plugin_cls) - ) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index c2444da456..6ce9b0836d 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -17,8 +17,6 @@ load_with_product_contexts, LoadError, IncompatibleLoaderError, - get_loaders_by_name, - get_hook_loaders_by_identifier ) from ayon_core.tools.loader.abstract import ActionItem @@ -149,14 +147,12 @@ def trigger_action_item( ACTIONS_MODEL_SENDER, ) loader = self._get_loader_by_identifier(project_name, identifier) - hooks = self._get_hook_loaders_by_identifier(project_name, identifier) if representation_ids is not None: error_info = self._trigger_representation_loader( loader, options, project_name, representation_ids, - hooks ) elif version_ids is not None: error_info = self._trigger_version_loader( @@ -164,7 +160,6 @@ def trigger_action_item( options, project_name, version_ids, - hooks ) else: raise NotImplementedError( @@ -336,7 +331,6 @@ def _get_loaders(self, project_name): available_loaders = self._filter_loaders_by_tool_name( project_name, discover_loader_plugins(project_name) ) - hook_loaders_by_identifier = get_hook_loaders_by_identifier() repre_loaders = [] product_loaders = [] loaders_by_identifier = {} @@ -354,7 +348,6 @@ def _get_loaders(self, project_name): loaders_by_identifier_c.update_data(loaders_by_identifier) product_loaders_c.update_data(product_loaders) repre_loaders_c.update_data(repre_loaders) - hook_loaders_by_identifier_c.update_data(hook_loaders_by_identifier) return product_loaders, repre_loaders @@ -365,13 +358,6 @@ def _get_loader_by_identifier(self, project_name, identifier): loaders_by_identifier = loaders_by_identifier_c.get_data() return loaders_by_identifier.get(identifier) - def _get_hook_loaders_by_identifier(self, project_name, identifier): - if not self._hook_loaders_by_identifier[project_name].is_valid: - self._get_loaders(project_name) - hook_loaders_by_identifier_c = self._hook_loaders_by_identifier[project_name] - hook_loaders_by_identifier_c = hook_loaders_by_identifier_c.get_data() - return hook_loaders_by_identifier_c.get(identifier) - def _actions_sorter(self, action_item): """Sort the Loaders by their order and then their name. @@ -629,7 +615,6 @@ def _trigger_version_loader( options, project_name, version_ids, - hooks=None ): """Trigger version loader. @@ -679,7 +664,7 @@ def _trigger_version_loader( }) return self._load_products_by_loader( - loader, product_contexts, options, hooks=hooks + loader, product_contexts, options ) def _trigger_representation_loader( @@ -688,7 +673,6 @@ def _trigger_representation_loader( options, project_name, representation_ids, - hooks ): """Trigger representation loader. @@ -741,7 +725,7 @@ def _trigger_representation_loader( }) return self._load_representations_by_loader( - loader, repre_contexts, options, hooks + loader, repre_contexts, options ) def _load_representations_by_loader( @@ -749,7 +733,6 @@ def _load_representations_by_loader( loader, repre_contexts, options, - hooks=None ): """Loops through list of repre_contexts and loads them with one loader @@ -773,7 +756,6 @@ def _load_representations_by_loader( loader, repre_context, options=options, - hooks=hooks ) except IncompatibleLoaderError as exc: @@ -808,7 +790,6 @@ def _load_products_by_loader( loader, version_contexts, options, - hooks=None ): """Triggers load with ProductLoader type of loaders. @@ -834,7 +815,6 @@ def _load_products_by_loader( loader, version_contexts, options=options, - hooks=hooks ) except Exception as exc: formatted_traceback = None @@ -860,7 +840,6 @@ def _load_products_by_loader( loader, version_context, options=options, - hooks=hooks ) except Exception as exc: diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index c4e59699c3..60d9bc77a9 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -5,7 +5,6 @@ from ayon_core.pipeline import ( registered_host, get_current_context, - get_hook_loaders_by_identifier ) from ayon_core.tools.common_models import HierarchyModel, ProjectsModel @@ -36,8 +35,6 @@ def __init__(self, host=None): self._projects_model = ProjectsModel(self) self._event_system = self._create_event_system() - self._hooks_by_identifier = None - def get_host(self) -> HostBase: return self._host @@ -118,16 +115,6 @@ def get_version_items(self, project_name, product_ids): return self._containers_model.get_version_items( project_name, product_ids) - def get_hook_loaders_by_identifier(self): - """Returns lists of pre|post hooks per Loader identifier. - - Returns: - (dict) {"LoaderName": {"pre": ["PreLoader1"], "post":["PreLoader2]} - """ - if self._hooks_by_identifier is None: - self._hooks_by_identifier = get_hook_loaders_by_identifier() - return self._hooks_by_identifier - # Site Sync methods def is_sitesync_enabled(self): return self._sitesync_model.is_sitesync_enabled() diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py index c878cad079..f1b144f2aa 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py @@ -1339,13 +1339,11 @@ def _switch_container( repre_entity = repres_by_name[container_repre_name] error = None - hook_loaders_by_id = self._controller.get_hook_loaders_by_identifier() try: switch_container( container, repre_entity, loader, - hook_loaders_by_id ) except ( LoaderSwitchNotImplementedError, diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 75d3d9a680..42338fe33b 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -1100,7 +1100,6 @@ def _update_containers(self, item_ids, versions): containers_by_id = self._controller.get_containers_by_item_ids( item_ids ) - hook_loaders_by_id = self._controller.get_hook_loaders_by_identifier() try: for item_id, item_version in zip(item_ids, versions): container = containers_by_id[item_id] @@ -1108,7 +1107,6 @@ def _update_containers(self, item_ids, versions): update_container( container, item_version, - hook_loaders_by_id ) except AssertionError: log.warning("Update failed", exc_info=True) From 37f5f5583250c122e15898f5b0b33374b3f89b57 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:22:24 +0200 Subject: [PATCH 31/64] Added typing --- client/ayon_core/pipeline/load/plugins.py | 36 ++++++++--------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 39133bc342..56b6a84706 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -265,33 +265,23 @@ class PrePostLoaderHookPlugin: def process(self, context, name=None, namespace=None, options=None): pass - def update(self, container, context): - pass - - def switch(self, container, context): - pass - - -class PostLoaderHookPlugin: - """Plugin that should be run after any Loaders in 'loaders' - - Should be used as non-invasive method to enrich core loading process. - Any external studio might want to modify loaded data before or after - they are loaded without need to override existing core plugins. - """ - loader_identifiers: ClassVar[set[str]] - - def process( + def pre_process( self, - container, - context, - name=None, - namespace=None, - options=None + context: dict, + name: str | None = None, + namespace: str | None = None, + options: dict | None = None, ): pass - def update(self, container, context): + def post_process( + self, + container: dict, # (ayon:container-3.0) + context: dict, + name: str | None = None, + namespace: str | None = None, + options: dict | None = None + ): pass def switch(self, container, context): From b742dfc381cfca3a8584a126aae67529557b5f43 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:22:44 +0200 Subject: [PATCH 32/64] Changed from loader_identifiers to is_compatible method --- client/ayon_core/pipeline/load/plugins.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 56b6a84706..c72f697e8b 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -260,9 +260,8 @@ class PrePostLoaderHookPlugin: Any studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. """ - loader_identifiers: ClassVar[set[str]] - - def process(self, context, name=None, namespace=None, options=None): + @classmethod + def is_compatible(cls, Loader: LoaderPlugin) -> bool: pass def pre_process( From 976ef5fb2b8064efecfa4824e3d516b18bab197a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:23:22 +0200 Subject: [PATCH 33/64] Added monkey patched load method if hooks are found for loader --- client/ayon_core/pipeline/load/plugins.py | 32 +++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index c72f697e8b..695e4634f8 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -292,10 +292,11 @@ def discover_loader_plugins(project_name=None): from ayon_core.pipeline import get_current_project_name log = Logger.get_logger("LoaderDiscover") - plugins = discover(LoaderPlugin) if not project_name: project_name = get_current_project_name() project_settings = get_project_settings(project_name) + plugins = discover(LoaderPlugin) + hooks = discover(PrePostLoaderHookPlugin) for plugin in plugins: try: plugin.apply_settings(project_settings) @@ -304,11 +305,38 @@ def discover_loader_plugins(project_name=None): "Failed to apply settings to loader {}".format( plugin.__name__ ), - exc_info=True + exc_info=True, ) + for Hook in hooks: + if Hook.is_compatible(plugin): + hook_loader_load(plugin, Hook()) return plugins +def hook_loader_load(loader_class, hook_instance): + # If this is the first hook being added, wrap the original load method + if not hasattr(loader_class, '_load_hooks'): + loader_class._load_hooks = [] + + original_load = loader_class.load + + def wrapped_load(self, *args, **kwargs): + # Call pre_load on all hooks + for hook in loader_class._load_hooks: + hook.pre_load(*args, **kwargs) + # Call original load + result = original_load(self, *args, **kwargs) + # Call post_load on all hooks + for hook in loader_class._load_hooks: + hook.post_load(*args, **kwargs) + return result + + loader_class.load = wrapped_load + + # Add the new hook instance to the list + loader_class._load_hooks.append(hook_instance) + + def register_loader_plugin(plugin): return register_plugin(LoaderPlugin, plugin) From 4c113ca5b50e5afbdcfd1291bd637d0c3b665026 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:28:02 +0200 Subject: [PATCH 34/64] Removed unneeded _load_context --- client/ayon_core/pipeline/load/utils.py | 51 ++----------------------- 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index fba4a8d2a8..02a48dc6b6 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -321,14 +321,7 @@ def load_with_repre_context( ) loader = Loader() - return _load_context( - Loader, - repre_context, - name, - namespace, - options, - hooks - ) + loader.load(repre_context, name, namespace, options) def load_with_product_context( @@ -355,14 +348,7 @@ def load_with_product_context( Loader.__name__, product_context["folder"]["path"] ) ) - return _load_context( - Loader, - product_context, - name, - namespace, - options, - hooks - ) + return Loader().load(product_context, name, namespace, options) def load_with_product_contexts( @@ -393,38 +379,7 @@ def load_with_product_contexts( Loader.__name__, joined_product_names ) ) - return _load_context( - Loader, - product_contexts, - name, - namespace, - options, - hooks - ) - - -def _load_context(Loader, contexts, name, namespace, options, hooks): - """Helper function to wrap hooks around generic load function. - - Only dynamic part is different context(s) to be loaded. - """ - for hook_plugin_cls in hooks.get("pre", []): - hook_plugin_cls().process( - contexts, - name, - namespace, - options, - ) - loaded_container = Loader().load(contexts, name, namespace, options) - for hook_plugin_cls in hooks.get("post", []): - hook_plugin_cls().process( - loaded_container, - contexts, - name, - namespace, - options, - ) - return loaded_container + return Loader().load(product_contexts, name, namespace, options) def load_container( From 55583e68f8c388a15514db16859225288426f1df Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:29:14 +0200 Subject: [PATCH 35/64] Fix missing return --- client/ayon_core/pipeline/load/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 02a48dc6b6..75d9450003 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -321,7 +321,7 @@ def load_with_repre_context( ) loader = Loader() - loader.load(repre_context, name, namespace, options) + return loader.load(repre_context, name, namespace, options) def load_with_product_context( From 7e88916fcd8567da2b466a27bdd1b7096e1bc106 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:29:54 +0200 Subject: [PATCH 36/64] Reverted missing newline --- client/ayon_core/pipeline/load/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 75d9450003..45000b023b 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -348,6 +348,7 @@ def load_with_product_context( Loader.__name__, product_context["folder"]["path"] ) ) + return Loader().load(product_context, name, namespace, options) @@ -379,6 +380,7 @@ def load_with_product_contexts( Loader.__name__, joined_product_names ) ) + return Loader().load(product_contexts, name, namespace, options) From f21f4a5e016c0f02c187d92d7d4051714096a9d0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:33:09 +0200 Subject: [PATCH 37/64] Removed usage of hooks in update, switch --- client/ayon_core/pipeline/load/utils.py | 33 +++---------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 45000b023b..a61ffee95a 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -469,7 +469,7 @@ def remove_container(container): return Loader().remove(container) -def update_container(container, version=-1, hooks_by_identifier=None): +def update_container(container, version=-1): """Update a container""" from ayon_core.pipeline import get_current_project_name @@ -565,20 +565,7 @@ def update_container(container, version=-1, hooks_by_identifier=None): if not path or not os.path.exists(path): raise ValueError("Path {} doesn't exist".format(path)) - loader_identifier = get_loader_identifier(Loader) - hooks = hooks_by_identifier.get(loader_identifier, {}) - for hook_plugin_cls in hooks.get("pre", []): - hook_plugin_cls().update( - context, - container - ) - updated_container = Loader().update(container, context) - for hook_plugin_cls in hooks.get("post", []): - hook_plugin_cls().update( - context, - container - ) - return updated_container + return Loader().update(container, context) def switch_container( @@ -635,21 +622,7 @@ def switch_container( loader = loader_plugin(context) - loader_identifier = get_loader_identifier(loader) - hooks = hooks_by_identifier.get(loader_identifier, {}) - for hook_plugin_cls in hooks.get("pre", []): - hook_plugin_cls().switch( - context, - container - ) - switched_container = loader.switch(container, context) - for hook_plugin_cls in hooks.get("post", []): - hook_plugin_cls().switch( - context, - container - ) - - return switched_container + return loader.switch(container, context) def _fix_representation_context_compatibility(repre_context): From 07809b56ddcadf764182fbeb923d72bd59ec734c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:35:22 +0200 Subject: [PATCH 38/64] Removed _hook_loaders_by_identifier in actions --- client/ayon_core/tools/loader/models/actions.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 6ce9b0836d..f55f303f21 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -51,8 +51,6 @@ def __init__(self, controller): levels=1, lifetime=self.loaders_cache_lifetime) self._repre_loaders = NestedCacheItem( levels=1, lifetime=self.loaders_cache_lifetime) - self._hook_loaders_by_identifier = NestedCacheItem( - levels=1, lifetime=self.loaders_cache_lifetime) def reset(self): """Reset the model with all cached items.""" @@ -61,7 +59,6 @@ def reset(self): self._loaders_by_identifier.reset() self._product_loaders.reset() self._repre_loaders.reset() - self._hook_loaders_by_identifier.reset() def get_versions_action_items(self, project_name, version_ids): """Get action items for given version ids. @@ -318,13 +315,8 @@ def _get_loaders(self, project_name): loaders_by_identifier_c = self._loaders_by_identifier[project_name] product_loaders_c = self._product_loaders[project_name] repre_loaders_c = self._repre_loaders[project_name] - hook_loaders_by_identifier_c = self._hook_loaders_by_identifier[project_name] if loaders_by_identifier_c.is_valid: - return ( - product_loaders_c.get_data(), - repre_loaders_c.get_data(), - hook_loaders_by_identifier_c.get_data() - ) + return product_loaders_c.get_data(), repre_loaders_c.get_data() # Get all representation->loader combinations available for the # index under the cursor, so we can list the user the options. From 9a050d92006c268ac4a99ddce83ae31c87006832 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:35:32 +0200 Subject: [PATCH 39/64] Formatting change --- client/ayon_core/tools/loader/models/actions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index f55f303f21..9a1a19c682 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -724,7 +724,7 @@ def _load_representations_by_loader( self, loader, repre_contexts, - options, + options ): """Loops through list of repre_contexts and loads them with one loader @@ -747,7 +747,7 @@ def _load_representations_by_loader( load_with_repre_context( loader, repre_context, - options=options, + options=options ) except IncompatibleLoaderError as exc: @@ -781,7 +781,7 @@ def _load_products_by_loader( self, loader, version_contexts, - options, + options ): """Triggers load with ProductLoader type of loaders. @@ -806,7 +806,7 @@ def _load_products_by_loader( load_with_product_contexts( loader, version_contexts, - options=options, + options=options ) except Exception as exc: formatted_traceback = None @@ -831,7 +831,7 @@ def _load_products_by_loader( load_with_product_context( loader, version_context, - options=options, + options=options ) except Exception as exc: From c8cca23e48fe6cd52a7632b376bd3946e38e3d57 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:37:43 +0200 Subject: [PATCH 40/64] Revert unneeded change --- client/ayon_core/tools/sceneinventory/view.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 42338fe33b..bb95e37d4e 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -1104,10 +1104,7 @@ def _update_containers(self, item_ids, versions): for item_id, item_version in zip(item_ids, versions): container = containers_by_id[item_id] try: - update_container( - container, - item_version, - ) + update_container(container, item_version) except AssertionError: log.warning("Update failed", exc_info=True) self._show_version_error_dialog( From a4babae5f5432d6d01f68e44d2b5b6675e08365d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 16:38:29 +0200 Subject: [PATCH 41/64] Revert unneeded change --- .../ayon_core/tools/sceneinventory/switch_dialog/dialog.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py index f1b144f2aa..a6d88ed44a 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py @@ -1340,11 +1340,7 @@ def _switch_container( error = None try: - switch_container( - container, - repre_entity, - loader, - ) + switch_container(container, repre_entity, loader) except ( LoaderSwitchNotImplementedError, IncompatibleLoaderError, From 0e292eb3560d9771ded3b4c9ab0198a59952ef25 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 17:43:47 +0200 Subject: [PATCH 42/64] Renamed --- client/ayon_core/pipeline/load/plugins.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 695e4634f8..02479655eb 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -307,9 +307,9 @@ def discover_loader_plugins(project_name=None): ), exc_info=True, ) - for Hook in hooks: - if Hook.is_compatible(plugin): - hook_loader_load(plugin, Hook()) + for hookCls in hooks: + if hookCls.is_compatible(plugin): + hook_loader_load(plugin, hookCls()) return plugins From 276eff0097a2846cd7fd3497e22ff2f67ce88781 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 17:45:44 +0200 Subject: [PATCH 43/64] Added docstring --- client/ayon_core/pipeline/load/plugins.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 02479655eb..190b3d3a30 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -313,7 +313,11 @@ def discover_loader_plugins(project_name=None): return plugins -def hook_loader_load(loader_class, hook_instance): +def hook_loader_load( + loader_class: LoaderPlugin, + hook_instance: PrePostLoaderHookPlugin +) -> None: + """Monkey patch method replacing Loader.load method with wrapped hooks.""" # If this is the first hook being added, wrap the original load method if not hasattr(loader_class, '_load_hooks'): loader_class._load_hooks = [] From 118796a32523fedf60709437502f1ade8304dd7b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 17:46:41 +0200 Subject: [PATCH 44/64] Passed returned container from load as keyword Could be appended on position 0 on args, but this feels safer. --- client/ayon_core/pipeline/load/plugins.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 190b3d3a30..9040b122e3 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -264,7 +264,7 @@ class PrePostLoaderHookPlugin: def is_compatible(cls, Loader: LoaderPlugin) -> bool: pass - def pre_process( + def pre_load( self, context: dict, name: str | None = None, @@ -273,13 +273,13 @@ def pre_process( ): pass - def post_process( + def post_load( self, - container: dict, # (ayon:container-3.0) context: dict, name: str | None = None, namespace: str | None = None, - options: dict | None = None + options: dict | None = None, + container: dict | None = None, # (ayon:container-3.0) ): pass @@ -329,11 +329,12 @@ def wrapped_load(self, *args, **kwargs): for hook in loader_class._load_hooks: hook.pre_load(*args, **kwargs) # Call original load - result = original_load(self, *args, **kwargs) + container = original_load(self, *args, **kwargs) + kwargs["container"] = container # Call post_load on all hooks for hook in loader_class._load_hooks: hook.post_load(*args, **kwargs) - return result + return container loader_class.load = wrapped_load From 4ae7ab28ab0ff89ae930d5900307bfb2048d755e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jun 2025 17:55:12 +0200 Subject: [PATCH 45/64] Removed unnecessary import --- client/ayon_core/pipeline/load/plugins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 9040b122e3..808eaf9340 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,7 +1,6 @@ from __future__ import annotations import os import logging -from typing import ClassVar from ayon_core.settings import get_project_settings from ayon_core.pipeline.plugin_discover import ( From cf4f9cfea61b2cbb95aa6caef3fcda10723c36b9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jun 2025 14:04:47 +0200 Subject: [PATCH 46/64] Removed unused argument --- client/ayon_core/pipeline/load/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index a61ffee95a..3c50d76fb5 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -572,7 +572,6 @@ def switch_container( container, representation, loader_plugin=None, - hooks_by_identifier=None ): """Switch a container to representation @@ -580,7 +579,6 @@ def switch_container( container (dict): container information representation (dict): representation entity loader_plugin (LoaderPlugin) - hooks_by_identifier (dict): {"pre": [PreHookPlugin1], "post":[]} Returns: return from function call From 234e38869773507c0b9e8eb96a8d1f182e71a8e8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jun 2025 14:07:52 +0200 Subject: [PATCH 47/64] Added abstractmethod decorator --- client/ayon_core/pipeline/load/plugins.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 808eaf9340..50423af051 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,6 +1,7 @@ from __future__ import annotations import os import logging +from abc import abstractmethod from ayon_core.settings import get_project_settings from ayon_core.pipeline.plugin_discover import ( @@ -260,9 +261,11 @@ class PrePostLoaderHookPlugin: they are loaded without need to override existing core plugins. """ @classmethod + @abstractmethod def is_compatible(cls, Loader: LoaderPlugin) -> bool: pass + @abstractmethod def pre_load( self, context: dict, @@ -272,6 +275,7 @@ def pre_load( ): pass + @abstractmethod def post_load( self, context: dict, From 0e582c7d5fbdf54e90b54f5398257495e7e7735e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jun 2025 14:08:26 +0200 Subject: [PATCH 48/64] Update docstring --- client/ayon_core/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 50423af051..2268935099 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -254,7 +254,7 @@ class ProductLoaderPlugin(LoaderPlugin): class PrePostLoaderHookPlugin: - """Plugin that should be run before or post specific Loader in 'loaders' + """Plugin that runs before and post specific Loader in 'loaders' Should be used as non-invasive method to enrich core loading process. Any studio might want to modify loaded data before or after From 03e3b29597c7c7ccab83e1777f7be13d4d419b3e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jun 2025 14:22:08 +0200 Subject: [PATCH 49/64] Renamed variable --- client/ayon_core/pipeline/load/plugins.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 2268935099..4cf497836c 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -310,9 +310,9 @@ def discover_loader_plugins(project_name=None): ), exc_info=True, ) - for hookCls in hooks: - if hookCls.is_compatible(plugin): - hook_loader_load(plugin, hookCls()) + for hook_cls in hooks: + if hook_cls.is_compatible(plugin): + hook_loader_load(plugin, hook_cls()) return plugins From f4556ac697ea9b127451b661f8c0e6c9f546bdd2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jun 2025 15:58:03 +0200 Subject: [PATCH 50/64] Formatting change --- client/ayon_core/tools/loader/models/actions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index 9a1a19c682..40331d73a4 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -17,7 +17,6 @@ load_with_product_contexts, LoadError, IncompatibleLoaderError, - ) from ayon_core.tools.loader.abstract import ActionItem @@ -743,7 +742,6 @@ def _load_representations_by_loader( if version < 0: version = "Hero" try: - load_with_repre_context( loader, repre_context, From 77d5d8d162fcccf322f635e56d5498dac7d6164a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jun 2025 17:33:30 +0200 Subject: [PATCH 51/64] Implemented order attribute for sorting --- client/ayon_core/pipeline/load/plugins.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 4cf497836c..9f3d9531c7 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -260,6 +260,8 @@ class PrePostLoaderHookPlugin: Any studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. """ + order = 0 + @classmethod @abstractmethod def is_compatible(cls, Loader: LoaderPlugin) -> bool: @@ -300,6 +302,7 @@ def discover_loader_plugins(project_name=None): project_settings = get_project_settings(project_name) plugins = discover(LoaderPlugin) hooks = discover(PrePostLoaderHookPlugin) + sorted_hooks = sorted(hooks, key=lambda hook: hook.order) for plugin in plugins: try: plugin.apply_settings(project_settings) @@ -310,7 +313,7 @@ def discover_loader_plugins(project_name=None): ), exc_info=True, ) - for hook_cls in hooks: + for hook_cls in sorted_hooks: if hook_cls.is_compatible(plugin): hook_loader_load(plugin, hook_cls()) return plugins From 802b8b2567ccd13067d1a14b86138781c02ae114 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jun 2025 17:38:15 +0200 Subject: [PATCH 52/64] Refactored monkey patched method --- client/ayon_core/pipeline/load/plugins.py | 47 +++++++++++------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 9f3d9531c7..30e8856cf8 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -313,39 +313,36 @@ def discover_loader_plugins(project_name=None): ), exc_info=True, ) + compatible_hooks = [] for hook_cls in sorted_hooks: if hook_cls.is_compatible(plugin): - hook_loader_load(plugin, hook_cls()) + compatible_hooks.append(hook_cls) + add_hooks_to_loader(plugin, compatible_hooks) return plugins -def hook_loader_load( +def add_hooks_to_loader( loader_class: LoaderPlugin, - hook_instance: PrePostLoaderHookPlugin + compatible_hooks: list[PrePostLoaderHookPlugin] ) -> None: """Monkey patch method replacing Loader.load method with wrapped hooks.""" - # If this is the first hook being added, wrap the original load method - if not hasattr(loader_class, '_load_hooks'): - loader_class._load_hooks = [] - - original_load = loader_class.load - - def wrapped_load(self, *args, **kwargs): - # Call pre_load on all hooks - for hook in loader_class._load_hooks: - hook.pre_load(*args, **kwargs) - # Call original load - container = original_load(self, *args, **kwargs) - kwargs["container"] = container - # Call post_load on all hooks - for hook in loader_class._load_hooks: - hook.post_load(*args, **kwargs) - return container - - loader_class.load = wrapped_load - - # Add the new hook instance to the list - loader_class._load_hooks.append(hook_instance) + loader_class._load_hooks = compatible_hooks + + original_load = loader_class.load + + def wrapped_load(self, *args, **kwargs): + # Call pre_load on all hooks + for hook in loader_class._load_hooks: + hook.pre_load(*args, **kwargs) + # Call original load + container = original_load(self, *args, **kwargs) + kwargs["container"] = container + # Call post_load on all hooks + for hook in loader_class._load_hooks: + hook.post_load(*args, **kwargs) + return container + + loader_class.load = wrapped_load def register_loader_plugin(plugin): From 3db2cd046a1f8755b51a2473bd8af2d138385083 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 17 Jun 2025 14:44:24 +0200 Subject: [PATCH 53/64] Implemented pre/post for update and remove --- client/ayon_core/pipeline/load/plugins.py | 84 ++++++++++++++++++----- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 30e8856cf8..76cc8bd19b 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -288,7 +288,33 @@ def post_load( ): pass - def switch(self, container, context): + @abstractmethod + def pre_update( + self, + container: dict, # (ayon:container-3.0) + context: dict, + ): + pass + + @abstractmethod + def post_update( + container: dict, # (ayon:container-3.0) + context: dict, + ): + pass + + @abstractmethod + def pre_remove( + self, + container: dict, # (ayon:container-3.0) + ): + pass + + @abstractmethod + def post_remove( + container: dict, # (ayon:container-3.0) + context: dict, + ): pass @@ -322,27 +348,47 @@ def discover_loader_plugins(project_name=None): def add_hooks_to_loader( - loader_class: LoaderPlugin, - compatible_hooks: list[PrePostLoaderHookPlugin] + loader_class: LoaderPlugin, compatible_hooks: list[PrePostLoaderHookPlugin] ) -> None: - """Monkey patch method replacing Loader.load method with wrapped hooks.""" + """Monkey patch method replacing Loader.load|update|remove methods + + It wraps applicable loaders with pre/post hooks. Discovery is called only + once per loaders discovery. + """ loader_class._load_hooks = compatible_hooks - original_load = loader_class.load - - def wrapped_load(self, *args, **kwargs): - # Call pre_load on all hooks - for hook in loader_class._load_hooks: - hook.pre_load(*args, **kwargs) - # Call original load - container = original_load(self, *args, **kwargs) - kwargs["container"] = container - # Call post_load on all hooks - for hook in loader_class._load_hooks: - hook.post_load(*args, **kwargs) - return container - - loader_class.load = wrapped_load + def wrap_method(method_name: str): + original_method = getattr(loader_class, method_name) + + def wrapped_method(self, *args, **kwargs): + # Call pre_ on all hooks + pre_hook_name = f"pre_{method_name}" + for hook in loader_class._load_hooks: + pre_hook = getattr(hook, pre_hook_name, None) + if callable(pre_hook): + pre_hook(self, *args, **kwargs) + + # Call original method + result = original_method(self, *args, **kwargs) + + # Add result to kwargs if needed + # Assuming container-like result for load, update, remove + kwargs["container"] = result + + # Call post_ on all hooks + post_hook_name = f"post_{method_name}" + for hook in loader_class._load_hooks: + post_hook = getattr(hook, post_hook_name, None) + if callable(post_hook): + post_hook(self, *args, **kwargs) + + return result + + setattr(loader_class, method_name, wrapped_method) + + for method in ("load", "update", "remove"): + if hasattr(loader_class, method): + wrap_method(method) def register_loader_plugin(plugin): From 3018dd35b68825f0cff49744713511ca583bd217 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 17 Jun 2025 21:58:16 +0200 Subject: [PATCH 54/64] Refactor `PrePostLoaderHookPlugin` to `LoaderHookPlugin` --- client/ayon_core/pipeline/load/plugins.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 76cc8bd19b..ca3cdce5e3 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -253,7 +253,7 @@ class ProductLoaderPlugin(LoaderPlugin): """ -class PrePostLoaderHookPlugin: +class LoaderHookPlugin: """Plugin that runs before and post specific Loader in 'loaders' Should be used as non-invasive method to enrich core loading process. @@ -327,7 +327,7 @@ def discover_loader_plugins(project_name=None): project_name = get_current_project_name() project_settings = get_project_settings(project_name) plugins = discover(LoaderPlugin) - hooks = discover(PrePostLoaderHookPlugin) + hooks = discover(LoaderHookPlugin) sorted_hooks = sorted(hooks, key=lambda hook: hook.order) for plugin in plugins: try: @@ -348,7 +348,7 @@ def discover_loader_plugins(project_name=None): def add_hooks_to_loader( - loader_class: LoaderPlugin, compatible_hooks: list[PrePostLoaderHookPlugin] + loader_class: LoaderPlugin, compatible_hooks: list[LoaderHookPlugin] ) -> None: """Monkey patch method replacing Loader.load|update|remove methods @@ -408,16 +408,16 @@ def register_loader_plugin_path(path): def register_loader_hook_plugin(plugin): - return register_plugin(PrePostLoaderHookPlugin, plugin) + return register_plugin(LoaderHookPlugin, plugin) def deregister_loader_hook_plugin(plugin): - deregister_plugin(PrePostLoaderHookPlugin, plugin) + deregister_plugin(LoaderHookPlugin, plugin) def register_loader_hook_plugin_path(path): - return register_plugin_path(PrePostLoaderHookPlugin, path) + return register_plugin_path(LoaderHookPlugin, path) def deregister_loader_hook_plugin_path(path): - deregister_plugin_path(PrePostLoaderHookPlugin, path) + deregister_plugin_path(LoaderHookPlugin, path) From 85ef0fefa41cfbdbe9dfea21cfc390fab69258b1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 17 Jun 2025 21:59:06 +0200 Subject: [PATCH 55/64] Fix `post_update` and `post_remove` --- client/ayon_core/pipeline/load/plugins.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index ca3cdce5e3..7176bd82ac 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -298,6 +298,7 @@ def pre_update( @abstractmethod def post_update( + self, container: dict, # (ayon:container-3.0) context: dict, ): @@ -312,8 +313,8 @@ def pre_remove( @abstractmethod def post_remove( + self, container: dict, # (ayon:container-3.0) - context: dict, ): pass From bcca8ca8c1d847dc5064c37ef8a790c415a451b4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 17 Jun 2025 22:41:31 +0200 Subject: [PATCH 56/64] Rename `post_*` method kwarg `container` to `result` to not clash with `container` argument on `update` and `remove` and make it clearer that it's the "result" of something --- client/ayon_core/pipeline/load/plugins.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 7176bd82ac..0fc2718190 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,6 +1,7 @@ from __future__ import annotations import os import logging +from typing import Any from abc import abstractmethod from ayon_core.settings import get_project_settings @@ -259,6 +260,9 @@ class LoaderHookPlugin: Should be used as non-invasive method to enrich core loading process. Any studio might want to modify loaded data before or after they are loaded without need to override existing core plugins. + + The post methods are called after the loader's methods and receive the + return value of the loader's method as `result` argument. """ order = 0 @@ -284,7 +288,7 @@ def post_load( name: str | None = None, namespace: str | None = None, options: dict | None = None, - container: dict | None = None, # (ayon:container-3.0) + result: Any = None, ): pass @@ -301,6 +305,7 @@ def post_update( self, container: dict, # (ayon:container-3.0) context: dict, + result: Any = None, ): pass @@ -315,6 +320,7 @@ def pre_remove( def post_remove( self, container: dict, # (ayon:container-3.0) + result: Any = None, ): pass @@ -374,7 +380,7 @@ def wrapped_method(self, *args, **kwargs): # Add result to kwargs if needed # Assuming container-like result for load, update, remove - kwargs["container"] = result + kwargs["result"] = result # Call post_ on all hooks post_hook_name = f"post_{method_name}" From d583279c6dad91ecb5e72218455560d66afe6697 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 17 Jun 2025 23:26:30 +0200 Subject: [PATCH 57/64] Fix type hint --- client/ayon_core/pipeline/load/plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 0fc2718190..4582c2b329 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,7 +1,7 @@ from __future__ import annotations import os import logging -from typing import Any +from typing import Any, Type from abc import abstractmethod from ayon_core.settings import get_project_settings @@ -268,7 +268,7 @@ class LoaderHookPlugin: @classmethod @abstractmethod - def is_compatible(cls, Loader: LoaderPlugin) -> bool: + def is_compatible(cls, Loader: Type[LoaderPlugin]) -> bool: pass @abstractmethod From a593516f29394b28f03cb14e8d2d1818ebbc9ca0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 17 Jun 2025 23:39:25 +0200 Subject: [PATCH 58/64] Fix Hook logic to actually run on a `LoaderHookPlugin` instance - Now `self` on the hook method actually refers to an instantiated `LoaderHookPlugin` - Add `plugin` kwargs to the hook methods to still provide access to the `LoaderPlugin` instance for potential advanced behavior - Fix type hint on `compatible_hooks` arguments to `add_hooks_to_loader` function --- client/ayon_core/pipeline/load/plugins.py | 30 +++++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 4582c2b329..4f0071b4d3 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -278,6 +278,7 @@ def pre_load( name: str | None = None, namespace: str | None = None, options: dict | None = None, + plugin: LoaderPlugin | None = None, ): pass @@ -288,6 +289,7 @@ def post_load( name: str | None = None, namespace: str | None = None, options: dict | None = None, + plugin: LoaderPlugin | None = None, result: Any = None, ): pass @@ -297,6 +299,8 @@ def pre_update( self, container: dict, # (ayon:container-3.0) context: dict, + plugin: LoaderPlugin | None = None, + ): pass @@ -305,6 +309,7 @@ def post_update( self, container: dict, # (ayon:container-3.0) context: dict, + plugin: LoaderPlugin | None = None, result: Any = None, ): pass @@ -313,6 +318,7 @@ def post_update( def pre_remove( self, container: dict, # (ayon:container-3.0) + plugin: LoaderPlugin | None = None, ): pass @@ -320,6 +326,7 @@ def pre_remove( def post_remove( self, container: dict, # (ayon:container-3.0) + plugin: LoaderPlugin | None = None, result: Any = None, ): pass @@ -355,7 +362,7 @@ def discover_loader_plugins(project_name=None): def add_hooks_to_loader( - loader_class: LoaderPlugin, compatible_hooks: list[LoaderHookPlugin] + loader_class: LoaderPlugin, compatible_hooks: list[Type[LoaderHookPlugin]] ) -> None: """Monkey patch method replacing Loader.load|update|remove methods @@ -370,24 +377,31 @@ def wrap_method(method_name: str): def wrapped_method(self, *args, **kwargs): # Call pre_ on all hooks pre_hook_name = f"pre_{method_name}" - for hook in loader_class._load_hooks: + + # Pass the LoaderPlugin instance to the hooks + hook_kwargs = kwargs.copy() + hook_kwargs["plugin"] = self + + hooks: list[LoaderHookPlugin] = [] + for Hook in loader_class._load_hooks: + hook = Hook() # Instantiate the hook + hooks.append(hook) pre_hook = getattr(hook, pre_hook_name, None) if callable(pre_hook): - pre_hook(self, *args, **kwargs) + pre_hook(*args, **hook_kwargs) # Call original method result = original_method(self, *args, **kwargs) - # Add result to kwargs if needed - # Assuming container-like result for load, update, remove - kwargs["result"] = result + # Add result to kwargs for post hooks from the original method + hook_kwargs["result"] = result # Call post_ on all hooks post_hook_name = f"post_{method_name}" - for hook in loader_class._load_hooks: + for hook in hooks: post_hook = getattr(hook, post_hook_name, None) if callable(post_hook): - post_hook(self, *args, **kwargs) + post_hook(*args, **hook_kwargs) return result From 1d55f2a033248c845f2f7ac1432acd85ce49248c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 18 Jun 2025 09:59:09 +0200 Subject: [PATCH 59/64] Update client/ayon_core/pipeline/load/plugins.py --- client/ayon_core/pipeline/load/plugins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 4f0071b4d3..6a6c6c639f 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -300,7 +300,6 @@ def pre_update( container: dict, # (ayon:container-3.0) context: dict, plugin: LoaderPlugin | None = None, - ): pass From e8dcec0510dd78099f8ab2a3bea000cdd91a6ce5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 19 Jun 2025 10:52:34 +0200 Subject: [PATCH 60/64] Changed typing to support 3.7 We still support older Maya --- client/ayon_core/pipeline/load/plugins.py | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 6a6c6c639f..e9ba5a37c3 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -1,7 +1,7 @@ from __future__ import annotations import os import logging -from typing import Any, Type +from typing import Any, Type, Optional from abc import abstractmethod from ayon_core.settings import get_project_settings @@ -275,10 +275,10 @@ def is_compatible(cls, Loader: Type[LoaderPlugin]) -> bool: def pre_load( self, context: dict, - name: str | None = None, - namespace: str | None = None, - options: dict | None = None, - plugin: LoaderPlugin | None = None, + name: Optional[str] = None, + namespace: Optional[str] = None, + options: Optional[dict] = None, + plugin: Optional[LoaderPlugin] = None, ): pass @@ -286,10 +286,10 @@ def pre_load( def post_load( self, context: dict, - name: str | None = None, - namespace: str | None = None, - options: dict | None = None, - plugin: LoaderPlugin | None = None, + name: Optional[str] = None, + namespace: Optional[str] = None, + options: Optional[dict] = None, + plugin: Optional[LoaderPlugin] = None, result: Any = None, ): pass @@ -299,7 +299,7 @@ def pre_update( self, container: dict, # (ayon:container-3.0) context: dict, - plugin: LoaderPlugin | None = None, + plugin: Optional[LoaderPlugin] = None, ): pass @@ -308,7 +308,7 @@ def post_update( self, container: dict, # (ayon:container-3.0) context: dict, - plugin: LoaderPlugin | None = None, + plugin: Optional[LoaderPlugin] = None, result: Any = None, ): pass @@ -317,7 +317,7 @@ def post_update( def pre_remove( self, container: dict, # (ayon:container-3.0) - plugin: LoaderPlugin | None = None, + plugin: Optional[LoaderPlugin] = None, ): pass @@ -325,7 +325,7 @@ def pre_remove( def post_remove( self, container: dict, # (ayon:container-3.0) - plugin: LoaderPlugin | None = None, + plugin: Optional[LoaderPlugin] = None, result: Any = None, ): pass From b0e472ebe9be6741676860bdf9dd6852bc219a03 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 19 Jun 2025 11:18:34 +0200 Subject: [PATCH 61/64] Changed order of plugin, result Plugin is required so it makes sense to bump it up to first position and pass it before args --- client/ayon_core/pipeline/load/plugins.py | 35 +++++++++-------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index e9ba5a37c3..5f4056d2d5 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -274,59 +274,59 @@ def is_compatible(cls, Loader: Type[LoaderPlugin]) -> bool: @abstractmethod def pre_load( self, + plugin: LoaderPlugin, context: dict, name: Optional[str] = None, namespace: Optional[str] = None, options: Optional[dict] = None, - plugin: Optional[LoaderPlugin] = None, ): pass @abstractmethod def post_load( self, + plugin: LoaderPlugin, + result: Any, context: dict, name: Optional[str] = None, namespace: Optional[str] = None, options: Optional[dict] = None, - plugin: Optional[LoaderPlugin] = None, - result: Any = None, ): pass @abstractmethod def pre_update( self, + plugin: LoaderPlugin, container: dict, # (ayon:container-3.0) context: dict, - plugin: Optional[LoaderPlugin] = None, ): pass @abstractmethod def post_update( self, + plugin: LoaderPlugin, + result: Any, container: dict, # (ayon:container-3.0) context: dict, - plugin: Optional[LoaderPlugin] = None, - result: Any = None, ): pass @abstractmethod def pre_remove( self, + plugin: LoaderPlugin, container: dict, # (ayon:container-3.0) - plugin: Optional[LoaderPlugin] = None, ): pass @abstractmethod def post_remove( self, + plugin: LoaderPlugin, + result: Any, container: dict, # (ayon:container-3.0) - plugin: Optional[LoaderPlugin] = None, - result: Any = None, ): pass @@ -377,30 +377,21 @@ def wrapped_method(self, *args, **kwargs): # Call pre_ on all hooks pre_hook_name = f"pre_{method_name}" - # Pass the LoaderPlugin instance to the hooks - hook_kwargs = kwargs.copy() - hook_kwargs["plugin"] = self - hooks: list[LoaderHookPlugin] = [] - for Hook in loader_class._load_hooks: - hook = Hook() # Instantiate the hook + for cls in loader_class._load_hooks: + hook = cls() # Instantiate the hook hooks.append(hook) pre_hook = getattr(hook, pre_hook_name, None) if callable(pre_hook): - pre_hook(*args, **hook_kwargs) - + pre_hook(hook, *args, **kwargs) # Call original method result = original_method(self, *args, **kwargs) - - # Add result to kwargs for post hooks from the original method - hook_kwargs["result"] = result - # Call post_ on all hooks post_hook_name = f"post_{method_name}" for hook in hooks: post_hook = getattr(hook, post_hook_name, None) if callable(post_hook): - post_hook(*args, **hook_kwargs) + post_hook(hook, result, *args, **kwargs) return result From d12273a6b82fc948fd1a1b2987c7dc7415826bec Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 19 Jun 2025 11:24:13 +0200 Subject: [PATCH 62/64] Removed None assignment --- client/ayon_core/pipeline/load/plugins.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 5f4056d2d5..73cc93797d 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -276,9 +276,9 @@ def pre_load( self, plugin: LoaderPlugin, context: dict, - name: Optional[str] = None, - namespace: Optional[str] = None, - options: Optional[dict] = None, + name: Optional[str], + namespace: Optional[str], + options: Optional[dict], ): pass @@ -288,9 +288,9 @@ def post_load( plugin: LoaderPlugin, result: Any, context: dict, - name: Optional[str] = None, - namespace: Optional[str] = None, - options: Optional[dict] = None, + name: Optional[str], + namespace: Optional[str], + options: Optional[dict], ): pass From 68751b8f2287734dcc50e081a3ff6b6172ace11d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 19 Jun 2025 12:08:27 +0200 Subject: [PATCH 63/64] Fix wrong usage of Hook, should be LoaderPlugin Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 73cc93797d..d2e6cd1abf 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -391,7 +391,7 @@ def wrapped_method(self, *args, **kwargs): for hook in hooks: post_hook = getattr(hook, post_hook_name, None) if callable(post_hook): - post_hook(hook, result, *args, **kwargs) + post_hook(self, result, *args, **kwargs) return result From e82716978d32638c9aa20aff5b5114a293469c7d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 19 Jun 2025 12:08:37 +0200 Subject: [PATCH 64/64] Fix wrong usage of Hook, should be LoaderPlugin Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index d2e6cd1abf..1dac8a4048 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -383,7 +383,7 @@ def wrapped_method(self, *args, **kwargs): hooks.append(hook) pre_hook = getattr(hook, pre_hook_name, None) if callable(pre_hook): - pre_hook(hook, *args, **kwargs) + pre_hook(self, *args, **kwargs) # Call original method result = original_method(self, *args, **kwargs) # Call post_ on all hooks