mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #3623 from pypeclub/feature/OP-3679_Plugin-settings-handled-by-plugins
General: Plugin settings handled by plugins
This commit is contained in:
commit
3ffa995ed6
7 changed files with 276 additions and 58 deletions
|
|
@ -1,11 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Avalon/Pyblish plugin tools."""
|
||||
import os
|
||||
import inspect
|
||||
import logging
|
||||
import re
|
||||
import json
|
||||
|
||||
import warnings
|
||||
import functools
|
||||
|
||||
from openpype.client import get_asset_by_id
|
||||
from openpype.settings import get_project_settings
|
||||
|
||||
|
|
@ -17,6 +19,51 @@ log = logging.getLogger(__name__)
|
|||
DEFAULT_SUBSET_TEMPLATE = "{family}{Variant}"
|
||||
|
||||
|
||||
class PluginToolsDeprecatedWarning(DeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
def deprecated(new_destination):
|
||||
"""Mark functions as deprecated.
|
||||
|
||||
It will result in a warning being emitted when the function is used.
|
||||
"""
|
||||
|
||||
func = None
|
||||
if callable(new_destination):
|
||||
func = new_destination
|
||||
new_destination = None
|
||||
|
||||
def _decorator(decorated_func):
|
||||
if new_destination is None:
|
||||
warning_message = (
|
||||
" Please check content of deprecated function to figure out"
|
||||
" possible replacement."
|
||||
)
|
||||
else:
|
||||
warning_message = " Please replace your usage with '{}'.".format(
|
||||
new_destination
|
||||
)
|
||||
|
||||
@functools.wraps(decorated_func)
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.simplefilter("always", PluginToolsDeprecatedWarning)
|
||||
warnings.warn(
|
||||
(
|
||||
"Call to deprecated function '{}'"
|
||||
"\nFunction was moved or removed.{}"
|
||||
).format(decorated_func.__name__, warning_message),
|
||||
category=PluginToolsDeprecatedWarning,
|
||||
stacklevel=4
|
||||
)
|
||||
return decorated_func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
if func is None:
|
||||
return _decorator
|
||||
return _decorator(func)
|
||||
|
||||
|
||||
class TaskNotSetError(KeyError):
|
||||
def __init__(self, msg=None):
|
||||
if not msg:
|
||||
|
|
@ -197,6 +244,7 @@ def prepare_template_data(fill_pairs):
|
|||
return fill_data
|
||||
|
||||
|
||||
@deprecated("openpype.pipeline.publish.lib.filter_pyblish_plugins")
|
||||
def filter_pyblish_plugins(plugins):
|
||||
"""Filter pyblish plugins by presets.
|
||||
|
||||
|
|
@ -206,57 +254,14 @@ def filter_pyblish_plugins(plugins):
|
|||
Args:
|
||||
plugins (dict): Dictionary of plugins produced by :mod:`pyblish-base`
|
||||
`discover()` method.
|
||||
|
||||
"""
|
||||
from pyblish import api
|
||||
|
||||
host = api.current_host()
|
||||
from openpype.pipeline.publish.lib import filter_pyblish_plugins
|
||||
|
||||
presets = get_project_settings(os.environ['AVALON_PROJECT']) or {}
|
||||
# skip if there are no presets to process
|
||||
if not presets:
|
||||
return
|
||||
|
||||
# iterate over plugins
|
||||
for plugin in plugins[:]:
|
||||
|
||||
try:
|
||||
config_data = presets[host]["publish"][plugin.__name__]
|
||||
except KeyError:
|
||||
# host determined from path
|
||||
file = os.path.normpath(inspect.getsourcefile(plugin))
|
||||
file = os.path.normpath(file)
|
||||
|
||||
split_path = file.split(os.path.sep)
|
||||
if len(split_path) < 4:
|
||||
log.warning(
|
||||
'plugin path too short to extract host {}'.format(file)
|
||||
)
|
||||
continue
|
||||
|
||||
host_from_file = split_path[-4]
|
||||
plugin_kind = split_path[-2]
|
||||
|
||||
# TODO: change after all plugins are moved one level up
|
||||
if host_from_file == "openpype":
|
||||
host_from_file = "global"
|
||||
|
||||
try:
|
||||
config_data = presets[host_from_file][plugin_kind][plugin.__name__] # noqa: E501
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
for option, value in config_data.items():
|
||||
if option == "enabled" and value is False:
|
||||
log.info('removing plugin {}'.format(plugin.__name__))
|
||||
plugins.remove(plugin)
|
||||
else:
|
||||
log.info('setting {}:{} on plugin {}'.format(
|
||||
option, value, plugin.__name__))
|
||||
|
||||
setattr(plugin, option, value)
|
||||
filter_pyblish_plugins(plugins)
|
||||
|
||||
|
||||
@deprecated
|
||||
def set_plugin_attributes_from_settings(
|
||||
plugins, superclass, host_name=None, project_name=None
|
||||
):
|
||||
|
|
@ -273,6 +278,8 @@ def set_plugin_attributes_from_settings(
|
|||
project_name (str): Name of project for which settings will be loaded.
|
||||
Value from environment `AVALON_PROJECT` is used if not entered.
|
||||
"""
|
||||
|
||||
# Function is not used anymore
|
||||
from openpype.pipeline import LegacyCreator, LoaderPlugin
|
||||
|
||||
# determine host application to use for finding presets
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ from openpype.client import (
|
|||
)
|
||||
from openpype.modules import load_modules, ModulesManager
|
||||
from openpype.settings import get_project_settings
|
||||
from openpype.lib import filter_pyblish_plugins
|
||||
|
||||
from .publish.lib import filter_pyblish_plugins
|
||||
from .anatomy import Anatomy
|
||||
from .template_data import get_template_data_with_names
|
||||
from . import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import copy
|
||||
|
||||
from abc import (
|
||||
|
|
@ -7,10 +8,8 @@ from abc import (
|
|||
)
|
||||
import six
|
||||
|
||||
from openpype.lib import (
|
||||
get_subset_name_with_asset_doc,
|
||||
set_plugin_attributes_from_settings,
|
||||
)
|
||||
from openpype.settings import get_system_settings, get_project_settings
|
||||
from openpype.lib import get_subset_name_with_asset_doc
|
||||
from openpype.pipeline.plugin_discover import (
|
||||
discover,
|
||||
register_plugin,
|
||||
|
|
@ -438,8 +437,24 @@ def discover_creator_plugins():
|
|||
|
||||
|
||||
def discover_legacy_creator_plugins():
|
||||
from openpype.lib import Logger
|
||||
|
||||
log = Logger.get_logger("CreatorDiscover")
|
||||
|
||||
plugins = discover(LegacyCreator)
|
||||
set_plugin_attributes_from_settings(plugins, LegacyCreator)
|
||||
project_name = os.environ.get("AVALON_PROJECT")
|
||||
system_settings = get_system_settings()
|
||||
project_settings = get_project_settings(project_name)
|
||||
for plugin in plugins:
|
||||
try:
|
||||
plugin.apply_settings(project_settings, system_settings)
|
||||
except Exception:
|
||||
log.warning(
|
||||
"Failed to apply settings to loader {}".format(
|
||||
plugin.__name__
|
||||
),
|
||||
exc_info=True
|
||||
)
|
||||
return plugins
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Renamed classes and functions
|
|||
- 'create' -> 'legacy_create'
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
import collections
|
||||
|
||||
|
|
@ -37,6 +38,48 @@ class LegacyCreator(object):
|
|||
|
||||
self.data.update(data or {})
|
||||
|
||||
@classmethod
|
||||
def apply_settings(cls, project_settings, system_settings):
|
||||
"""Apply OpenPype settings to a plugin class."""
|
||||
|
||||
host_name = os.environ.get("AVALON_APP")
|
||||
plugin_type = "create"
|
||||
plugin_type_settings = (
|
||||
project_settings
|
||||
.get(host_name, {})
|
||||
.get(plugin_type, {})
|
||||
)
|
||||
global_type_settings = (
|
||||
project_settings
|
||||
.get("global", {})
|
||||
.get(plugin_type, {})
|
||||
)
|
||||
if not global_type_settings and not plugin_type_settings:
|
||||
return
|
||||
|
||||
plugin_name = cls.__name__
|
||||
|
||||
plugin_settings = None
|
||||
# Look for plugin settings in host specific settings
|
||||
if plugin_name in plugin_type_settings:
|
||||
plugin_settings = plugin_type_settings[plugin_name]
|
||||
|
||||
# Look for plugin settings in global settings
|
||||
elif plugin_name in global_type_settings:
|
||||
plugin_settings = global_type_settings[plugin_name]
|
||||
|
||||
if not plugin_settings:
|
||||
return
|
||||
|
||||
print(">>> We have preset for {}".format(plugin_name))
|
||||
for option, value in plugin_settings.items():
|
||||
if option == "enabled" and value is False:
|
||||
setattr(cls, "active", False)
|
||||
print(" - is disabled by preset")
|
||||
else:
|
||||
setattr(cls, option, value)
|
||||
print(" - setting `{}`: `{}`".format(option, value))
|
||||
|
||||
def process(self):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
from openpype.lib import set_plugin_attributes_from_settings
|
||||
from openpype.settings import get_system_settings, get_project_settings
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.pipeline.plugin_discover import (
|
||||
discover,
|
||||
register_plugin,
|
||||
|
|
@ -37,6 +39,46 @@ class LoaderPlugin(list):
|
|||
def __init__(self, context):
|
||||
self.fname = self.filepath_from_context(context)
|
||||
|
||||
@classmethod
|
||||
def apply_settings(cls, project_settings, system_settings):
|
||||
host_name = os.environ.get("AVALON_APP")
|
||||
plugin_type = "load"
|
||||
plugin_type_settings = (
|
||||
project_settings
|
||||
.get(host_name, {})
|
||||
.get(plugin_type, {})
|
||||
)
|
||||
global_type_settings = (
|
||||
project_settings
|
||||
.get("global", {})
|
||||
.get(plugin_type, {})
|
||||
)
|
||||
if not global_type_settings and not plugin_type_settings:
|
||||
return
|
||||
|
||||
plugin_name = cls.__name__
|
||||
|
||||
plugin_settings = None
|
||||
# Look for plugin settings in host specific settings
|
||||
if plugin_name in plugin_type_settings:
|
||||
plugin_settings = plugin_type_settings[plugin_name]
|
||||
|
||||
# Look for plugin settings in global settings
|
||||
elif plugin_name in global_type_settings:
|
||||
plugin_settings = global_type_settings[plugin_name]
|
||||
|
||||
if not plugin_settings:
|
||||
return
|
||||
|
||||
print(">>> We have preset for {}".format(plugin_name))
|
||||
for option, value in plugin_settings.items():
|
||||
if option == "enabled" and value is False:
|
||||
setattr(cls, "active", False)
|
||||
print(" - is disabled by preset")
|
||||
else:
|
||||
setattr(cls, option, value)
|
||||
print(" - setting `{}`: `{}`".format(option, value))
|
||||
|
||||
@classmethod
|
||||
def get_representations(cls):
|
||||
return cls.representations
|
||||
|
|
@ -110,9 +152,25 @@ class SubsetLoaderPlugin(LoaderPlugin):
|
|||
pass
|
||||
|
||||
|
||||
def discover_loader_plugins():
|
||||
def discover_loader_plugins(project_name=None):
|
||||
from openpype.lib import Logger
|
||||
|
||||
log = Logger.get_logger("LoaderDiscover")
|
||||
plugins = discover(LoaderPlugin)
|
||||
set_plugin_attributes_from_settings(plugins, LoaderPlugin)
|
||||
if not project_name:
|
||||
project_name = legacy_io.active_project()
|
||||
system_settings = get_system_settings()
|
||||
project_settings = get_project_settings(project_name)
|
||||
for plugin in plugins:
|
||||
try:
|
||||
plugin.apply_settings(project_settings, system_settings)
|
||||
except Exception:
|
||||
log.warning(
|
||||
"Failed to apply settings to loader {}".format(
|
||||
plugin.__name__
|
||||
),
|
||||
exc_info=True
|
||||
)
|
||||
return plugins
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ import xml.etree.ElementTree
|
|||
|
||||
import six
|
||||
import pyblish.plugin
|
||||
import pyblish.api
|
||||
|
||||
from openpype.lib import Logger
|
||||
from openpype.settings import get_project_settings, get_system_settings
|
||||
|
||||
|
||||
class DiscoverResult:
|
||||
|
|
@ -180,3 +184,92 @@ def publish_plugins_discover(paths=None):
|
|||
result.plugins = plugins
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def filter_pyblish_plugins(plugins):
|
||||
"""Pyblish plugin filter which applies OpenPype settings.
|
||||
|
||||
Apply OpenPype settings on discovered plugins. On plugin with implemented
|
||||
class method 'def apply_settings(cls, project_settings, system_settings)'
|
||||
is called the method. Default behavior looks for plugin name and current
|
||||
host name to look for
|
||||
|
||||
Args:
|
||||
plugins (List[pyblish.plugin.Plugin]): Discovered plugins on which
|
||||
are applied settings.
|
||||
"""
|
||||
|
||||
log = Logger.get_logger("filter_pyblish_plugins")
|
||||
|
||||
# TODO: Don't use host from 'pyblish.api' but from defined host by us.
|
||||
# - kept becau on farm is probably used host 'shell' which propably
|
||||
# affect how settings are applied there
|
||||
host = pyblish.api.current_host()
|
||||
project_name = os.environ.get("AVALON_PROJECT")
|
||||
|
||||
project_setting = get_project_settings(project_name)
|
||||
system_settings = get_system_settings()
|
||||
|
||||
# iterate over plugins
|
||||
for plugin in plugins[:]:
|
||||
if hasattr(plugin, "apply_settings"):
|
||||
try:
|
||||
# Use classmethod 'apply_settings'
|
||||
# - can be used to target settings from custom settings place
|
||||
# - skip default behavior when successful
|
||||
plugin.apply_settings(project_setting, system_settings)
|
||||
continue
|
||||
|
||||
except Exception:
|
||||
log.warning(
|
||||
(
|
||||
"Failed to apply settings on plugin {}"
|
||||
).format(plugin.__name__),
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
try:
|
||||
config_data = (
|
||||
project_setting
|
||||
[host]
|
||||
["publish"]
|
||||
[plugin.__name__]
|
||||
)
|
||||
except KeyError:
|
||||
# host determined from path
|
||||
file = os.path.normpath(inspect.getsourcefile(plugin))
|
||||
file = os.path.normpath(file)
|
||||
|
||||
split_path = file.split(os.path.sep)
|
||||
if len(split_path) < 4:
|
||||
log.warning(
|
||||
'plugin path too short to extract host {}'.format(file)
|
||||
)
|
||||
continue
|
||||
|
||||
host_from_file = split_path[-4]
|
||||
plugin_kind = split_path[-2]
|
||||
|
||||
# TODO: change after all plugins are moved one level up
|
||||
if host_from_file == "openpype":
|
||||
host_from_file = "global"
|
||||
|
||||
try:
|
||||
config_data = (
|
||||
project_setting
|
||||
[host_from_file]
|
||||
[plugin_kind]
|
||||
[plugin.__name__]
|
||||
)
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
for option, value in config_data.items():
|
||||
if option == "enabled" and value is False:
|
||||
log.info('removing plugin {}'.format(plugin.__name__))
|
||||
plugins.remove(plugin)
|
||||
else:
|
||||
log.info('setting {}:{} on plugin {}'.format(
|
||||
option, value, plugin.__name__))
|
||||
|
||||
setattr(plugin, option, value)
|
||||
|
|
|
|||
|
|
@ -434,7 +434,8 @@ class SubsetWidget(QtWidgets.QWidget):
|
|||
|
||||
# Get all representation->loader combinations available for the
|
||||
# index under the cursor, so we can list the user the options.
|
||||
available_loaders = discover_loader_plugins()
|
||||
project_name = self.dbcon.active_project()
|
||||
available_loaders = discover_loader_plugins(project_name)
|
||||
if self.tool_name:
|
||||
available_loaders = lib.remove_tool_name_from_loaders(
|
||||
available_loaders, self.tool_name
|
||||
|
|
@ -1330,7 +1331,8 @@ class RepresentationWidget(QtWidgets.QWidget):
|
|||
selected_side = self._get_selected_side(point_index, rows)
|
||||
# Get all representation->loader combinations available for the
|
||||
# index under the cursor, so we can list the user the options.
|
||||
available_loaders = discover_loader_plugins()
|
||||
project_name = self.dbcon.active_project()
|
||||
available_loaders = discover_loader_plugins(project_name)
|
||||
|
||||
filtered_loaders = []
|
||||
for loader in available_loaders:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue