mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Publish: Enhance automated publish plugin settings (#4986)
* prepared helper functions for custom settings apply method * publish plugin can have 'settings_category' attribute to define settings category * Better 'settings_category' comment Co-authored-by: Roy Nieterau <roy_nieterau@hotmail.com> * fix trailing spaces * added more information about pyblish plugins to dev docs --------- Co-authored-by: Roy Nieterau <roy_nieterau@hotmail.com>
This commit is contained in:
parent
136af34a71
commit
30fe6759c3
3 changed files with 136 additions and 23 deletions
|
|
@ -36,6 +36,9 @@ from .lib import (
|
||||||
context_plugin_should_run,
|
context_plugin_should_run,
|
||||||
get_instance_staging_dir,
|
get_instance_staging_dir,
|
||||||
get_publish_repre_path,
|
get_publish_repre_path,
|
||||||
|
|
||||||
|
apply_plugin_settings_automatically,
|
||||||
|
get_plugin_settings,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .abstract_expected_files import ExpectedFiles
|
from .abstract_expected_files import ExpectedFiles
|
||||||
|
|
@ -80,6 +83,9 @@ __all__ = (
|
||||||
"get_instance_staging_dir",
|
"get_instance_staging_dir",
|
||||||
"get_publish_repre_path",
|
"get_publish_repre_path",
|
||||||
|
|
||||||
|
"apply_plugin_settings_automatically",
|
||||||
|
"get_plugin_settings",
|
||||||
|
|
||||||
"ExpectedFiles",
|
"ExpectedFiles",
|
||||||
|
|
||||||
"RenderInstance",
|
"RenderInstance",
|
||||||
|
|
|
||||||
|
|
@ -355,29 +355,55 @@ def publish_plugins_discover(paths=None):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _get_plugin_settings(host_name, project_settings, plugin, log):
|
def get_plugin_settings(plugin, project_settings, log, category=None):
|
||||||
"""Get plugin settings based on host name and plugin name.
|
"""Get plugin settings based on host name and plugin name.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Default implementation of automated settings is passing host name
|
||||||
|
into 'category'.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
host_name (str): Name of host.
|
plugin (pyblish.Plugin): Plugin where settings are applied.
|
||||||
project_settings (dict[str, Any]): Project settings.
|
project_settings (dict[str, Any]): Project settings.
|
||||||
plugin (pyliblish.Plugin): Plugin where settings are applied.
|
|
||||||
log (logging.Logger): Logger to log messages.
|
log (logging.Logger): Logger to log messages.
|
||||||
|
category (Optional[str]): Settings category key where to look
|
||||||
|
for plugin settings.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict[str, Any]: Plugin settings {'attribute': 'value'}.
|
dict[str, Any]: Plugin settings {'attribute': 'value'}.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Use project settings from host name category when available
|
# Plugin can define settings category by class attribute
|
||||||
try:
|
# - it's impossible to set `settings_category` via settings because
|
||||||
return (
|
# obviously settings are not applied before it.
|
||||||
project_settings
|
# - if `settings_category` is set the fallback category method is ignored
|
||||||
[host_name]
|
settings_category = getattr(plugin, "settings_category", None)
|
||||||
["publish"]
|
if settings_category:
|
||||||
[plugin.__name__]
|
try:
|
||||||
)
|
return (
|
||||||
except KeyError:
|
project_settings
|
||||||
pass
|
[settings_category]
|
||||||
|
["publish"]
|
||||||
|
[plugin.__name__]
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
log.warning((
|
||||||
|
"Couldn't find plugin '{}' settings"
|
||||||
|
" under settings category '{}'"
|
||||||
|
).format(plugin.__name__, settings_category))
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Use project settings based on a category name
|
||||||
|
if category:
|
||||||
|
try:
|
||||||
|
return (
|
||||||
|
project_settings
|
||||||
|
[category]
|
||||||
|
["publish"]
|
||||||
|
[plugin.__name__]
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
# Settings category determined from path
|
# Settings category determined from path
|
||||||
# - usually path is './<category>/plugins/publish/<plugin file>'
|
# - usually path is './<category>/plugins/publish/<plugin file>'
|
||||||
|
|
@ -386,9 +412,10 @@ def _get_plugin_settings(host_name, project_settings, plugin, log):
|
||||||
|
|
||||||
split_path = filepath.rsplit(os.path.sep, 5)
|
split_path = filepath.rsplit(os.path.sep, 5)
|
||||||
if len(split_path) < 4:
|
if len(split_path) < 4:
|
||||||
log.warning(
|
log.debug((
|
||||||
'plugin path too short to extract host {}'.format(filepath)
|
"Plugin path is too short to automatically"
|
||||||
)
|
" extract settings category. {}"
|
||||||
|
).format(filepath))
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
category_from_file = split_path[-4]
|
category_from_file = split_path[-4]
|
||||||
|
|
@ -410,6 +437,28 @@ def _get_plugin_settings(host_name, project_settings, plugin, log):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def apply_plugin_settings_automatically(plugin, settings, logger=None):
|
||||||
|
"""Automatically apply plugin settings to a plugin object.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
This function was created to be able to use it in custom overrides of
|
||||||
|
'apply_settings' class method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin (type[pyblish.api.Plugin]): Class of a plugin.
|
||||||
|
settings (dict[str, Any]): Plugin specific settings.
|
||||||
|
logger (Optional[logging.Logger]): Logger to log debug messages about
|
||||||
|
applied settings values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for option, value in settings.items():
|
||||||
|
if logger:
|
||||||
|
logger.debug("Plugin {} - Attr: {} -> {}".format(
|
||||||
|
option, value, plugin.__name__
|
||||||
|
))
|
||||||
|
setattr(plugin, option, value)
|
||||||
|
|
||||||
|
|
||||||
def filter_pyblish_plugins(plugins):
|
def filter_pyblish_plugins(plugins):
|
||||||
"""Pyblish plugin filter which applies OpenPype settings.
|
"""Pyblish plugin filter which applies OpenPype settings.
|
||||||
|
|
||||||
|
|
@ -453,13 +502,10 @@ def filter_pyblish_plugins(plugins):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Automated
|
# Automated
|
||||||
plugin_settins = _get_plugin_settings(
|
plugin_settins = get_plugin_settings(
|
||||||
host_name, project_settings, plugin, log
|
plugin, project_settings, log, host_name
|
||||||
)
|
)
|
||||||
for option, value in plugin_settins.items():
|
apply_plugin_settings_automatically(plugin, plugin_settins, log)
|
||||||
log.info("setting {}:{} on plugin {}".format(
|
|
||||||
option, value, plugin.__name__))
|
|
||||||
setattr(plugin, option, value)
|
|
||||||
|
|
||||||
# Remove disabled plugins
|
# Remove disabled plugins
|
||||||
if getattr(plugin, "enabled", True) is False:
|
if getattr(plugin, "enabled", True) is False:
|
||||||
|
|
|
||||||
|
|
@ -506,6 +506,67 @@ or the scene file was copy pasted from different context.
|
||||||
#### *Known errors*
|
#### *Known errors*
|
||||||
When there is a known error that can't be fixed by the user (e.g. can't connect to deadline service, etc.) `KnownPublishError` should be raised. The only difference is that its message is shown in UI to the artist otherwise a neutral message without context is shown.
|
When there is a known error that can't be fixed by the user (e.g. can't connect to deadline service, etc.) `KnownPublishError` should be raised. The only difference is that its message is shown in UI to the artist otherwise a neutral message without context is shown.
|
||||||
|
|
||||||
|
### Plugins
|
||||||
|
Plugin is a single processing unit that can work with publish context and instances.
|
||||||
|
|
||||||
|
#### Plugin types
|
||||||
|
There are 2 types of plugins - `InstancePlugin` and `ContextPlugin`. Be aware that inheritance of plugin from `InstancePlugin` or `ContextPlugin` actually does not affect if plugin is instance or context plugin, that is affected by argument name in `process` method.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
|
# Context plugin
|
||||||
|
class MyContextPlugin(pyblish.api.ContextPlugin):
|
||||||
|
def process(self, context):
|
||||||
|
...
|
||||||
|
|
||||||
|
# Instance plugin
|
||||||
|
class MyInstancePlugin(pyblish.api.InstancePlugin):
|
||||||
|
def process(self, instance):
|
||||||
|
...
|
||||||
|
|
||||||
|
# Still an instance plugin
|
||||||
|
class MyOtherInstancePlugin(pyblish.api.ContextPlugin):
|
||||||
|
def process(self, instance):
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Plugin filtering
|
||||||
|
By pyblish logic, plugins have predefined filtering class attributes `hosts`, `targets` and `families`. Filter by `hosts` and `targets` are filters that are applied for current publishing process. Both filters are registered in `pyblish` module, `hosts` filtering may not match OpenPype host name (e.g. farm publishing uses `shell` in pyblish). Filter `families` works only on instance plugins and is dynamic during publish process by changing families of an instance.
|
||||||
|
|
||||||
|
All filters are list of a strings `families = ["image"]`. Empty list is invalid filter and plugin will be skipped, to allow plugin for all values use a start `families = ["*"]`. For more detailed filtering options check [pyblish documentation](https://api.pyblish.com/pluginsystem).
|
||||||
|
|
||||||
|
Each plugin must have order, there are 4 order milestones - Collect, Validate, Extract, Integration. Any plugin below collection order won't be processed. for more details check [pyblish documentation](https://api.pyblish.com/ordering).
|
||||||
|
|
||||||
|
#### Plugin settings
|
||||||
|
Pyblish plugins may have settings. There are 2 ways how settings are applied, first is automated, and it's logic is based on function `filter_pyblish_plugins` in `./openpype/pipeline/publish/lib.py`, second is explicit by implementing class method `apply_settings` on a plugin.
|
||||||
|
|
||||||
|
|
||||||
|
Automated logic is expecting specific structure of project settings `project_settings[{category}]["plugins"]["publish"][{plugin class name}]`. The category is a key in root of project settings. There are currently 3 ways how the category key is received.
|
||||||
|
1. Use `settings_category` class attribute value from plugin. If `settings_category` is not `None` there is not any fallback to other way.
|
||||||
|
2. Use currently registered pyblish host. This will be probably deprecated soon.
|
||||||
|
3. Use 3rd folder name from a plugin filepath. From path `./maya/plugins/publish/collect_render.py` is used `maya` as the key.
|
||||||
|
|
||||||
|
For any other use-case is recommended to use explicit approach by implementing `apply_settings` method. Must use `@classmethod` decorator and expect arguments for project settings and system settings. We're planning to support single argument with only project settings.
|
||||||
|
```python
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
|
class MyPlugin(pyblish.api.InstancePlugin):
|
||||||
|
profiles = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def apply_settings(cls, project_settings, system_settings):
|
||||||
|
cls.profiles = (
|
||||||
|
project_settings
|
||||||
|
["addon"]
|
||||||
|
["plugins"]
|
||||||
|
["publish"]
|
||||||
|
["vfx_profiles"]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
### Plugin extension
|
### Plugin extension
|
||||||
Publish plugins can be extended by additional logic when inheriting from `OpenPypePyblishPluginMixin` which can be used as mixin (additional inheritance of class). Publish plugins that inherit from this mixin can define attributes that will be shown in **CreatedInstance**. One of the most important usages is to be able turn on/off optional plugins.
|
Publish plugins can be extended by additional logic when inheriting from `OpenPypePyblishPluginMixin` which can be used as mixin (additional inheritance of class). Publish plugins that inherit from this mixin can define attributes that will be shown in **CreatedInstance**. One of the most important usages is to be able turn on/off optional plugins.
|
||||||
|
|
||||||
|
|
@ -596,4 +657,4 @@ Publish attributes work the same way as create attributes but the source of attr
|
||||||
|
|
||||||
### Create dialog
|
### Create dialog
|
||||||

|

|
||||||
Create dialog is used by artist to create new instances in a context. The context selection can be enabled/disabled by changing `create_allow_context_change` on [creator plugin](#creator). In the middle part the artist selects what will be created and what variant it is. On the right side is information about the selected creator and its pre-create attributes. There is also a question mark button which extends the window and displays more detailed information about the creator.
|
Create dialog is used by artist to create new instances in a context. The context selection can be enabled/disabled by changing `create_allow_context_change` on [creator plugin](#creator). In the middle part the artist selects what will be created and what variant it is. On the right side is information about the selected creator and its pre-create attributes. There is also a question mark button which extends the window and displays more detailed information about the creator.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue