From a91a980b1becaf112ee0b05d4d5c4498ee13611e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 20 Jan 2023 19:56:38 +0100 Subject: [PATCH 01/56] laoder plugin has compatibility method on it's own --- openpype/pipeline/load/plugins.py | 35 ++++++++++++++++++++++++++++++- openpype/pipeline/load/utils.py | 18 +--------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index b5e55834db..4197ddb2ae 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -2,7 +2,10 @@ import os import logging from openpype.settings import get_system_settings, get_project_settings -from openpype.pipeline import legacy_io +from openpype.pipeline import ( + schema, + legacy_io, +) from openpype.pipeline.plugin_discover import ( discover, register_plugin, @@ -79,6 +82,36 @@ class LoaderPlugin(list): print(" - setting `{}`: `{}`".format(option, value)) setattr(cls, option, value) + @classmethod + def is_compatible_loader(cls, context): + """Return whether a loader is compatible with a context. + + This checks the version's families and the representation for the given + Loader. + + Returns: + bool + """ + + maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) + if maj_version < 3: + families = context["version"]["data"].get("families", []) + else: + families = context["subset"]["data"]["families"] + + representation = context["representation"] + has_family = ( + "*" in cls.families or any( + family in cls.families for family in families + ) + ) + representations = cls.get_representations() + has_representation = ( + "*" in representations + or representation["name"] in representations + ) + return has_family and has_representation + @classmethod def get_representations(cls): return cls.representations diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index e2b3675115..e30923f922 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -748,25 +748,9 @@ def is_compatible_loader(Loader, context): Returns: bool - """ - maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) - if maj_version < 3: - families = context["version"]["data"].get("families", []) - else: - families = context["subset"]["data"]["families"] - representation = context["representation"] - has_family = ( - "*" in Loader.families or any( - family in Loader.families for family in families - ) - ) - representations = Loader.get_representations() - has_representation = ( - "*" in representations or representation["name"] in representations - ) - return has_family and has_representation + return Loader.is_compatible_loader(context) def loaders_from_repre_context(loaders, repre_context): From bce5363e4250bc6e9a83c7702889a30790bf909a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 23 Jan 2023 10:23:07 +0100 Subject: [PATCH 02/56] reorganized the order of conditions --- openpype/pipeline/load/plugins.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index 4197ddb2ae..9b891a4da3 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -93,24 +93,33 @@ class LoaderPlugin(list): bool """ + plugin_repre_names = cls.get_representations() + plugin_families = cls.families + if not plugin_repre_names or not plugin_families: + return False + + repre_doc = context.get("representation") + if not repre_doc: + return False + + plugin_repre_names = set(plugin_repre_names) + if ( + "*" not in plugin_repre_names + and repre_doc["name"] not in plugin_repre_names + ): + return False + maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) if maj_version < 3: families = context["version"]["data"].get("families", []) else: families = context["subset"]["data"]["families"] - representation = context["representation"] - has_family = ( - "*" in cls.families or any( - family in cls.families for family in families - ) + plugin_families = set(plugin_families) + return ( + "*" in plugin_families + or any(family in plugin_families for family in families) ) - representations = cls.get_representations() - has_representation = ( - "*" in representations - or representation["name"] in representations - ) - return has_family and has_representation @classmethod def get_representations(cls): From a6df39c77b26c12ef236bf36a601c032633680ae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:55:11 +0100 Subject: [PATCH 03/56] fix 'update_context_data' in tray publisher --- openpype/hosts/traypublisher/api/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/traypublisher/api/pipeline.py b/openpype/hosts/traypublisher/api/pipeline.py index 0a8ddaa343..3264f52b0f 100644 --- a/openpype/hosts/traypublisher/api/pipeline.py +++ b/openpype/hosts/traypublisher/api/pipeline.py @@ -37,7 +37,7 @@ class TrayPublisherHost(HostBase, IPublishHost): return HostContext.get_context_data() def update_context_data(self, data, changes): - HostContext.save_context_data(data, changes) + HostContext.save_context_data(data) def set_project_name(self, project_name): # TODO Deregister project specific plugins and register new project From fa62e7954c0d061695651052651e55c6d1b59ef2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:56:18 +0100 Subject: [PATCH 04/56] removed unused methods --- openpype/pipeline/create/context.py | 78 ----------------------------- 1 file changed, 78 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9c468ae8fc..0d42c663d9 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -315,16 +315,6 @@ class AttributeValues(object): def changes(self): return self.calculate_changes(self._data, self._origin_data) - def apply_changes(self, changes): - for key, item in changes.items(): - old_value, new_value = item - if new_value is None: - if key in self: - self.pop(key) - - elif self.get(key) != new_value: - self[key] = new_value - class CreatorAttributeValues(AttributeValues): """Creator specific attribute values of an instance. @@ -458,21 +448,6 @@ class PublishAttributes: changes[key] = (value, None) return changes - def apply_changes(self, changes): - for key, item in changes.items(): - if isinstance(item, dict): - self._data[key].apply_changes(item) - continue - - old_value, new_value = item - if new_value is not None: - raise ValueError( - "Unexpected type \"{}\" expected None".format( - str(type(new_value)) - ) - ) - self.pop(key) - def set_publish_plugins(self, attr_plugins): """Set publish plugins attribute definitions.""" @@ -909,59 +884,6 @@ class CreatedInstance: return obj - def remote_changes(self): - """Prepare serializable changes on remote side. - - Returns: - Dict[str, Any]: Prepared changes that can be send to client side. - """ - - return { - "changes": self.changes(), - "asset_is_valid": self._asset_is_valid, - "task_is_valid": self._task_is_valid, - } - - def update_from_remote(self, remote_changes): - """Apply changes from remote side on client side. - - Args: - remote_changes (Dict[str, Any]): Changes created on remote side. - """ - - self._asset_is_valid = remote_changes["asset_is_valid"] - self._task_is_valid = remote_changes["task_is_valid"] - - changes = remote_changes["changes"] - creator_attributes = changes.pop("creator_attributes", None) or {} - publish_attributes = changes.pop("publish_attributes", None) or {} - if changes: - self.apply_changes(changes) - - if creator_attributes: - self.creator_attributes.apply_changes(creator_attributes) - - if publish_attributes: - self.publish_attributes.apply_changes(publish_attributes) - - def apply_changes(self, changes): - """Apply changes created via 'changes'. - - Args: - Dict[str, Tuple[Any, Any]]: Instance changes to apply. Same values - are kept untouched. - """ - - for key, item in changes.items(): - old_value, new_value = item - if new_value is None: - if key in self: - self.pop(key) - else: - current_value = self.get(key) - if current_value != new_value: - self[key] = new_value - class ConvertorItem(object): """Item representing convertor plugin. From 1496b74fc2668e8c075be1a771002b7b7c8c7fb5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:58:52 +0100 Subject: [PATCH 05/56] added helper object to handle changes --- openpype/pipeline/create/context.py | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 0d42c663d9..94230bda09 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -177,6 +177,99 @@ def prepare_failed_creator_operation_info( } +class ChangedItem(object): + def __init__(self, old_value, new_value): + self._old_value = copy.deepcopy(old_value) + self._new_value = copy.deepcopy(new_value) + self._changed = self._old_value != self._new_value + + old_is_dict = isinstance(old_value, dict) + new_is_dict = isinstance(new_value, dict) + children = {} + changed_keys = set() + available_keys = set() + if old_is_dict and new_is_dict: + old_keys = set(old_value.keys()) + new_keys = set(new_value.keys()) + available_keys = old_keys | new_keys + for key in available_keys: + item = ChangedItem( + old_value.get(key), new_value.get(key) + ) + children[key] = item + if item.changed or key not in old_keys or key not in new_keys: + changed_keys.add(key) + + elif old_is_dict: + available_keys = set(old_value.keys()) + changed_keys = set(available_keys) + for key in available_keys: + children[key] = ChangedItem(old_value.get(key), None) + + elif new_is_dict: + available_keys = set(new_value.keys()) + changed_keys = set(available_keys) + for key in available_keys: + children[key] = ChangedItem(None, new_value.get(key)) + + self._changed_keys = changed_keys + self._available_keys = available_keys + self._children = children + self._old_is_dict = old_is_dict + self._new_is_dict = new_is_dict + + def __getitem__(self, key): + return self._children[key] + + def __bool__(self): + return self._changed + + def __iter__(self): + for key in self._changed_keys: + yield key + + def keys(self): + return set(self._changed_keys) + + @property + def changed(self): + return self._changed + + @property + def changes(self): + if not self._old_is_dict and not self._new_is_dict: + return (self.old_value, self.new_value) + + old_value = self.old_value + new_value = self.new_value + output = {} + for key in self.changed_keys: + _old = None + _new = None + if self._old_is_dict: + _old = old_value.get(key) + if self._new_is_dict: + _new = new_value.get(key) + output[key] = (_old, _new) + return output + + @property + def changed_keys(self): + return set(self._changed_keys) + + @property + def available_keys(self): + return set(self._available_keys) + + @property + def old_value(self): + return copy.deepcopy(self._old_value) + + @property + def new_value(self): + return copy.deepcopy(self._new_value) + + class InstanceMember: """Representation of instance member. From e46042c2d646333ee8723cd98aaecbe06881f878 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:59:35 +0100 Subject: [PATCH 06/56] use the 'ChangedItem' object to handle changes --- openpype/pipeline/create/context.py | 71 ++++++----------------------- 1 file changed, 15 insertions(+), 56 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 94230bda09..7db4849300 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -384,6 +384,10 @@ class AttributeValues(object): """Pointer to attribute definitions.""" return self._attr_defs + @property + def origin_data(self): + return copy.deepcopy(self._origin_data) + def data_to_store(self): """Create new dictionary with data to store.""" output = {} @@ -395,19 +399,6 @@ class AttributeValues(object): output[key] = attr_def.default return output - @staticmethod - def calculate_changes(new_data, old_data): - """Calculate changes of 2 dictionary objects.""" - changes = {} - for key, new_value in new_data.items(): - old_value = old_data.get(key) - if old_value != new_value: - changes[key] = (old_value, new_value) - return changes - - def changes(self): - return self.calculate_changes(self._data, self._origin_data) - class CreatorAttributeValues(AttributeValues): """Creator specific attribute values of an instance. @@ -525,21 +516,9 @@ class PublishAttributes: output[key] = attr_value.data_to_store() return output - def changes(self): - """Return changes per each key.""" - - changes = {} - for key, attr_val in self._data.items(): - attr_changes = attr_val.changes() - if attr_changes: - if key not in changes: - changes[key] = {} - changes[key].update(attr_val) - - for key, value in self._origin_data.items(): - if key not in self._data: - changes[key] = (value, None) - return changes + @property + def origin_data(self): + return copy.deepcopy(self._origin_data) def set_publish_plugins(self, attr_plugins): """Set publish plugins attribute definitions.""" @@ -746,6 +725,10 @@ class CreatedInstance: return label return self.creator.get_group_label() + @property + def origin_data(self): + return copy.deepcopy(self._orig_data) + @property def creator_identifier(self): return self.creator.identifier @@ -837,29 +820,7 @@ class CreatedInstance: def changes(self): """Calculate and return changes.""" - changes = {} - new_keys = set() - for key, new_value in self._data.items(): - new_keys.add(key) - if key in ("creator_attributes", "publish_attributes"): - continue - - old_value = self._orig_data.get(key) - if old_value != new_value: - changes[key] = (old_value, new_value) - - creator_attr_changes = self.creator_attributes.changes() - if creator_attr_changes: - changes["creator_attributes"] = creator_attr_changes - - publish_attr_changes = self.publish_attributes.changes() - if publish_attr_changes: - changes["publish_attributes"] = publish_attr_changes - - for key, old_value in self._orig_data.items(): - if key not in new_keys: - changes[key] = (old_value, None) - return changes + return ChangedItem(self.origin_data, self.data_to_store()) def mark_as_stored(self): """Should be called when instance data are stored. @@ -1390,11 +1351,9 @@ class CreateContext: def context_data_changes(self): """Changes of attributes.""" - changes = {} - publish_attribute_changes = self._publish_attributes.changes() - if publish_attribute_changes: - changes["publish_attributes"] = publish_attribute_changes - return changes + + old_value = copy.deepcopy(self._original_context_data) + return ChangedItem(old_value, self.context_data_to_store()) def creator_adds_instance(self, instance): """Creator adds new instance to context. From f9f077722c161048f7f3328ce1ab5a2aab12f7e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 02:06:26 +0100 Subject: [PATCH 07/56] adde items method to extend dictionary approach --- openpype/pipeline/create/context.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 7db4849300..b6a6747f65 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -231,6 +231,14 @@ class ChangedItem(object): def keys(self): return set(self._changed_keys) + def items(self): + changes = self.changes + if isinstance(changes, tuple): + yield None, changes + else: + for item in changes.items(): + yield item + @property def changed(self): return self._changed From c88579591a7fb5f9d079b6ee656f5af2937266b7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 02:14:16 +0100 Subject: [PATCH 08/56] keep track of new/old keys --- openpype/pipeline/create/context.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index b6a6747f65..485252b4f8 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -188,6 +188,8 @@ class ChangedItem(object): children = {} changed_keys = set() available_keys = set() + new_keys = set() + old_keys = set() if old_is_dict and new_is_dict: old_keys = set(old_value.keys()) new_keys = set(new_value.keys()) @@ -201,13 +203,15 @@ class ChangedItem(object): changed_keys.add(key) elif old_is_dict: - available_keys = set(old_value.keys()) + old_keys = set(old_value.keys()) + available_keys = set(old_keys) changed_keys = set(available_keys) for key in available_keys: children[key] = ChangedItem(old_value.get(key), None) elif new_is_dict: - available_keys = set(new_value.keys()) + new_keys = set(new_value.keys()) + available_keys = set(new_keys) changed_keys = set(available_keys) for key in available_keys: children[key] = ChangedItem(None, new_value.get(key)) @@ -217,6 +221,8 @@ class ChangedItem(object): self._children = children self._old_is_dict = old_is_dict self._new_is_dict = new_is_dict + self._old_keys = old_keys + self._new_keys = new_keys def __getitem__(self, key): return self._children[key] @@ -225,11 +231,14 @@ class ChangedItem(object): return self._changed def __iter__(self): - for key in self._changed_keys: + for key in self.changed_keys: yield key + def get(self, key, default=None): + return self._children.get(key, default) + def keys(self): - return set(self._changed_keys) + return self.changed_keys def items(self): changes = self.changes @@ -269,6 +278,14 @@ class ChangedItem(object): def available_keys(self): return set(self._available_keys) + @property + def old_keys(self): + return set(self._old_keys) + + @property + def new_keys(self): + return set(self._new_keys) + @property def old_value(self): return copy.deepcopy(self._old_value) From 7c1a39d5e4f17d3abf94268d9da6dcf8d5eeea9c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 02:17:13 +0100 Subject: [PATCH 09/56] added 'is_dict' property --- openpype/pipeline/create/context.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 485252b4f8..e44e5db851 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -241,20 +241,23 @@ class ChangedItem(object): return self.changed_keys def items(self): - changes = self.changes - if isinstance(changes, tuple): - yield None, changes + if self.is_dict: + yield None, self.changes else: - for item in changes.items(): + for item in self.changes.items(): yield item @property def changed(self): return self._changed + @property + def is_dict(self): + return self._old_is_dict or self._new_is_dict + @property def changes(self): - if not self._old_is_dict and not self._new_is_dict: + if not self.is_dict: return (self.old_value, self.new_value) old_value = self.old_value From 44bcfc167a448fd4d50aec91aed1195f76430ccf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 26 Jan 2023 14:34:53 +0100 Subject: [PATCH 10/56] fix 'items' method --- openpype/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index e44e5db851..95e194fda1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -241,7 +241,7 @@ class ChangedItem(object): return self.changed_keys def items(self): - if self.is_dict: + if not self.is_dict: yield None, self.changes else: for item in self.changes.items(): From dc656050b7184c965774e289c5827c735533ab5d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 18:27:44 +0100 Subject: [PATCH 11/56] remove api.pi in openpype root --- openpype/api.py | 112 ------------------------------------------------ 1 file changed, 112 deletions(-) delete mode 100644 openpype/api.py diff --git a/openpype/api.py b/openpype/api.py deleted file mode 100644 index b60cd21d2b..0000000000 --- a/openpype/api.py +++ /dev/null @@ -1,112 +0,0 @@ -from .settings import ( - get_system_settings, - get_project_settings, - get_current_project_settings, - get_anatomy_settings, - - SystemSettings, - ProjectSettings -) -from .lib import ( - PypeLogger, - Logger, - Anatomy, - execute, - run_subprocess, - version_up, - get_asset, - get_workdir_data, - get_version_from_path, - get_last_version_from_path, - get_app_environments_for_context, - source_hash, - get_latest_version, - get_local_site_id, - change_openpype_mongo_url, - create_project_folders, - get_project_basic_paths -) - -from .lib.mongo import ( - get_default_components -) - -from .lib.applications import ( - ApplicationManager -) - -from .lib.avalon_context import ( - BuildWorkfile -) - -from . import resources - -from .plugin import ( - Extractor, - - ValidatePipelineOrder, - ValidateContentsOrder, - ValidateSceneOrder, - ValidateMeshOrder, -) - -# temporary fix, might -from .action import ( - get_errored_instances_from_context, - RepairAction, - RepairContextAction -) - - -__all__ = [ - "get_system_settings", - "get_project_settings", - "get_current_project_settings", - "get_anatomy_settings", - "get_project_basic_paths", - - "SystemSettings", - "ProjectSettings", - - "PypeLogger", - "Logger", - "Anatomy", - "execute", - "get_default_components", - "ApplicationManager", - "BuildWorkfile", - - # Resources - "resources", - - # plugin classes - "Extractor", - # ordering - "ValidatePipelineOrder", - "ValidateContentsOrder", - "ValidateSceneOrder", - "ValidateMeshOrder", - # action - "get_errored_instances_from_context", - "RepairAction", - "RepairContextAction", - - # get contextual data - "version_up", - "get_asset", - "get_workdir_data", - "get_version_from_path", - "get_last_version_from_path", - "get_app_environments_for_context", - "source_hash", - - "run_subprocess", - "get_latest_version", - - "get_local_site_id", - "change_openpype_mongo_url", - - "get_project_basic_paths", - "create_project_folders" - -] From 14ecf3aec18b2f89d6e0a53119d7e4adff3d5c84 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 18:33:32 +0100 Subject: [PATCH 12/56] remove last usage of openpype.api --- openpype/hosts/nuke/plugins/publish/collect_context_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_context_data.py b/openpype/hosts/nuke/plugins/publish/collect_context_data.py index 5a1cdcf49e..b487c946f0 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_context_data.py +++ b/openpype/hosts/nuke/plugins/publish/collect_context_data.py @@ -1,7 +1,7 @@ import os import nuke import pyblish.api -import openpype.api as api +from openpype.lib import get_version_from_path import openpype.hosts.nuke.api as napi from openpype.pipeline import KnownPublishError @@ -57,7 +57,7 @@ class CollectContextData(pyblish.api.ContextPlugin): "fps": root_node['fps'].value(), "currentFile": current_file, - "version": int(api.get_version_from_path(current_file)), + "version": int(get_version_from_path(current_file)), "host": pyblish.api.current_host(), "hostVersion": nuke.NUKE_VERSION_STRING From 0dbb63df944d81d7318f0ea503f00cf7cca1f48d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 11:54:55 +0100 Subject: [PATCH 13/56] modified change item a little --- openpype/pipeline/create/context.py | 324 ++++++++++++++++++++++------ 1 file changed, 254 insertions(+), 70 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 59314b9236..52e43f500e 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -183,92 +183,169 @@ def prepare_failed_creator_operation_info( } -class ChangedItem(object): +_EMPTY_VALUE = object() + + +class TrackChangesItem(object): + """Helper object to track changes in data. + + Has access to full old and new data and will create deep copy of them, + so it is not needed to create copy before passed in. + + Can work as a dictionary if old or new value is a dictionary. In + that case received object is another object of 'TrackChangesItem'. + + Goal is to be able to get old or new value as was or only changed values + or get information about removed/changed keys, and all of that on + any "dictionary level". + + ``` + # Example of possible usages + old_value = { + "key_1": "value_1", + "key_2": { + "key_sub_1": 1, + "key_sub_2": { + "enabled": True + } + }, + "key_3": "value_2" + } + + new_value = { + "key_1": "value_1", + "key_2": { + "key_sub_2": { + "enabled": False + }, + "key_sub_3": 3 + }, + "key_3": "value_3" + } + + changes = TrackChangesItem(old_value, new_value) + print(changes.changed) + >>> True + print(changes.changed_keys) + >>> {"key_2", "key_3"} + print(changes["key_2"]["key_sub_2"]["enabled"].changed) + >>> True + print(changes["key_2"].removed_keys) + >>> {"key_sub_1"} + print(changes["key_2"].available_keys) + >>> {"key_sub_1", "key_sub_2", "key_sub_3"} + print(changes.new_value == new_value) + >>> True + + only_changed_new_values = { + key: changes[key].new_value + for key in changes + } + ``` + + Args: + old_value (Any): Previous value. + new_value (Any): New value. + """ + def __init__(self, old_value, new_value): + self._changed = old_value != new_value + if old_value is _EMPTY_VALUE: + old_value = None + if new_value is _EMPTY_VALUE: + new_value = None self._old_value = copy.deepcopy(old_value) self._new_value = copy.deepcopy(new_value) - self._changed = self._old_value != self._new_value - old_is_dict = isinstance(old_value, dict) - new_is_dict = isinstance(new_value, dict) - children = {} - changed_keys = set() - available_keys = set() - new_keys = set() - old_keys = set() - if old_is_dict and new_is_dict: - old_keys = set(old_value.keys()) - new_keys = set(new_value.keys()) - available_keys = old_keys | new_keys - for key in available_keys: - item = ChangedItem( - old_value.get(key), new_value.get(key) - ) - children[key] = item - if item.changed or key not in old_keys or key not in new_keys: - changed_keys.add(key) + self._old_is_dict = isinstance(old_value, dict) + self._new_is_dict = isinstance(new_value, dict) - elif old_is_dict: - old_keys = set(old_value.keys()) - available_keys = set(old_keys) - changed_keys = set(available_keys) - for key in available_keys: - children[key] = ChangedItem(old_value.get(key), None) + self._old_keys = None + self._new_keys = None + self._available_keys = None + self._removed_keys = None - elif new_is_dict: - new_keys = set(new_value.keys()) - available_keys = set(new_keys) - changed_keys = set(available_keys) - for key in available_keys: - children[key] = ChangedItem(None, new_value.get(key)) + self._changed_keys = None - self._changed_keys = changed_keys - self._available_keys = available_keys - self._children = children - self._old_is_dict = old_is_dict - self._new_is_dict = new_is_dict - self._old_keys = old_keys - self._new_keys = new_keys + self._sub_items = None def __getitem__(self, key): - return self._children[key] + """Getter looks into subitems if object is dictionary.""" + + if self._sub_items is None: + self._prepare_sub_items() + return self._sub_items[key] def __bool__(self): + """Boolean of object is if old and new value are the same.""" + return self._changed - def __iter__(self): - for key in self.changed_keys: - yield key - def get(self, key, default=None): - return self._children.get(key, default) + """Try to get sub item.""" - def keys(self): - return self.changed_keys + if self._sub_items is None: + self._prepare_sub_items() + return self._sub_items.get(key, default) - def items(self): - if not self.is_dict: - yield None, self.changes - else: - for item in self.changes.items(): - yield item + @property + def old_value(self): + """Get copy of old value. + + Returns: + Any: Whatever old value was. + """ + + return copy.deepcopy(self._old_value) + + @property + def new_value(self): + """Get copy of new value. + + Returns: + Any: Whatever new value was. + """ + + return copy.deepcopy(self._new_value) @property def changed(self): + """Value changed. + + Returns: + bool: If data changed. + """ + return self._changed @property def is_dict(self): + """Object can be used as dictionary. + + Returns: + bool: When can be used that way. + """ + return self._old_is_dict or self._new_is_dict @property def changes(self): + """Get changes in raw data. + + This method should be used only if 'is_dict' value is 'True'. + + Returns: + Dict[str, Tuple[Any, Any]]: Changes are by key in tuple + (, ). If 'is_dict' is 'False' then + output is always empty dictionary. + """ + + output = {} if not self.is_dict: - return (self.old_value, self.new_value) + return output old_value = self.old_value new_value = self.new_value - output = {} for key in self.changed_keys: _old = None _new = None @@ -279,29 +356,135 @@ class ChangedItem(object): output[key] = (_old, _new) return output - @property - def changed_keys(self): - return set(self._changed_keys) - - @property - def available_keys(self): - return set(self._available_keys) - + # Methods/properties that can be used when 'is_dict' is 'True' @property def old_keys(self): + """Keys from old value. + + Empty set is returned if old value is not a dict. + + Returns: + Set[str]: Keys from old value. + """ + + if self._old_keys is None: + self._prepare_keys() return set(self._old_keys) @property def new_keys(self): + """Keys from new value. + + Empty set is returned if old value is not a dict. + + Returns: + Set[str]: Keys from new value. + """ + + if self._new_keys is None: + self._prepare_keys() return set(self._new_keys) @property - def old_value(self): - return copy.deepcopy(self._old_value) + def changed_keys(self): + """Keys that has changed from old to new value. + + Empty set is returned if both old and new value are not a dict. + + Returns: + Set[str]: Keys of changed keys. + """ + + if self._changed_keys is None: + self._prepare_sub_items() + return set(self._changed_keys) @property - def new_value(self): - return copy.deepcopy(self._new_value) + def available_keys(self): + """All keys that are available in old and new value. + + Empty set is returned if both old and new value are not a dict. + Output it is Union of 'old_keys' and 'new_keys'. + + Returns: + Set[str]: All keys from old and new value. + """ + + if self._available_keys is None: + self._prepare_keys() + return set(self._available_keys) + + @property + def removed_keys(self): + """Key that are not available in new value but were in old value. + + Returns: + Set[str]: All removed keys. + """ + + if self._removed_keys is None: + self._prepare_sub_items() + return set(self._removed_keys) + + def _prepare_keys(self): + old_keys = set() + new_keys = set() + if self._old_is_dict and self._new_is_dict: + old_keys = set(self._old_value.keys()) + new_keys = set(self._new_value.keys()) + + elif self._old_is_dict: + old_keys = set(self._old_value.keys()) + + elif self._new_is_dict: + new_keys = set(self._new_value.keys()) + + self._old_keys = old_keys + self._new_keys = new_keys + self._available_keys = old_keys | new_keys + self._removed_keys = old_keys - new_keys + + def _prepare_sub_items(self): + sub_items = {} + changed_keys = set() + + old_keys = self.old_keys + new_keys = self.new_keys + new_value = self.new_value + old_value = self.old_value + if self._old_is_dict and self._new_is_dict: + for key in self.available_keys: + item = TrackChangesItem( + old_value.get(key), new_value.get(key) + ) + sub_items[key] = item + if item.changed or key not in old_keys or key not in new_keys: + changed_keys.add(key) + + elif self._old_is_dict: + old_keys = set(old_value.keys()) + available_keys = set(old_keys) + changed_keys = set(available_keys) + for key in available_keys: + # NOTE Use '_EMPTY_VALUE' because old value could be 'None' + # which would result in "unchanged" item + sub_items[key] = TrackChangesItem( + old_value.get(key), _EMPTY_VALUE + ) + + elif self._new_is_dict: + new_keys = set(new_value.keys()) + available_keys = set(new_keys) + changed_keys = set(available_keys) + for key in available_keys: + # NOTE Use '_EMPTY_VALUE' because new value could be 'None' + # which would result in "unchanged" item + sub_items[key] = TrackChangesItem( + _EMPTY_VALUE, new_value.get(key) + ) + + self._sub_items = sub_items + self._changed_keys = changed_keys class InstanceMember: @@ -895,7 +1078,7 @@ class CreatedInstance: def changes(self): """Calculate and return changes.""" - return ChangedItem(self.origin_data, self.data_to_store()) + return TrackChangesItem(self._orig_data, self.data_to_store()) def mark_as_stored(self): """Should be called when instance data are stored. @@ -1519,8 +1702,9 @@ class CreateContext: def context_data_changes(self): """Changes of attributes.""" - old_value = copy.deepcopy(self._original_context_data) - return ChangedItem(old_value, self.context_data_to_store()) + return TrackChangesItem( + self._original_context_data, self.context_data_to_store() + ) def creator_adds_instance(self, instance): """Creator adds new instance to context. From 6ca987fdd259981f05bfcf0e2717a6e91682680e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 11:56:51 +0100 Subject: [PATCH 14/56] modified houdini to use new changes object --- openpype/hosts/houdini/api/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 61127bda57..0b9940a993 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -225,12 +225,12 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): self._add_instance_to_context(created_instance) def update_instances(self, update_list): - for created_inst, _changes in update_list: + for created_inst, changes in update_list: instance_node = hou.node(created_inst.get("instance_node")) new_values = { - key: new_value - for key, (_old_value, new_value) in _changes.items() + key: changes[key].new_value + for key in changes } imprint( instance_node, From 0b65168688be847861d89b4e12547608f30c627c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 12:17:07 +0100 Subject: [PATCH 15/56] fix other places using changes --- openpype/hosts/aftereffects/plugins/create/create_render.py | 4 ++-- openpype/hosts/max/api/plugin.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 8d38288257..5427edb44b 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -47,10 +47,10 @@ class RenderCreator(Creator): for created_inst, _changes in update_list: api.get_stub().imprint(created_inst.get("instance_id"), created_inst.data_to_store()) - subset_change = _changes.get("subset") + subset_change = _changes["subset"] if subset_change: api.get_stub().rename_item(created_inst.data["members"][0], - subset_change[1]) + subset_change.new_value) def remove_instances(self, instances): for instance in instances: diff --git a/openpype/hosts/max/api/plugin.py b/openpype/hosts/max/api/plugin.py index 4788bfd383..55603b26a0 100644 --- a/openpype/hosts/max/api/plugin.py +++ b/openpype/hosts/max/api/plugin.py @@ -78,12 +78,12 @@ class MaxCreator(Creator, MaxCreatorBase): self._add_instance_to_context(created_instance) def update_instances(self, update_list): - for created_inst, _changes in update_list: + for created_inst, changes in update_list: instance_node = created_inst.get("instance_node") new_values = { - key: new_value - for key, (_old_value, new_value) in _changes.items() + key: changes[key].new_value + for key in changes } imprint( instance_node, From 638375c9db7caca607375ed8b3f29eba76e5349d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 13:47:15 +0100 Subject: [PATCH 16/56] add missing attribute usage --- openpype/hosts/houdini/api/plugin.py | 2 +- openpype/hosts/max/api/plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 0b9940a993..f0985973a6 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -230,7 +230,7 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): new_values = { key: changes[key].new_value - for key in changes + for key in changes.changed_keys } imprint( instance_node, diff --git a/openpype/hosts/max/api/plugin.py b/openpype/hosts/max/api/plugin.py index 55603b26a0..c16d9e61ec 100644 --- a/openpype/hosts/max/api/plugin.py +++ b/openpype/hosts/max/api/plugin.py @@ -83,7 +83,7 @@ class MaxCreator(Creator, MaxCreatorBase): new_values = { key: changes[key].new_value - for key in changes + for key in changes.changed_keys } imprint( instance_node, From a9e139998ca49b7dc6135858333923f223a92c3b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 15:00:56 +0100 Subject: [PATCH 17/56] modified code example to also contain tests --- openpype/pipeline/create/context.py | 76 ++++++++++++++++------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 52e43f500e..f46b4eccdb 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -201,45 +201,53 @@ class TrackChangesItem(object): ``` # Example of possible usages - old_value = { - "key_1": "value_1", - "key_2": { - "key_sub_1": 1, - "key_sub_2": { - "enabled": True - } - }, - "key_3": "value_2" - } + >>> old_value = { + ... "key_1": "value_1", + ... "key_2": { + ... "key_sub_1": 1, + ... "key_sub_2": { + ... "enabled": True + ... } + ... }, + ... "key_3": "value_2" + ... } + >>> new_value = { + ... "key_1": "value_1", + ... "key_2": { + ... "key_sub_2": { + ... "enabled": False + ... }, + ... "key_sub_3": 3 + ... }, + ... "key_3": "value_3" + ... } - new_value = { - "key_1": "value_1", - "key_2": { - "key_sub_2": { - "enabled": False - }, - "key_sub_3": 3 - }, - "key_3": "value_3" - } + >>> changes = TrackChangesItem(old_value, new_value) + >>> changes.changed + True - changes = TrackChangesItem(old_value, new_value) - print(changes.changed) - >>> True - print(changes.changed_keys) - >>> {"key_2", "key_3"} - print(changes["key_2"]["key_sub_2"]["enabled"].changed) - >>> True - print(changes["key_2"].removed_keys) - >>> {"key_sub_1"} - print(changes["key_2"].available_keys) - >>> {"key_sub_1", "key_sub_2", "key_sub_3"} - print(changes.new_value == new_value) - >>> True + >>> changes["key_2"]["key_sub_1"].new_value is None + True + >>> list(sorted(changes.changed_keys)) + ['key_2', 'key_3'] + + >>> changes["key_2"]["key_sub_2"]["enabled"].changed + True + + >>> changes["key_2"].removed_keys + {'key_sub_1'} + + >>> list(sorted(changes["key_2"].available_keys)) + ['key_sub_1', 'key_sub_2', 'key_sub_3'] + + >>> changes.new_value == new_value + True + + # Get only changed values only_changed_new_values = { key: changes[key].new_value - for key in changes + for key in changes.changed_keys } ``` From 27773fbbf16fa8c9fa48678f1ec438d5f65f3b8d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:23:51 +0100 Subject: [PATCH 18/56] OP-4822 - added profile to disble check to Settings --- .../defaults/project_settings/deadline.json | 3 +- .../schema_project_deadline.json | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index ceb0b2e39a..11bb1aee2e 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -33,7 +33,8 @@ "limit": [], "jobInfo": {}, "pluginInfo": {}, - "scene_patches": [] + "scene_patches": [], + "disable_strict_check_profiles": [] }, "NukeSubmitDeadline": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index 08a505bd47..d38358773a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -195,6 +195,36 @@ ] } + }, + { + "type": "list", + "collapsible": true, + "key": "disable_strict_check_profiles", + "label": "Disable Strict Error Check profiles", + "use_label_wrap": true, + "docstring": "Set profile for disabling 'Strict Error Checking'", + "object_type": { + "type": "dict", + "children": [ + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + } + ] + } } ] }, From bd77b30c5c3fc3f21d8732d9309ec6c8f5d776f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:25:24 +0100 Subject: [PATCH 19/56] OP-4822 - added possibility to disable strict checking DL by default has Strict error checking, but some errors are not fatal. This allows to set profile based on Task and Subset values to temporarily disable Strict Error Checks --- .../plugins/publish/submit_maya_deadline.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 070d4eab18..a59738979c 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -35,6 +35,7 @@ from openpype.pipeline import legacy_io from openpype.hosts.maya.api.lib_rendersettings import RenderSettings from openpype.hosts.maya.api.lib import get_attr_in_layer +from openpype.lib.profiles_filtering import filter_profiles from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.tests.lib import is_in_tests @@ -64,6 +65,7 @@ class MayaPluginInfo(object): # Include all lights flag RenderSetupIncludeLights = attr.ib( default="1", validator=_validate_deadline_bool_value) + StrictErrorChecking = attr.ib(default="1") @attr.s @@ -104,6 +106,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): jobInfo = {} pluginInfo = {} group = "none" + disable_strict_check_profiles = [] def get_job_info(self): job_info = DeadlineJobInfo(Plugin="MayaBatch") @@ -219,6 +222,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): "renderSetupIncludeLights", default_rs_include_lights) if rs_include_lights not in {"1", "0", True, False}: rs_include_lights = default_rs_include_lights + strict_checking = self._get_strict_checking(instance) plugin_info = MayaPluginInfo( SceneFile=self.scene_path, Version=cmds.about(version=True), @@ -227,6 +231,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): RenderSetupIncludeLights=rs_include_lights, # noqa ProjectPath=context.data["workspaceDir"], UsingRenderLayers=True, + StrictErrorChecking=strict_checking ) plugin_payload = attr.asdict(plugin_info) @@ -748,6 +753,32 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): for file in exp: yield file + def _get_strict_checking(self, plugin_info, instance): + """Find profile to disable Strict Error Checking + + Args: + instance (dict) + Returns: + (int) - 1 if Strict (DL default), 0 for disabled Strict + """ + strict_checking = 1 + if not self.disable_strict_check_profiles: + return strict_checking + + task_data = instance.data["anatomyData"].get("task", {}) + key_values = { + "task_names": task_data.get("name"), + "task_types": task_data.get("type"), + "subsets": instance.data["subset"] + } + profile = filter_profiles(self.profiles, key_values, + logger=self.log) + + if profile: + strict_checking = 0 + + return strict_checking + def _format_tiles( filename, index, tiles_x, tiles_y, From f29c46f7c1cea1b0a9f3cf5b889394ab8bf03afd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:44:00 +0100 Subject: [PATCH 20/56] OP-4822 - fix arguments --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index a59738979c..a2a80c319b 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -753,7 +753,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): for file in exp: yield file - def _get_strict_checking(self, plugin_info, instance): + def _get_strict_checking(self, instance): """Find profile to disable Strict Error Checking Args: From 8a2cf2e6d93d1d9ed0887a84315e13608e859060 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:44:24 +0100 Subject: [PATCH 21/56] OP-4822 - fix wrong variable --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index a2a80c319b..c353e0b109 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -771,7 +771,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): "task_types": task_data.get("type"), "subsets": instance.data["subset"] } - profile = filter_profiles(self.profiles, key_values, + profile = filter_profiles(self.disable_strict_check_profiles, + key_values, logger=self.log) if profile: From 5df388912abba15c5a477aa47f06807cce0cf8fb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:44:43 +0100 Subject: [PATCH 22/56] OP-4822 - added logging --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index c353e0b109..3d1b9bcbf6 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -776,6 +776,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): logger=self.log) if profile: + self.log.debug("Disabling Strict Error Checking") strict_checking = 0 return strict_checking From 1ac6d9eadb892ca24a7ab2e5c556ae23970c4da9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 16:39:25 +0100 Subject: [PATCH 23/56] OP-4653 - added explicit 'use_layer_name' toggl Similar toggle is in AE, to keep same approach. This allow to create only single instance with layer name (previously only if multiple selected). --- .../hosts/photoshop/plugins/create/create_image.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index ca3bbfd27c..036a1127c1 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -73,13 +73,16 @@ class ImageCreator(Creator): groups_to_create.append(group) layer_name = '' - creating_multiple_groups = len(groups_to_create) > 1 + # use artist chosen option OR force layer if more subsets are created + # to differentiate them + use_layer_name = (pre_create_data.get("use_layer_name") or + len(groups_to_create) > 1) for group in groups_to_create: subset_name = subset_name_from_ui # reset to name from creator UI layer_names_in_hierarchy = [] created_group_name = self._clean_highlights(stub, group.name) - if creating_multiple_groups: + if use_layer_name: layer_name = re.sub( "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), "", @@ -137,7 +140,10 @@ class ImageCreator(Creator): label="Create only for selected"), BoolDef("create_multiple", default=True, - label="Create separate instance for each selected") + label="Create separate instance for each selected"), + BoolDef("use_layer_name", + default=False, + label="Use layer name in subset") ] return output From f0310e99f1799ec06c38e161ecd620cec6a1cbd3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 16:40:49 +0100 Subject: [PATCH 24/56] OP-4653 - updated description, fixed typos --- .../photoshop/plugins/create/create_image.py | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 036a1127c1..d15d7b2ab5 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -13,7 +13,11 @@ from openpype.hosts.photoshop.api.pipeline import cache_and_get_instances class ImageCreator(Creator): - """Creates image instance for publishing.""" + """Creates image instance for publishing. + + Result of 'image' instance is image of all visible layers, or image(s) of + selected layers. + """ identifier = "image" label = "Image" family = "image" @@ -59,9 +63,10 @@ class ImageCreator(Creator): try: group = stub.group_selected_layers(subset_name_from_ui) except: - raise ValueError("Cannot group locked Bakcground layer!") + raise ValueError("Cannot group locked Background layer!") groups_to_create.append(group) + # create empty group if nothing selected if not groups_to_create and not top_layers_to_wrap: group = stub.create_group(subset_name_from_ui) groups_to_create.append(group) @@ -148,7 +153,34 @@ class ImageCreator(Creator): return output def get_detail_description(self): - return """Creator for Image instances""" + return """Creator for Image instances + + Main publishable item in Photoshop will be of `image` family. Result of + this item (instance) is picture that could be loaded and used + in another DCCs (for example as single layer in composition in + AfterEffects, reference in Maya etc). + + There are couple of options what to publish: + - separate image per selected layer (or group of layers) + - one image for all selected layers + - all visible layers (groups) flattened into single image + + In most cases you would like to keep `Create only for selected` + toggled on and select what you would like to publish. + Toggling this option off will allow you to create instance for all + visible layers without a need to select them explicitly. + + Use 'Create separate instance for each selected' to create separate + images per selected layer (group of layers). + + 'Use layer name in subset' will explicitly add layer name into subset + name. Position of this name is configurable in + `project_settings/global/tools/creator/subset_name_profiles`. + If layer placeholder ({layer}) is not used in `subset_name_profiles` + but layer name should be used (set explicitly in UI or implicitly if + multiple images should be created), it is added in capitalized form + as a suffix to subset name. + """ def _handle_legacy(self, instance_data): """Converts old instances to new format.""" From 53c1c842f573622d3c5704bfc06a2c6d56cdde19 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:02:33 +0100 Subject: [PATCH 25/56] OP-4653 - standardize use_composition_name Follow more closely login in PS, eg. if {composition} placeholder not present in subset template and should be used, add capitalized. Clean composition name --- .../aftereffects/plugins/create/create_render.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 8d38288257..4ed9192964 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -11,6 +11,7 @@ from openpype.pipeline import ( ) from openpype.hosts.aftereffects.api.pipeline import cache_and_get_instances from openpype.lib import prepare_template_data +from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS class RenderCreator(Creator): @@ -82,10 +83,19 @@ class RenderCreator(Creator): "if 'useSelection' or create at least " "one composition." ) - + use_composition_name = (pre_create_data.get("use_composition_name") or + len(comps) > 1) for comp in comps: - if pre_create_data.get("use_composition_name"): - composition_name = comp.name + if use_composition_name: + if "{composition}" not in subset_name.lower(): + subset_name += "{Composition}" + + composition_name = re.sub( + "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), + "", + comp.name + ) + dynamic_fill = prepare_template_data({"composition": composition_name}) subset_name = subset_name_from_ui.format(**dynamic_fill) From f2930ed156aa1e877fe0478e5539e4dd16aaff62 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:04:06 +0100 Subject: [PATCH 26/56] OP-4653 - updated description in AE creator --- .../plugins/create/create_render.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 4ed9192964..25dadbecfd 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -15,6 +15,11 @@ from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS class RenderCreator(Creator): + """Creates 'render' instance for publishing. + + Result of 'render' instance is video or sequence of images for particular + composition based of configuration in its RenderQueue. + """ identifier = "render" label = "Render" family = "render" @@ -140,7 +145,32 @@ class RenderCreator(Creator): return output def get_detail_description(self): - return """Creator for Render instances""" + return """Creator for Render instances + + Main publishable item in AfterEffects will be of `render` family. + Result of this item (instance) is picture sequence or video that could + be a final delivery product or loaded and used in another DCCs. + + Select single composition and create instance of 'render' family or + turn off 'Use selection' to create instance for all compositions. + + 'Use composition name in subset' allows to explicitly add composition + name into created subset name. + + Position of composition name could be set in + `project_settings/global/tools/creator/subset_name_profiles` with some + form of '{composition}' placeholder. + + Composition name will be used implicitly if multiple composition should + be handled at same time. + + If {composition} placeholder is not us 'subset_name_profiles' + composition name will be capitalized and set at the end of subset name + if necessary. + + If composition name should be used, it will be cleaned up of characters + that would cause an issue in published file names. + """ def get_dynamic_data(self, variant, task_name, asset_doc, project_name, host_name, instance): From 993145e6f8bf4b829401a89f428d8821108e54a6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:16:55 +0100 Subject: [PATCH 27/56] OP-4653 - fix wrong name --- openpype/hosts/aftereffects/plugins/create/create_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 25dadbecfd..c95270a3bb 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -92,8 +92,8 @@ class RenderCreator(Creator): len(comps) > 1) for comp in comps: if use_composition_name: - if "{composition}" not in subset_name.lower(): - subset_name += "{Composition}" + if "{composition}" not in subset_name_from_ui.lower(): + subset_name_from_ui += "{Composition}" composition_name = re.sub( "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), From 31b137fa1d6257d97e170f475cd6d11fbdbfa8f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:17:27 +0100 Subject: [PATCH 28/56] OP-4653 - Hound --- .../plugins/create/create_render.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index c95270a3bb..a5f8dedace 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -146,28 +146,28 @@ class RenderCreator(Creator): def get_detail_description(self): return """Creator for Render instances - - Main publishable item in AfterEffects will be of `render` family. - Result of this item (instance) is picture sequence or video that could + + Main publishable item in AfterEffects will be of `render` family. + Result of this item (instance) is picture sequence or video that could be a final delivery product or loaded and used in another DCCs. - - Select single composition and create instance of 'render' family or + + Select single composition and create instance of 'render' family or turn off 'Use selection' to create instance for all compositions. - + 'Use composition name in subset' allows to explicitly add composition name into created subset name. - - Position of composition name could be set in + + Position of composition name could be set in `project_settings/global/tools/creator/subset_name_profiles` with some form of '{composition}' placeholder. - + Composition name will be used implicitly if multiple composition should be handled at same time. - - If {composition} placeholder is not us 'subset_name_profiles' + + If {composition} placeholder is not us 'subset_name_profiles' composition name will be capitalized and set at the end of subset name if necessary. - + If composition name should be used, it will be cleaned up of characters that would cause an issue in published file names. """ From 59010eb9a6c3a8abbabdd48dbb6c2a8563d9ac27 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:19:02 +0100 Subject: [PATCH 29/56] OP-4653 - refactor - move most important method higher --- .../plugins/create/create_render.py | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index a5f8dedace..10ded8b912 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -34,45 +34,6 @@ class RenderCreator(Creator): ["RenderCreator"] ["defaults"]) - def get_icon(self): - return resources.get_openpype_splash_filepath() - - def collect_instances(self): - for instance_data in cache_and_get_instances(self): - # legacy instances have family=='render' or 'renderLocal', use them - creator_id = (instance_data.get("creator_identifier") or - instance_data.get("family", '').replace("Local", '')) - if creator_id == self.identifier: - instance_data = self._handle_legacy(instance_data) - instance = CreatedInstance.from_existing( - instance_data, self - ) - self._add_instance_to_context(instance) - - def update_instances(self, update_list): - for created_inst, _changes in update_list: - api.get_stub().imprint(created_inst.get("instance_id"), - created_inst.data_to_store()) - subset_change = _changes.get("subset") - if subset_change: - api.get_stub().rename_item(created_inst.data["members"][0], - subset_change[1]) - - def remove_instances(self, instances): - for instance in instances: - self._remove_instance_from_context(instance) - self.host.remove_instance(instance) - - subset = instance.data["subset"] - comp_id = instance.data["members"][0] - comp = api.get_stub().get_item(comp_id) - if comp: - new_comp_name = comp.name.replace(subset, '') - if not new_comp_name: - new_comp_name = "dummyCompName" - api.get_stub().rename_item(comp_id, - new_comp_name) - def create(self, subset_name_from_ui, data, pre_create_data): stub = api.get_stub() # only after After Effects is up if pre_create_data.get("use_selection"): @@ -144,6 +105,45 @@ class RenderCreator(Creator): ] return output + def get_icon(self): + return resources.get_openpype_splash_filepath() + + def collect_instances(self): + for instance_data in cache_and_get_instances(self): + # legacy instances have family=='render' or 'renderLocal', use them + creator_id = (instance_data.get("creator_identifier") or + instance_data.get("family", '').replace("Local", '')) + if creator_id == self.identifier: + instance_data = self._handle_legacy(instance_data) + instance = CreatedInstance.from_existing( + instance_data, self + ) + self._add_instance_to_context(instance) + + def update_instances(self, update_list): + for created_inst, _changes in update_list: + api.get_stub().imprint(created_inst.get("instance_id"), + created_inst.data_to_store()) + subset_change = _changes.get("subset") + if subset_change: + api.get_stub().rename_item(created_inst.data["members"][0], + subset_change[1]) + + def remove_instances(self, instances): + for instance in instances: + self._remove_instance_from_context(instance) + self.host.remove_instance(instance) + + subset = instance.data["subset"] + comp_id = instance.data["members"][0] + comp = api.get_stub().get_item(comp_id) + if comp: + new_comp_name = comp.name.replace(subset, '') + if not new_comp_name: + new_comp_name = "dummyCompName" + api.get_stub().rename_item(comp_id, + new_comp_name) + def get_detail_description(self): return """Creator for Render instances From 0dd1a376b0463622dfe6aaab08057ce670524ee0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:19:52 +0100 Subject: [PATCH 30/56] OP-4653 - refactor - move most important method higher --- .../photoshop/plugins/create/create_image.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index d15d7b2ab5..8a103ea6c2 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -23,21 +23,6 @@ class ImageCreator(Creator): family = "image" description = "Image creator" - def collect_instances(self): - for instance_data in cache_and_get_instances(self): - # legacy instances have family=='image' - creator_id = (instance_data.get("creator_identifier") or - instance_data.get("family")) - - if creator_id == self.identifier: - instance_data = self._handle_legacy(instance_data) - layer = api.stub().get_layer(instance_data["members"][0]) - instance_data["layer"] = layer - instance = CreatedInstance.from_existing( - instance_data, self - ) - self._add_instance_to_context(instance) - def create(self, subset_name_from_ui, data, pre_create_data): groups_to_create = [] top_layers_to_wrap = [] @@ -120,6 +105,21 @@ class ImageCreator(Creator): stub.rename_layer(group.id, stub.PUBLISH_ICON + created_group_name) + def collect_instances(self): + for instance_data in cache_and_get_instances(self): + # legacy instances have family=='image' + creator_id = (instance_data.get("creator_identifier") or + instance_data.get("family")) + + if creator_id == self.identifier: + instance_data = self._handle_legacy(instance_data) + layer = api.stub().get_layer(instance_data["members"][0]) + instance_data["layer"] = layer + instance = CreatedInstance.from_existing( + instance_data, self + ) + self._add_instance_to_context(instance) + def update_instances(self, update_list): self.log.debug("update_list:: {}".format(update_list)) for created_inst, _changes in update_list: From 3a8c36e0570e5554d0d9ae7afd073fdde43294ff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 18:28:29 +0100 Subject: [PATCH 31/56] OP-4822 - updated settings After discussion profile is too complicated, removed, replaced with simple boolean --- .../defaults/project_settings/deadline.json | 2 +- .../schema_project_deadline.json | 32 +++---------------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 11bb1aee2e..0a4318a659 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -34,7 +34,7 @@ "jobInfo": {}, "pluginInfo": {}, "scene_patches": [], - "disable_strict_check_profiles": [] + "strict_error_checking": true }, "NukeSubmitDeadline": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index d38358773a..03f6489a41 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -197,34 +197,10 @@ } }, { - "type": "list", - "collapsible": true, - "key": "disable_strict_check_profiles", - "label": "Disable Strict Error Check profiles", - "use_label_wrap": true, - "docstring": "Set profile for disabling 'Strict Error Checking'", - "object_type": { - "type": "dict", - "children": [ - { - "key": "task_types", - "label": "Task types", - "type": "task-types-enum" - }, - { - "key": "task_names", - "label": "Task names", - "type": "list", - "object_type": "text" - }, - { - "key": "subsets", - "label": "Subset names", - "type": "list", - "object_type": "text" - } - ] - } + "type": "boolean", + "key": "strict_error_checking", + "label": "Strict Error Checking", + "default": true } ] }, From 214ef5104dfe17619c5f04e466d2bb70a9fbd195 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 18:33:40 +0100 Subject: [PATCH 32/56] OP-4822 - changed strict error checking to boolean flag Added boolean flag to render instance to allow artist to change it. --- .../maya/plugins/create/create_render.py | 4 +++ .../plugins/publish/submit_maya_deadline.py | 35 +++---------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_render.py b/openpype/hosts/maya/plugins/create/create_render.py index 8375149442..387b7321b9 100644 --- a/openpype/hosts/maya/plugins/create/create_render.py +++ b/openpype/hosts/maya/plugins/create/create_render.py @@ -54,6 +54,7 @@ class CreateRender(plugin.Creator): tileRendering (bool): Instance is set to tile rendering mode. We won't submit actual render, but we'll make publish job to wait for Tile Assembly job done and then publish. + strict_error_checking (bool): Enable/disable error checking on DL See Also: https://pype.club/docs/artist_hosts_maya#creating-basic-render-setup @@ -271,6 +272,9 @@ class CreateRender(plugin.Creator): secondary_pool = pool_setting["secondary_pool"] self.data["secondaryPool"] = self._set_default_pool(pool_names, secondary_pool) + strict_error_checking = maya_submit_dl.get("strict_error_checking", + True) + self.data["strict_error_checking"] = strict_error_checking if muster_enabled: self.log.info(">>> Loading Muster credentials ...") diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 3d1b9bcbf6..9fef816e1c 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -65,7 +65,7 @@ class MayaPluginInfo(object): # Include all lights flag RenderSetupIncludeLights = attr.ib( default="1", validator=_validate_deadline_bool_value) - StrictErrorChecking = attr.ib(default="1") + StrictErrorChecking = attr.ib(default=True) @attr.s @@ -222,7 +222,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): "renderSetupIncludeLights", default_rs_include_lights) if rs_include_lights not in {"1", "0", True, False}: rs_include_lights = default_rs_include_lights - strict_checking = self._get_strict_checking(instance) + strict_error_checking = instance.data.get("strict_error_checking", + True) plugin_info = MayaPluginInfo( SceneFile=self.scene_path, Version=cmds.about(version=True), @@ -231,7 +232,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): RenderSetupIncludeLights=rs_include_lights, # noqa ProjectPath=context.data["workspaceDir"], UsingRenderLayers=True, - StrictErrorChecking=strict_checking + StrictErrorChecking=strict_error_checking ) plugin_payload = attr.asdict(plugin_info) @@ -753,34 +754,6 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): for file in exp: yield file - def _get_strict_checking(self, instance): - """Find profile to disable Strict Error Checking - - Args: - instance (dict) - Returns: - (int) - 1 if Strict (DL default), 0 for disabled Strict - """ - strict_checking = 1 - if not self.disable_strict_check_profiles: - return strict_checking - - task_data = instance.data["anatomyData"].get("task", {}) - key_values = { - "task_names": task_data.get("name"), - "task_types": task_data.get("type"), - "subsets": instance.data["subset"] - } - profile = filter_profiles(self.disable_strict_check_profiles, - key_values, - logger=self.log) - - if profile: - self.log.debug("Disabling Strict Error Checking") - strict_checking = 0 - - return strict_checking - def _format_tiles( filename, index, tiles_x, tiles_y, From 463f770f2d19dcb1bfb184412d1664a8134b7502 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 18:40:53 +0100 Subject: [PATCH 33/56] OP-4822 - Hound --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 9fef816e1c..e7c1899513 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -35,7 +35,6 @@ from openpype.pipeline import legacy_io from openpype.hosts.maya.api.lib_rendersettings import RenderSettings from openpype.hosts.maya.api.lib import get_attr_in_layer -from openpype.lib.profiles_filtering import filter_profiles from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.tests.lib import is_in_tests From 31173abd4fc75c6caca0be712f50faf06a24ed56 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 6 Feb 2023 13:39:04 +0100 Subject: [PATCH 34/56] OP-4702 - fix dirmap remote_site_dir started to return platform dict --- openpype/host/dirmap.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpype/host/dirmap.py b/openpype/host/dirmap.py index 88d68f27bf..347c5fbf85 100644 --- a/openpype/host/dirmap.py +++ b/openpype/host/dirmap.py @@ -8,6 +8,7 @@ exists is used. import os from abc import ABCMeta, abstractmethod +import platform import six @@ -187,11 +188,19 @@ class HostDirmap(object): self.log.debug("local overrides {}".format(active_overrides)) self.log.debug("remote overrides {}".format(remote_overrides)) + current_platform = platform.system().lower() for root_name, active_site_dir in active_overrides.items(): remote_site_dir = ( remote_overrides.get(root_name) or sync_settings["sites"][remote_site]["root"][root_name] ) + + if isinstance(remote_site_dir, dict): + remote_site_dir = remote_site_dir.get(current_platform) + + if not remote_site_dir: + continue + if os.path.isdir(active_site_dir): if "destination-path" not in mapping: mapping["destination-path"] = [] From 9110f7055415995637039c2da8ac2c1afe2bf9c5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:02:09 +0100 Subject: [PATCH 35/56] use safe access to "subset" in AE --- openpype/hosts/aftereffects/plugins/create/create_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 5427edb44b..f661b3bfd7 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -47,7 +47,7 @@ class RenderCreator(Creator): for created_inst, _changes in update_list: api.get_stub().imprint(created_inst.get("instance_id"), created_inst.data_to_store()) - subset_change = _changes["subset"] + subset_change = _changes.get("subset") if subset_change: api.get_stub().rename_item(created_inst.data["members"][0], subset_change.new_value) From 22e4b9862b7aaf1e30b1d1d86fcccff2d2ec6799 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:05:27 +0100 Subject: [PATCH 36/56] added 'order' attribute to creators which is used to process them in specific order --- openpype/pipeline/create/creator_plugins.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 1f92056b23..1aba3f8770 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -107,7 +107,11 @@ class SubsetConvertorPlugin(object): @property def create_context(self): - """Quick access to create context.""" + """Quick access to create context. + + Returns: + CreateContext: Context which initialized the plugin. + """ return self._create_context @@ -157,6 +161,10 @@ class BaseCreator: # Cached group label after first call 'get_group_label' _cached_group_label = None + # Order in which will be plugin executed (collect & update instances) + # less == earlier -> Order '90' will be processed before '100' + order = 100 + # Variable to store logger _log = None From fd31d7815a69b51cfe7bc3ecc0d6a48f13c6e817 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:05:46 +0100 Subject: [PATCH 37/56] added helper methods to get sorted creators --- openpype/pipeline/create/context.py | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index f260e483d9..04db1df790 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1237,6 +1237,37 @@ class CreateContext: """Access to global publish attributes.""" return self._publish_attributes + def get_sorted_creators(self, identifiers=None): + """Sorted creators by 'order' attribute. + + Returns: + List[BaseCreator]: Sorted creator plugins by 'order' value. + """ + + if identifiers is not None: + identifiers = set(identifiers) + creators = [ + creator + for identifier, creator in self.creators.items() + if identifier in identifiers + ] + else: + creators = self.creators.values() + + return sorted( + creators, key=lambda creator: creator.order + ) + + @property + def sorted_creators(self): + return self.get_sorted_creators() + + @property + def sorted_autocreators(self): + return sorted( + self.autocreators.values(), key=lambda creator: creator.order + ) + @classmethod def get_host_misssing_methods(cls, host): """Collect missing methods from host. From 101bbef42d0532dcca235e145cfdae7dc7d22436 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:07:16 +0100 Subject: [PATCH 38/56] added helper method to remove instance --- openpype/pipeline/create/context.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 04db1df790..9d8bb63804 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1630,6 +1630,9 @@ class CreateContext: ) ]) + def _remove_instance(self, instance): + self._instances_by_id.pop(instance.id, None) + def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. @@ -1641,7 +1644,7 @@ class CreateContext: from scene metadata. """ - self._instances_by_id.pop(instance.id, None) + self._remove_instance(instance) def add_convertor_item(self, convertor_identifier, label): self.convertor_items_by_id[convertor_identifier] = ConvertorItem( From 3316417173b1f0bdd9a49f91cd210ca4f85236ef Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:07:50 +0100 Subject: [PATCH 39/56] process creators in their order --- openpype/pipeline/create/context.py | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9d8bb63804..5682fb3115 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1688,7 +1688,7 @@ class CreateContext: # Collect instances error_message = "Collection of instances for creator {} failed. {}" failed_info = [] - for creator in self.creators.values(): + for creator in self.sorted_creators: label = creator.label identifier = creator.identifier failed = False @@ -1760,7 +1760,8 @@ class CreateContext: error_message = "Failed to run AutoCreator with identifier \"{}\". {}" failed_info = [] - for identifier, creator in self.autocreators.items(): + for creator in self.sorted_autocreators: + identifier = creator.identifier label = creator.label failed = False add_traceback = False @@ -1865,19 +1866,26 @@ class CreateContext: """Save instance specific values.""" instances_by_identifier = collections.defaultdict(list) for instance in self._instances_by_id.values(): + instance_changes = instance.changes() + if not instance_changes: + continue + identifier = instance.creator_identifier - instances_by_identifier[identifier].append(instance) + instances_by_identifier[identifier].append( + UpdateData(instance, instance_changes) + ) + + if not instances_by_identifier: + return error_message = "Instances update of creator \"{}\" failed. {}" failed_info = [] - for identifier, creator_instances in instances_by_identifier.items(): - update_list = [] - for instance in creator_instances: - instance_changes = instance.changes() - if instance_changes: - update_list.append(UpdateData(instance, instance_changes)) - creator = self.creators[identifier] + for creator in self.get_sorted_creators( + instances_by_identifier.keys() + ): + identifier = creator.identifier + update_list = instances_by_identifier[identifier] if not update_list: continue @@ -1913,9 +1921,13 @@ class CreateContext: def remove_instances(self, instances): """Remove instances from context. + All instances that don't have creator identifier leading to existing + creator are just removed from context. + Args: - instances(list): Instances that should be removed - from context. + instances(List[CreatedInstance]): Instances that should be removed. + Remove logic is done using creator, which may require to + do other cleanup than just remove instance from context. """ instances_by_identifier = collections.defaultdict(list) @@ -1925,8 +1937,13 @@ class CreateContext: error_message = "Instances removement of creator \"{}\" failed. {}" failed_info = [] - for identifier, creator_instances in instances_by_identifier.items(): - creator = self.creators.get(identifier) + # Remove instances by creator plugin order + for creator in self.get_sorted_creators( + instances_by_identifier.keys() + ): + identifier = creator.identifier + creator_instances = instances_by_identifier[identifier] + label = creator.label failed = False add_traceback = False @@ -1969,6 +1986,7 @@ class CreateContext: family(str): Instance family for which should be attribute definitions returned. """ + if family not in self._attr_plugins_by_family: import pyblish.logic @@ -1984,7 +2002,13 @@ class CreateContext: return self._attr_plugins_by_family[family] def _get_publish_plugins_with_attr_for_context(self): - """Publish plugins attributes for Context plugins.""" + """Publish plugins attributes for Context plugins. + + Returns: + List[pyblish.api.Plugin]: Publish plugins that have attribute + definitions for context. + """ + plugins = [] for plugin in self.plugins_with_defs: if not plugin.__instanceEnabled__: @@ -2009,7 +2033,7 @@ class CreateContext: return self._collection_shared_data def run_convertor(self, convertor_identifier): - """Run convertor plugin by it's idenfitifier. + """Run convertor plugin by identifier. Conversion is skipped if convertor is not available. From c5be74156652323a43f05e1d486ed77dfd0f0987 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:08:07 +0100 Subject: [PATCH 40/56] handle instances without available creator --- openpype/pipeline/create/context.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 5682fb3115..c978fcc2e1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1935,6 +1935,12 @@ class CreateContext: identifier = instance.creator_identifier instances_by_identifier[identifier].append(instance) + # Just remove instances from context if creator is not available + missing_creators = set(instances_by_identifier) - set(self.creators) + for identifier in missing_creators: + for instance in instances_by_identifier[identifier]: + self._remove_instance(instance) + error_message = "Instances removement of creator \"{}\" failed. {}" failed_info = [] # Remove instances by creator plugin order @@ -2046,7 +2052,7 @@ class CreateContext: convertor.convert() def run_convertors(self, convertor_identifiers): - """Run convertor plugins by idenfitifiers. + """Run convertor plugins by identifiers. Conversion is skipped if convertor is not available. It is recommended to trigger reset after conversion to reload instances. From c7f051db20c45ab9ba8b835fea3f858051a93df3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:08:42 +0100 Subject: [PATCH 41/56] added 'show_order' attribute to 'Creator' so show order can be different than processing --- openpype/pipeline/create/creator_plugins.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 1aba3f8770..53acb618ed 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -497,6 +497,17 @@ class Creator(BaseCreator): # - similar to instance attribute definitions pre_create_attr_defs = [] + @property + def show_order(self): + """Order in which is creator shown in UI. + + Returns: + int: Order in which is creator shown (less == earlier). By default + is using Creator's 'order' or processing. + """ + + return self.order + @abstractmethod def create(self, subset_name, instance_data, pre_create_data): """Create new instance and store it. From b67181c4e012ac370a1ca284e2112e92589772d9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:09:32 +0100 Subject: [PATCH 42/56] added show order to publisher UI --- openpype/tools/publisher/constants.py | 2 ++ openpype/tools/publisher/control.py | 10 ++++++++-- openpype/tools/publisher/widgets/create_widget.py | 10 +++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/openpype/tools/publisher/constants.py b/openpype/tools/publisher/constants.py index e9fdd4774a..b2bfd7dd5c 100644 --- a/openpype/tools/publisher/constants.py +++ b/openpype/tools/publisher/constants.py @@ -24,6 +24,7 @@ CREATOR_THUMBNAIL_ENABLED_ROLE = QtCore.Qt.UserRole + 5 FAMILY_ROLE = QtCore.Qt.UserRole + 6 GROUP_ROLE = QtCore.Qt.UserRole + 7 CONVERTER_IDENTIFIER_ROLE = QtCore.Qt.UserRole + 8 +CREATOR_SORT_ROLE = QtCore.Qt.UserRole + 9 __all__ = ( @@ -36,6 +37,7 @@ __all__ = ( "IS_GROUP_ROLE", "CREATOR_IDENTIFIER_ROLE", "CREATOR_THUMBNAIL_ENABLED_ROLE", + "CREATOR_SORT_ROLE", "FAMILY_ROLE", "GROUP_ROLE", "CONVERTER_IDENTIFIER_ROLE", diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 7c8da66744..d1ee3ea8aa 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -832,7 +832,8 @@ class CreatorItem: default_variants, create_allow_context_change, create_allow_thumbnail, - pre_create_attributes_defs + show_order, + pre_create_attributes_defs, ): self.identifier = identifier self.creator_type = creator_type @@ -846,6 +847,7 @@ class CreatorItem: self.default_variants = default_variants self.create_allow_context_change = create_allow_context_change self.create_allow_thumbnail = create_allow_thumbnail + self.show_order = show_order self.pre_create_attributes_defs = pre_create_attributes_defs def get_group_label(self): @@ -869,6 +871,7 @@ class CreatorItem: pre_create_attr_defs = None create_allow_context_change = None create_allow_thumbnail = None + show_order = creator.order if creator_type is CreatorTypes.artist: description = creator.get_description() detail_description = creator.get_detail_description() @@ -877,6 +880,7 @@ class CreatorItem: pre_create_attr_defs = creator.get_pre_create_attr_defs() create_allow_context_change = creator.create_allow_context_change create_allow_thumbnail = creator.create_allow_thumbnail + show_order = creator.show_order identifier = creator.identifier return cls( @@ -892,7 +896,8 @@ class CreatorItem: default_variants, create_allow_context_change, create_allow_thumbnail, - pre_create_attr_defs + show_order, + pre_create_attr_defs, ) def to_data(self): @@ -915,6 +920,7 @@ class CreatorItem: "default_variants": self.default_variants, "create_allow_context_change": self.create_allow_context_change, "create_allow_thumbnail": self.create_allow_thumbnail, + "show_order": self.show_order, "pre_create_attributes_defs": pre_create_attributes_defs, } diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 07b124f616..994b9ac912 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -18,9 +18,10 @@ from .tasks_widget import CreateWidgetTasksWidget from .precreate_widget import PreCreateWidget from ..constants import ( VARIANT_TOOLTIP, - CREATOR_IDENTIFIER_ROLE, FAMILY_ROLE, + CREATOR_IDENTIFIER_ROLE, CREATOR_THUMBNAIL_ENABLED_ROLE, + CREATOR_SORT_ROLE, ) SEPARATORS = ("---separator---", "---") @@ -441,7 +442,8 @@ class CreateWidget(QtWidgets.QWidget): # Add new families new_creators = set() - for identifier, creator_item in self._controller.creator_items.items(): + creator_items_by_identifier = self._controller.creator_items + for identifier, creator_item in creator_items_by_identifier.items(): if creator_item.creator_type != "artist": continue @@ -457,6 +459,7 @@ class CreateWidget(QtWidgets.QWidget): self._creators_model.appendRow(item) item.setData(creator_item.label, QtCore.Qt.DisplayRole) + item.setData(creator_item.show_order, CREATOR_SORT_ROLE) item.setData(identifier, CREATOR_IDENTIFIER_ROLE) item.setData( creator_item.create_allow_thumbnail, @@ -482,8 +485,9 @@ class CreateWidget(QtWidgets.QWidget): index = indexes[0] identifier = index.data(CREATOR_IDENTIFIER_ROLE) + create_item = creator_items_by_identifier.get(identifier) - self._set_creator_by_identifier(identifier) + self._set_creator(create_item) def _on_plugins_refresh(self): # Trigger refresh only if is visible From 7e1450e95d1cbc2d3df206235a13f280c3c642cb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:09:53 +0100 Subject: [PATCH 43/56] modified proxy filter for sorting of creators --- openpype/tools/publisher/widgets/create_widget.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 994b9ac912..bc1f1ec4f7 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -91,6 +91,15 @@ class CreatorShortDescWidget(QtWidgets.QWidget): self._description_label.setText(description) +class CreatorsProxyModel(QtCore.QSortFilterProxyModel): + def lessThan(self, left, right): + l_show_order = left.data(CREATOR_SORT_ROLE) + r_show_order = right.data(CREATOR_SORT_ROLE) + if l_show_order == r_show_order: + return super(CreatorsProxyModel, self).lessThan(left, right) + return l_show_order < r_show_order + + class CreateWidget(QtWidgets.QWidget): def __init__(self, controller, parent=None): super(CreateWidget, self).__init__(parent) @@ -142,7 +151,7 @@ class CreateWidget(QtWidgets.QWidget): creators_view = QtWidgets.QListView(creators_view_widget) creators_model = QtGui.QStandardItemModel() - creators_sort_model = QtCore.QSortFilterProxyModel() + creators_sort_model = CreatorsProxyModel() creators_sort_model.setSourceModel(creators_model) creators_view.setModel(creators_sort_model) From dfb718741355d5b0c4cad8a9a4947d2dce2eb606 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:10:07 +0100 Subject: [PATCH 44/56] removed window title from creator widget --- openpype/tools/publisher/widgets/create_widget.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index bc1f1ec4f7..dbf075c216 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -104,8 +104,6 @@ class CreateWidget(QtWidgets.QWidget): def __init__(self, controller, parent=None): super(CreateWidget, self).__init__(parent) - self.setWindowTitle("Create new instance") - self._controller = controller self._asset_name = None From 310456ec137c621554b91d2a831f875489059106 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:10:24 +0100 Subject: [PATCH 45/56] fix creator_items cache cleanup --- openpype/tools/publisher/control.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index d1ee3ea8aa..435db5fcb3 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1508,9 +1508,6 @@ class BasePublisherController(AbstractPublisherController): def _reset_attributes(self): """Reset most of attributes that can be reset.""" - # Reset creator items - self._creator_items = None - self.publish_is_running = False self.publish_has_validated = False self.publish_has_crashed = False @@ -1766,6 +1763,8 @@ class PublisherController(BasePublisherController): self._resetting_plugins = True self._create_context.reset_plugins() + # Reset creator items + self._creator_items = None self._resetting_plugins = False From cd1b02c59504dab76294663b326851cf70920d7e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:14:53 +0100 Subject: [PATCH 46/56] move batch creator after simple creators --- .../hosts/traypublisher/plugins/create/create_movie_batch.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py b/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py index 1dc4bad9b3..d077131e4c 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py +++ b/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py @@ -33,6 +33,8 @@ class BatchMovieCreator(TrayPublishCreator): create_allow_context_change = False version_regex = re.compile(r"^(.+)_v([0-9]+)$") + # Position batch creator after simple creators + order = 110 def __init__(self, project_settings, *args, **kwargs): super(BatchMovieCreator, self).__init__(project_settings, From b51a2a72579966e721e268de68ad1d01e62c93a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:24:30 +0100 Subject: [PATCH 47/56] added more docstrings --- openpype/pipeline/create/context.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index c978fcc2e1..0ee70f39cb 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1240,6 +1240,10 @@ class CreateContext: def get_sorted_creators(self, identifiers=None): """Sorted creators by 'order' attribute. + Args: + identifiers (Iterable[str]): Filter creators by identifiers. All + creators are returned if 'None' is passed. + Returns: List[BaseCreator]: Sorted creator plugins by 'order' value. """ @@ -1260,10 +1264,22 @@ class CreateContext: @property def sorted_creators(self): + """Sorted creators by 'order' attribute. + + Returns: + List[BaseCreator]: Sorted creator plugins by 'order' value. + """ + return self.get_sorted_creators() @property def sorted_autocreators(self): + """Sorted auto-creators by 'order' attribute. + + Returns: + List[AutoCreator]: Sorted plugins by 'order' value. + """ + return sorted( self.autocreators.values(), key=lambda creator: creator.order ) From cba888e66e5e874cfa84add8f396f5a72d1b3e40 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Feb 2023 20:02:36 +0100 Subject: [PATCH 48/56] small docstring/comments enhancements --- openpype/pipeline/create/context.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index f46b4eccdb..427cabce67 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -252,12 +252,13 @@ class TrackChangesItem(object): ``` Args: - old_value (Any): Previous value. + old_value (Any): Old value. new_value (Any): New value. """ def __init__(self, old_value, new_value): self._changed = old_value != new_value + # Resolve if value is '_EMPTY_VALUE' after comparison of the values if old_value is _EMPTY_VALUE: old_value = None if new_value is _EMPTY_VALUE: @@ -412,7 +413,7 @@ class TrackChangesItem(object): """All keys that are available in old and new value. Empty set is returned if both old and new value are not a dict. - Output it is Union of 'old_keys' and 'new_keys'. + Output is Union of 'old_keys' and 'new_keys'. Returns: Set[str]: All keys from old and new value. From e7d60288a12a86e028b692da455ae5ca94ade552 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:15:43 +0100 Subject: [PATCH 49/56] Fix - added missed scopes for Slack bot --- openpype/modules/slack/manifest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 7a65cc5915..233c39fbaf 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,6 +19,8 @@ oauth_config: - chat:write.public - files:write - channels:read + - users:read + - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 1a7f7d310ec02be47e6e95e5e8781d72ba181a33 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:21:55 +0100 Subject: [PATCH 50/56] OP-4653 - changed exception Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/photoshop/plugins/create/create_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 8a103ea6c2..9bf47733f5 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -48,7 +48,7 @@ class ImageCreator(Creator): try: group = stub.group_selected_layers(subset_name_from_ui) except: - raise ValueError("Cannot group locked Background layer!") + raise CreatorError("Cannot group locked Background layer!") groups_to_create.append(group) # create empty group if nothing selected From 21d745cc3cc9ad288a1aa0d7eb6329a5aeea7f9e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:25:29 +0100 Subject: [PATCH 51/56] OP-4822 - remove unwanted variable Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index e7c1899513..ed37ff1897 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -105,7 +105,6 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): jobInfo = {} pluginInfo = {} group = "none" - disable_strict_check_profiles = [] def get_job_info(self): job_info = DeadlineJobInfo(Plugin="MayaBatch") From 3156cc542508949c7786809db94feb0de4c7da35 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:39:28 +0100 Subject: [PATCH 52/56] OP-4653 - added missed import for CreatorError --- openpype/hosts/photoshop/plugins/create/create_image.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 9bf47733f5..198c1586dc 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -5,7 +5,8 @@ from openpype.lib import BoolDef from openpype.pipeline import ( Creator, CreatedInstance, - legacy_io + legacy_io, + CreatorError ) from openpype.lib import prepare_template_data from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS From b28f91769367ad9b64eadb9d04f18bb2b4529c20 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 13:37:50 +0100 Subject: [PATCH 53/56] Fix - remove minor part in toml Causes issue in create_env and new Poetry --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6e88404700..2fc4f6fe39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1-nightly.3" # OpenPype +version = "3.15.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 6e55202349b5ea4f62001a586e44e8306143fbee Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 8 Feb 2023 14:23:10 +0100 Subject: [PATCH 54/56] change used method names from 'isAlive' to 'is_alive' --- openpype/modules/ftrack/ftrack_server/event_server_cli.py | 6 +++--- openpype/modules/ftrack/tray/login_dialog.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/modules/ftrack/ftrack_server/event_server_cli.py b/openpype/modules/ftrack/ftrack_server/event_server_cli.py index 25ebad6658..ad7ffd8e25 100644 --- a/openpype/modules/ftrack/ftrack_server/event_server_cli.py +++ b/openpype/modules/ftrack/ftrack_server/event_server_cli.py @@ -316,7 +316,7 @@ def main_loop(ftrack_url): statuser_failed_count = 0 # If thread failed test Ftrack and Mongo connection - elif not statuser_thread.isAlive(): + elif not statuser_thread.is_alive(): statuser_thread.join() statuser_thread = None ftrack_accessible = False @@ -359,7 +359,7 @@ def main_loop(ftrack_url): storer_failed_count = 0 # If thread failed test Ftrack and Mongo connection - elif not storer_thread.isAlive(): + elif not storer_thread.is_alive(): if storer_thread.mongo_error: raise MongoPermissionsError() storer_thread.join() @@ -396,7 +396,7 @@ def main_loop(ftrack_url): processor_failed_count = 0 # If thread failed test Ftrack and Mongo connection - elif not processor_thread.isAlive(): + elif not processor_thread.is_alive(): if processor_thread.mongo_error: raise Exception( "Exiting because have issue with acces to MongoDB" diff --git a/openpype/modules/ftrack/tray/login_dialog.py b/openpype/modules/ftrack/tray/login_dialog.py index 0e676545f7..f374a71178 100644 --- a/openpype/modules/ftrack/tray/login_dialog.py +++ b/openpype/modules/ftrack/tray/login_dialog.py @@ -259,7 +259,7 @@ class CredentialsDialog(QtWidgets.QDialog): # If there is an existing server thread running we need to stop it. if self._login_server_thread: - if self._login_server_thread.isAlive(): + if self._login_server_thread.is_alive(): self._login_server_thread.stop() self._login_server_thread.join() self._login_server_thread = None From 2b78c9404c587e4a3f3f19402e30c4741a07b3db Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 15:01:02 +0100 Subject: [PATCH 55/56] OP-4822 - push property to render instance render instance is created from collected instance explicitly. --- openpype/hosts/maya/plugins/publish/collect_render.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index b1ad3ca58e..fc297ef612 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -318,7 +318,9 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "aovSeparator": layer_render_products.layer_data.aov_separator, # noqa: E501 "renderSetupIncludeLights": render_instance.data.get( "renderSetupIncludeLights" - ) + ), + "strict_error_checking": render_instance.data.get( + "strict_error_checking") } # Collect Deadline url if Deadline module is enabled From f346ace6a12557879cb753c972cead6ea1f4e510 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 17:36:51 +0100 Subject: [PATCH 56/56] OP-4653 - removed obsolete import Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/photoshop/plugins/create/create_image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 198c1586dc..cdea82cb05 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -5,7 +5,6 @@ from openpype.lib import BoolDef from openpype.pipeline import ( Creator, CreatedInstance, - legacy_io, CreatorError ) from openpype.lib import prepare_template_data