## Changelog Description
This PR introduces new concept of `Pre/Post Loader Hoo…ks` which could be used by to enhance or modify loading processes without need of modifying official loaders.
The idea is for custom addons to be able to implement their own `PrePostLoaderHookPlugin` which would be triggered before/after Loader action.
Method to be implemented in `PrePostLoaderHookPlugin`:
- `pre_load`
- `post_load`
- `pre_update`
- `post_update`
- `pre_remove`
- `post_remove`
The function signature (arguments) to each of the pre/post methods matches the regular `LoadPlugin` except that `post_*` methods take an additional `result` argument which receives the output of the `LoadPlugin` method call, like simplified:
```python
pre_load()
result = load()
post_load(x, y, z, result=result)
```
## Additional info
Each addon must register its folder with pre/post hooks like this:
```
register_loader_hook_plugin_path(loader_hook_dir)
```
I am testing it with this dummy custom addon which shows hooks for `"CopyFile", "FileLoader", "LoadImage"` Loaders.
~~[my_custom_addon-1.0.0.zip](https://github.com/user-attachments/files/20777382/my_custom_addon-1.0.0.zip)~~ (outdated example files)
### Register example hook to just print all the hook methods
```python
from ayon_core.pipeline.load.plugins import LoaderHookPlugin
from ayon_core.pipeline.load import register_loader_hook_plugin
class LoadHookTest(LoaderHookPlugin):
@classmethod
def is_compatible(cls, Loader) -> bool:
return True
def pre_load(self, context, name=None, namespace=None, data=None, plugin=None):
self.log.info("pre_loadaa")
def post_load(self, context, name=None, namespace=None, data=None, plugin=None, result=None):
self.log.info("post_load")
def pre_update(self, container, context, plugin=None):
self.log.info("pre_update")
def post_update(self, container, context, plugin=None, result=None):
self.log.info("post_update")
def pre_remove(self, container, plugin=None):
self.log.info("pre_remove")
def post_remove(self, container, plugin=None, result=None):
self.log.info("post_remove")
register_loader_hook_plugin(LoadHookTest)
```
Or when targeting specific loaders and e.g. writing to a file on disk on each hook call:
```python
from ayon_core.pipeline.load import get_loader_identifier
from ayon_core.pipeline.load.plugins import LoaderHookPlugin
class PrePostCopyFile(LoaderHookPlugin):
"""Example of hook which would be run before and after CopyFile.load()"""
@classmethod
def is_compatible(cls, Loader) -> bool:
identifier = get_loader_identifier(Loader)
return identifier in {"CopyFile", "FileLoader", "LoadImage"}
def pre_load(self, context, name=None, namespace=None, data=None, plugin=None):
self._write("pre_load")
def post_load(self, context, name=None, namespace=None, data=None, plugin=None, result=None):
self._write("post_load")
def pre_update(self, container, context, plugin=None):
self._write("pre_update")
def post_update(self, container, context, plugin=None, result=None):
self._write("post_update")
def pre_remove(self, container, plugin=None):
self._write("pre_remove")
def post_remove(self, container, plugin=None, result=None):
self._write("post_remove")
def _write(self, value):
with open("c:/projects/pre_post_load.txt", "a") as fp:
fp.write(f"{value}\n")
```
## Testing notes:
1. update `copy_file_hook.py` in `my_custom_addon` however you feel fit for your testing
2. follow this step