From 6246bac7742aa18f852704588ba37d228f0a1413 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:29:21 +0100 Subject: [PATCH 01/55] renamed 'reset_avalon_context' to 'reset_current_context' --- openpype/pipeline/create/context.py | 6 +++--- openpype/tools/publisher/control.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9c468ae8fc..29bc32b658 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1138,7 +1138,7 @@ class CreateContext: self.reset_preparation() - self.reset_avalon_context() + self.reset_current_context() self.reset_plugins(discover_publish_plugins) self.reset_context_data() @@ -1185,8 +1185,8 @@ class CreateContext: self._collection_shared_data = None self.refresh_thumbnails() - def reset_avalon_context(self): - """Give ability to reset avalon context. + def reset_current_context(self): + """Refresh current context. Reset is based on optional host implementation of `get_current_context` function or using `legacy_io.Session`. diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 50a814de5c..c11d7c53d3 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1756,7 +1756,7 @@ class PublisherController(BasePublisherController): self._create_context.reset_preparation() # Reset avalon context - self._create_context.reset_avalon_context() + self._create_context.reset_current_context() self._asset_docs_cache.reset() From d1b41ebac0b7bbac4a1404ca0233d2a7d92e6230 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:30:24 +0100 Subject: [PATCH 02/55] AvalonMongoDB is not needed for CreateContext or Controller --- openpype/pipeline/create/context.py | 38 ++++++++++++++--------------- openpype/tools/publisher/control.py | 5 ++-- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 29bc32b658..e421a76b6e 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1003,8 +1003,6 @@ class CreateContext: Args: host(ModuleType): Host implementation which handles implementation and global metadata. - dbcon(AvalonMongoDB): Connection to mongo with context (at least - project). headless(bool): Context is created out of UI (Current not used). reset(bool): Reset context on initialization. discover_publish_plugins(bool): Discover publish plugins during reset @@ -1012,16 +1010,8 @@ class CreateContext: """ def __init__( - self, host, dbcon=None, headless=False, reset=True, - discover_publish_plugins=True + self, host, headless=False, reset=True, discover_publish_plugins=True ): - # Create conncetion if is not passed - if dbcon is None: - session = session_data_from_environment(True) - dbcon = AvalonMongoDB(session) - dbcon.install() - - self.dbcon = dbcon self.host = host # Prepare attribute for logger (Created on demand in `log` property) @@ -1045,6 +1035,10 @@ class CreateContext: " Missing methods: {}" ).format(joined_methods)) + self._current_project_name = None + self._current_asset_name = None + self._current_task_name = None + self._host_is_valid = host_is_valid # Currently unused variable self.headless = headless @@ -1119,9 +1113,16 @@ class CreateContext: def host_name(self): return os.environ["AVALON_APP"] - @property - def project_name(self): - return self.dbcon.active_project() + def get_current_project_name(self): + return self._current_project_name + + def get_current_asset_name(self): + return self._current_asset_name + + def get_current_task_name(self): + return self._current_task_name + + project_name = property(get_current_project_name) @property def log(self): @@ -1210,12 +1211,9 @@ class CreateContext: if not task_name: task_name = legacy_io.Session.get("AVALON_TASK") - if project_name: - self.dbcon.Session["AVALON_PROJECT"] = project_name - if asset_name: - self.dbcon.Session["AVALON_ASSET"] = asset_name - if task_name: - self.dbcon.Session["AVALON_TASK"] = task_name + self._current_project_name = project_name + self._current_asset_name = asset_name + self._current_task_name = task_name def reset_plugins(self, discover_publish_plugins=True): """Reload plugins. diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index c11d7c53d3..83c2dd4b1c 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1589,20 +1589,19 @@ class PublisherController(BasePublisherController): Handle both creation and publishing parts. Args: - dbcon (AvalonMongoDB): Connection to mongo with context. headless (bool): Headless publishing. ATM not implemented or used. """ _log = None - def __init__(self, dbcon=None, headless=False): + def __init__(self, headless=False): super(PublisherController, self).__init__() self._host = registered_host() self._headless = headless self._create_context = CreateContext( - self._host, dbcon, headless=headless, reset=False + self._host, headless=headless, reset=False ) self._publish_plugins_proxy = None From 0a900f8ae1e4ac3b1ba48aca017be03a7f743e89 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:30:59 +0100 Subject: [PATCH 03/55] use 'name' attribute of host implementation if is available --- openpype/pipeline/create/context.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index e421a76b6e..867809a4c1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1111,6 +1111,8 @@ class CreateContext: @property def host_name(self): + if hasattr(self.host, "name"): + return self.host.name return os.environ["AVALON_APP"] def get_current_project_name(self): From 8678f4e2fa36bab16aa8033bf518dd0231fa885c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:31:27 +0100 Subject: [PATCH 04/55] 'create' returns output from creator --- openpype/pipeline/create/context.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 867809a4c1..413580526e 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1429,6 +1429,7 @@ class CreateContext: failed = False add_traceback = False exc_info = None + result = None try: # Fake CreatorError (Could be maybe specific exception?) if creator is None: @@ -1436,7 +1437,7 @@ class CreateContext: "Creator {} was not found".format(identifier) ) - creator.create(*args, **kwargs) + result = creator.create(*args, **kwargs) except CreatorError: failed = True @@ -1458,6 +1459,7 @@ class CreateContext: identifier, label, exc_info, add_traceback ) ]) + return result def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. From d09b7812616bfaee29c4089e2ece893ff6bd3faa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:32:53 +0100 Subject: [PATCH 05/55] implemented helper function 'create_with_context' to trigger standartized creation --- openpype/pipeline/create/context.py | 64 ++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 413580526e..655af1b8ed 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -8,17 +8,13 @@ import inspect from uuid import uuid4 from contextlib import contextmanager -from openpype.client import get_assets +from openpype.client import get_assets, get_asset_by_name from openpype.settings import ( get_system_settings, get_project_settings ) from openpype.host import IPublishHost from openpype.pipeline import legacy_io -from openpype.pipeline.mongodb import ( - AvalonMongoDB, - session_data_from_environment, -) from .creator_plugins import ( Creator, @@ -1461,6 +1457,64 @@ class CreateContext: ]) return result + def create_with_context( + self, + creator_identifier, + variant=None, + asset_doc=None, + task_name=None, + pre_create_data=None + ): + """Trigger create of plugins with standartized + + Args: + creator_identifier (str): + asset_doc (Dict[str, Any]): + task_name (str): Name of task to which is context related. + variant (str): Variant used for subset name. + pre_create_data (Dict[str, Any]): Pre-create attribute values. + + Returns: + Any: Output of triggered creator's 'create' method. + + Raises: + CreatorsCreateFailed: When creation fails. + """ + + if pre_create_data is None: + pre_create_data = {} + + project_name = self.project_name + if asset_doc is None: + asset_name = self.get_current_asset_name() + asset_doc = get_asset_by_name(project_name, asset_name) + task_name = self.get_current_task_name() + + creator = self.creators.get(creator_identifier) + family = None + subset_name = None + if creator is not None: + family = creator.family + subset_name = creator.get_subset_name( + variant, + task_name, + asset_doc, + project_name, + self.host_name + ) + instance_data = { + "asset": asset_doc["name"], + "task": task_name, + "variant": variant, + "family": family + } + return self.raw_create( + creator_identifier, + subset_name, + instance_data, + pre_create_data + ) + def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. From 430fe6aed42d8c08f57c7b91dd2eb9185a3d1fde Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:33:33 +0100 Subject: [PATCH 06/55] renamed 'create'->'raw_create' and 'create_with_context'->'create' --- openpype/pipeline/create/context.py | 9 ++++++--- openpype/tools/publisher/control.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 655af1b8ed..a9f8ae3ce1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1407,8 +1407,8 @@ class CreateContext: with self.bulk_instances_collection(): self._bulk_instances_to_process.append(instance) - def create(self, identifier, *args, **kwargs): - """Wrapper for creators to trigger created. + def raw_create(self, identifier, *args, **kwargs): + """Wrapper for creators to trigger 'create' method. Different types of creators may expect different arguments thus the hints for args are blind. @@ -1417,6 +1417,9 @@ class CreateContext: identifier (str): Creator's identifier. *args (Tuple[Any]): Arguments for create method. **kwargs (Dict[Any, Any]): Keyword argument for create method. + + Raises: + CreatorsCreateFailed: When creation fails. """ error_message = "Failed to run Creator with identifier \"{}\". {}" @@ -1457,7 +1460,7 @@ class CreateContext: ]) return result - def create_with_context( + def create( self, creator_identifier, variant=None, diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 83c2dd4b1c..670c22a43e 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -2017,7 +2017,7 @@ class PublisherController(BasePublisherController): success = True try: - self._create_context.create( + self._create_context.raw_create( creator_identifier, subset_name, instance_data, options ) except CreatorsOperationFailed as exc: From 498c8564f71c4d85ad88a101d6de7ae11357bb7d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:40:08 +0100 Subject: [PATCH 07/55] swapped argments order in docstring --- 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 a9f8ae3ce1..702731f8b2 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1472,9 +1472,9 @@ class CreateContext: Args: creator_identifier (str): + variant (str): Variant used for subset name. asset_doc (Dict[str, Any]): task_name (str): Name of task to which is context related. - variant (str): Variant used for subset name. pre_create_data (Dict[str, Any]): Pre-create attribute values. Returns: From 40712089d94ce22a5d34981584dc7db8beed9554 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 10:48:57 +0100 Subject: [PATCH 08/55] Validate creator and asset doc --- openpype/pipeline/create/context.py | 33 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 702731f8b2..35024b5af8 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1484,27 +1484,32 @@ class CreateContext: CreatorsCreateFailed: When creation fails. """ - if pre_create_data is None: - pre_create_data = {} + creator = self.creators.get(creator_identifier) + if creator is None: + raise CreatorError( + "Creator {} was not found".format(creator_identifier) + ) project_name = self.project_name if asset_doc is None: asset_name = self.get_current_asset_name() asset_doc = get_asset_by_name(project_name, asset_name) task_name = self.get_current_task_name() + if asset_doc is None: + raise CreatorError( + "Asset with name {} was not found".format(asset_name) + ) - creator = self.creators.get(creator_identifier) - family = None - subset_name = None - if creator is not None: - family = creator.family - subset_name = creator.get_subset_name( - variant, - task_name, - asset_doc, - project_name, - self.host_name - ) + if pre_create_data is None: + pre_create_data = {} + + subset_name = creator.get_subset_name( + variant, + task_name, + asset_doc, + project_name, + self.host_name + ) instance_data = { "asset": asset_doc["name"], "task": task_name, From 75bffb4daeacc8a31dc584edf3b76b3989a0607d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 10:49:17 +0100 Subject: [PATCH 09/55] removed unnecessary family from instance data --- openpype/pipeline/create/context.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 35024b5af8..dbbde9218f 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1513,8 +1513,7 @@ class CreateContext: instance_data = { "asset": asset_doc["name"], "task": task_name, - "variant": variant, - "family": family + "variant": variant } return self.raw_create( creator_identifier, From daa961d24976a4b9a1d5f51320017fa346bbfc84 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 10:49:27 +0100 Subject: [PATCH 10/55] variant is required argument --- 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 dbbde9218f..b10bbc17de 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1463,7 +1463,7 @@ class CreateContext: def create( self, creator_identifier, - variant=None, + variant, asset_doc=None, task_name=None, pre_create_data=None From 4d990e6f87964cb4f5fb2c61cebfcaea47ac3151 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 11:03:51 +0100 Subject: [PATCH 11/55] Updated docstrings --- openpype/pipeline/create/context.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index b10bbc17de..190d542724 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1191,7 +1191,15 @@ class CreateContext: function or using `legacy_io.Session`. Some hosts have ability to change context file without using workfiles - tool but that change is not propagated to + tool but that change is not propagated to 'legacy_io.Session' + nor 'os.environ'. + + Todos: + UI: Current context should be also checked on save - compare + initial values vs. current values. + Related to UI checks: Current workfile can be also considered + as current context information as that's where the metadata + are stored. We should store the workfile (if is available) too. """ project_name = asset_name = task_name = None @@ -1468,12 +1476,19 @@ class CreateContext: task_name=None, pre_create_data=None ): - """Trigger create of plugins with standartized + """Trigger create of plugins with standartized arguments. + + Arguments 'asset_doc' and 'task_name' use current context as default + values. If only 'task_name' is provided it will be overriden by + task name from current context. If 'task_name' is not provided + when 'asset_doc' is, it is considered that task name is not specified, + which can lead to error if subset name template requires task name. Args: - creator_identifier (str): + creator_identifier (str): Identifier of creator plugin. variant (str): Variant used for subset name. - asset_doc (Dict[str, Any]): + asset_doc (Dict[str, Any]): Asset document which define context of + creation (possible context of created instance/s). task_name (str): Name of task to which is context related. pre_create_data (Dict[str, Any]): Pre-create attribute values. @@ -1481,6 +1496,7 @@ class CreateContext: Any: Output of triggered creator's 'create' method. Raises: + CreatorError: If creator was not found or asset is empty. CreatorsCreateFailed: When creation fails. """ From b00aab1eed7bf92347b64ed50e44340476b671a6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 Feb 2023 23:35:22 +0000 Subject: [PATCH 12/55] Fix rounding --- openpype/hosts/maya/api/lib.py | 149 +++++++++++++----- .../plugins/publish/validate_maya_units.py | 11 +- 2 files changed, 115 insertions(+), 45 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index e5fa883c99..887c04d257 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1969,8 +1969,6 @@ def get_id_from_sibling(node, history_only=True): return first_id - -# Project settings def set_scene_fps(fps, update=True): """Set FPS from project configuration @@ -1983,28 +1981,21 @@ def set_scene_fps(fps, update=True): """ - fps_mapping = {'15': 'game', - '24': 'film', - '25': 'pal', - '30': 'ntsc', - '48': 'show', - '50': 'palf', - '60': 'ntscf', - '23.98': '23.976fps', - '23.976': '23.976fps', - '29.97': '29.97fps', - '47.952': '47.952fps', - '47.95': '47.952fps', - '59.94': '59.94fps', - '44100': '44100fps', - '48000': '48000fps'} - - # pull from mapping - # this should convert float string to float and int to int - # so 25.0 is converted to 25, but 23.98 will be still float. - dec, ipart = math.modf(fps) - if dec == 0.0: - fps = int(ipart) + fps_mapping = { + '15': 'game', + '24': 'film', + '25': 'pal', + '30': 'ntsc', + '48': 'show', + '50': 'palf', + '60': 'ntscf', + '23.976023976023978': '23.976fps', + '29.97002997002997': '29.97fps', + '47.952047952047955': '47.952fps', + '59.94005994005994': '59.94fps', + '44100': '44100fps', + '48000': '48000fps' + } unit = fps_mapping.get(str(fps), None) if unit is None: @@ -2124,7 +2115,9 @@ def set_context_settings(): asset_data = asset_doc.get("data", {}) # Set project fps - fps = asset_data.get("fps", project_data.get("fps", 25)) + fps = convert_to_maya_fps( + asset_data.get("fps", project_data.get("fps", 25)) + ) legacy_io.Session["AVALON_FPS"] = str(fps) set_scene_fps(fps) @@ -2146,15 +2139,12 @@ def validate_fps(): """ - fps = get_current_project_asset(fields=["data.fps"])["data"]["fps"] - # TODO(antirotor): This is hack as for framerates having multiple - # decimal places. FTrack is ceiling decimal values on - # fps to two decimal places but Maya 2019+ is reporting those fps - # with much higher resolution. As we currently cannot fix Ftrack - # rounding, we have to round those numbers coming from Maya. - current_fps = float_round(mel.eval('currentTimeUnitToFPS()'), 2) + expected_fps = convert_to_maya_fps( + get_current_project_asset(fields=["data.fps"])["data"]["fps"] + ) + current_fps = mel.eval('currentTimeUnitToFPS()') - fps_match = current_fps == fps + fps_match = current_fps == expected_fps if not fps_match and not IS_HEADLESS: from openpype.widgets import popup @@ -2163,14 +2153,19 @@ def validate_fps(): dialog = popup.PopupUpdateKeys(parent=parent) dialog.setModal(True) dialog.setWindowTitle("Maya scene does not match project FPS") - dialog.setMessage("Scene %i FPS does not match project %i FPS" % - (current_fps, fps)) + dialog.setMessage( + "Scene {} FPS does not match project {} FPS".format( + current_fps, expected_fps + ) + ) dialog.setButtonText("Fix") # Set new text for button (add optional argument for the popup?) toggle = dialog.widgets["toggle"] update = toggle.isChecked() - dialog.on_clicked_state.connect(lambda: set_scene_fps(fps, update)) + dialog.on_clicked_state.connect( + lambda: set_scene_fps(expected_fps, update) + ) dialog.show() @@ -3353,3 +3348,85 @@ def iter_visible_nodes_in_range(nodes, start, end): def get_attribute_input(attr): connections = cmds.listConnections(attr, plugs=True, destination=False) return connections[0] if connections else None + + +def convert_to_maya_fps(fps): + """Convert any fps to supported Maya framerates.""" + float_framerates = [ + 23.976023976023978, + # WTF is 29.97 df vs fps? + 29.97002997002997, + 47.952047952047955, + 59.94005994005994 + ] + # 44100 fps evaluates as 41000.0. Why? Omitting for now. + int_framerates = [ + 2, + 3, + 4, + 5, + 6, + 8, + 10, + 12, + 15, + 16, + 20, + 24, + 25, + 30, + 40, + 48, + 50, + 60, + 75, + 80, + 90, + 100, + 120, + 125, + 150, + 200, + 240, + 250, + 300, + 375, + 400, + 500, + 600, + 750, + 1200, + 1500, + 2000, + 3000, + 6000, + 48000 + ] + + # If input fps is a whole number we'll return. + if float(fps).is_integer(): + # Validate fps is part of Maya's fps selection. + if fps not in int_framerates: + raise ValueError( + "Framerate \"{}\" is not supported in Maya".format(fps) + ) + return fps + else: + # Differences to supported float frame rates. + differences = [] + for i in float_framerates: + differences.append(abs(i - fps)) + + # Validate difference does not stray too far from supported framerates. + min_difference = min(differences) + min_index = differences.index(min_difference) + supported_framerate = float_framerates[min_index] + if round(min_difference) != 0: + raise ValueError( + "Framerate \"{}\" strays too far from any supported framerate" + " in Maya. Closest supported framerate is \"{}\"".format( + fps, supported_framerate + ) + ) + + return supported_framerate diff --git a/openpype/hosts/maya/plugins/publish/validate_maya_units.py b/openpype/hosts/maya/plugins/publish/validate_maya_units.py index e6fabb1712..ad256b6a72 100644 --- a/openpype/hosts/maya/plugins/publish/validate_maya_units.py +++ b/openpype/hosts/maya/plugins/publish/validate_maya_units.py @@ -33,18 +33,11 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): linearunits = context.data.get('linearUnits') angularunits = context.data.get('angularUnits') - # TODO(antirotor): This is hack as for framerates having multiple - # decimal places. FTrack is ceiling decimal values on - # fps to two decimal places but Maya 2019+ is reporting those fps - # with much higher resolution. As we currently cannot fix Ftrack - # rounding, we have to round those numbers coming from Maya. - # NOTE: this must be revisited yet again as it seems that Ftrack is - # now flooring the value? - fps = mayalib.float_round(context.data.get('fps'), 2, ceil) + fps = context.data.get('fps') # TODO repace query with using 'context.data["assetEntity"]' asset_doc = get_current_project_asset() - asset_fps = asset_doc["data"]["fps"] + asset_fps = mayalib.convert_to_maya_fps(asset_doc["data"]["fps"]) self.log.info('Units (linear): {0}'.format(linearunits)) self.log.info('Units (angular): {0}'.format(angularunits)) From 23987420a375c199b095b295282728e913bde75a Mon Sep 17 00:00:00 2001 From: Seyedmohammadreza Hashemizadeh Date: Mon, 6 Feb 2023 19:03:06 +0100 Subject: [PATCH 13/55] update asset info of imported sets --- .../maya/api/workfile_template_builder.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index ef043ed0f4..56a53c070c 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -2,7 +2,7 @@ import json from maya import cmds -from openpype.pipeline import registered_host +from openpype.pipeline import registered_host, legacy_io from openpype.pipeline.workfile.workfile_template_builder import ( TemplateAlreadyImported, AbstractTemplateBuilder, @@ -41,10 +41,26 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): )) cmds.sets(name=PLACEHOLDER_SET, empty=True) - cmds.file(path, i=True, returnNewNodes=True) + new_nodes = cmds.file(path, i=True, returnNewNodes=True) cmds.setAttr(PLACEHOLDER_SET + ".hiddenInOutliner", True) + imported_sets = cmds.ls(new_nodes, set=True) + if not imported_sets: + return True + + # update imported sets information + for node in imported_sets: + if not cmds.attributeQuery("id", node=node, exists=True): + continue + if cmds.getAttr("{}.id".format(node)) != "pyblish.avalon.instance": + continue + if not cmds.attributeQuery("asset", node=node, exists=True): + continue + asset = legacy_io.Session["AVALON_ASSET"] + + cmds.setAttr("{}.asset".format(node), asset, type="string") + return True From b1ef1b751f5bdf3df1274b524ac55f34f4b1c51d Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Wed, 8 Feb 2023 19:11:20 +0100 Subject: [PATCH 14/55] Abstract get_representation_path function and use it on shotgrid to fix remote errors with data instances not having 'published_path' --- .../publish/integrate_ftrack_instances.py | 52 ++----------------- .../publish/integrate_shotgrid_publish.py | 4 +- .../publish/integrate_shotgrid_version.py | 48 ++++++++++++----- .../plugins/publish/integrate_slack_api.py | 11 ++-- openpype/plugins/publish/integrate.py | 46 ++++++++++++++++ 5 files changed, 92 insertions(+), 69 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index 2d06e2ab02..c3baecec67 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -10,6 +10,7 @@ from openpype.lib.transcoding import ( ) from openpype.lib.profiles_filtering import filter_profiles from openpype.lib.transcoding import VIDEO_EXTENSIONS +from openpype.plugins.publish.integrate import get_representation_path class IntegrateFtrackInstance(pyblish.api.InstancePlugin): @@ -153,7 +154,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): if not review_representations or has_movie_review: for repre in thumbnail_representations: - repre_path = self._get_repre_path(instance, repre, False) + repre_path = get_representation_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -210,7 +211,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "from {}".format(repre)) continue - repre_path = self._get_repre_path(instance, repre, False) + repre_path = get_representation_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -324,7 +325,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): # Add others representations as component for repre in other_representations: - published_path = self._get_repre_path(instance, repre, True) + published_path = get_representation_path(instance, repre, True) if not published_path: continue # Create copy of base comp item and append it @@ -364,51 +365,6 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): def _collect_additional_metadata(self, streams): pass - def _get_repre_path(self, instance, repre, only_published): - """Get representation path that can be used for integration. - - When 'only_published' is set to true the validation of path is not - relevant. In that case we just need what is set in 'published_path' - as "reference". The reference is not used to get or upload the file but - for reference where the file was published. - - Args: - instance (pyblish.Instance): Processed instance object. Used - for source of staging dir if representation does not have - filled it. - repre (dict): Representation on instance which could be and - could not be integrated with main integrator. - only_published (bool): Care only about published paths and - ignore if filepath is not existing anymore. - - Returns: - str: Path to representation file. - None: Path is not filled or does not exists. - """ - - published_path = repre.get("published_path") - if published_path: - published_path = os.path.normpath(published_path) - if os.path.exists(published_path): - return published_path - - if only_published: - return published_path - - comp_files = repre["files"] - if isinstance(comp_files, (tuple, list, set)): - filename = comp_files[0] - else: - filename = comp_files - - staging_dir = repre.get("stagingDir") - if not staging_dir: - staging_dir = instance.data["stagingDir"] - src_path = os.path.normpath(os.path.join(staging_dir, filename)) - if os.path.exists(src_path): - return src_path - return None - def _get_asset_version_status_name(self, instance): if not self.asset_versions_status_profiles: return None diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index cfd2d10fd9..ee6ece2e67 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -1,6 +1,8 @@ import os import pyblish.api +from openpype.plugins.publish.integrate import get_representation_path + class IntegrateShotgridPublish(pyblish.api.InstancePlugin): """ @@ -22,7 +24,7 @@ class IntegrateShotgridPublish(pyblish.api.InstancePlugin): for representation in instance.data.get("representations", []): - local_path = representation.get("published_path") + local_path = get_representation_path(instance, representation, False) code = os.path.basename(local_path) if representation.get("tags", []): diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py index a1b7140e22..60ad1ff91d 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py @@ -1,6 +1,7 @@ -import os import pyblish.api +from openpype.plugins.publish.integrate import get_representation_path + class IntegrateShotgridVersion(pyblish.api.InstancePlugin): """Integrate Shotgrid Version""" @@ -17,15 +18,37 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): # TODO: Use path template solver to build version code from settings anatomy = instance.data.get("anatomyData", {}) - code = "_".join( - [ - anatomy["project"]["code"], - anatomy["parent"], - anatomy["asset"], - anatomy["task"]["name"], - "v{:03}".format(int(anatomy["version"])), - ] - ) + ### Starts Alkemy-X Override ### + # code = "_".join( + # [ + # anatomy["project"]["code"], + # anatomy["parent"], + # anatomy["asset"], + # anatomy["task"]["name"], + # "v{:03}".format(int(anatomy["version"])), + # ] + # ) + # Initial editorial Shotgrid versions don't need task in name + if anatomy["app"] == "hiero": + code = "_".join( + [ + anatomy["project"]["code"], + anatomy["parent"], + anatomy["asset"], + "v{:03}".format(int(anatomy["version"])), + ] + ) + else: + code = "_".join( + [ + anatomy["project"]["code"], + anatomy["parent"], + anatomy["asset"], + anatomy["task"]["name"], + "v{:03}".format(int(anatomy["version"])), + ] + ) + ### Ends Alkemy-X Override ### version = self._find_existing_version(code, context) @@ -41,8 +64,9 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): data_to_update["sg_status_list"] = status for representation in instance.data.get("representations", []): - local_path = representation.get("published_path") - code = os.path.basename(local_path) + # Get representation path from published_path or create it from stagingDir if not existent + local_path = get_representation_path(instance, representation, False) + self.log.info("Local path: %s", local_path) if "shotgridreview" in representation.get("tags", []): diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 612031efac..ac918381c0 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -9,6 +9,7 @@ import time from openpype.client import OpenPypeMongoConnection from openpype.lib.plugin_tools import prepare_template_data +from openpype.plugins.publish.integrate import get_representation_path class IntegrateSlackAPI(pyblish.api.InstancePlugin): @@ -167,10 +168,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): thumbnail_path = None for repre in instance.data.get("representations", []): if repre.get('thumbnail') or "thumbnail" in repre.get('tags', []): - repre_thumbnail_path = ( - repre.get("published_path") or - os.path.join(repre["stagingDir"], repre["files"]) - ) + repre_thumbnail_path = get_representation_path(instance, repre, False) if os.path.exists(repre_thumbnail_path): thumbnail_path = repre_thumbnail_path break @@ -184,10 +182,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): if (repre.get("review") or "review" in tags or "burnin" in tags): - repre_review_path = ( - repre.get("published_path") or - os.path.join(repre["stagingDir"], repre["files"]) - ) + repre_review_path = get_representation_path(instance, repre, False) if os.path.exists(repre_review_path): review_path = repre_review_path if "burnin" in tags: # burnin has precedence if exists diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 7b73943c37..854cf8b9ec 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -53,6 +53,52 @@ def get_frame_padded(frame, padding): return "{frame:0{padding}d}".format(padding=padding, frame=frame) +def get_representation_path(instance, repre, only_published): + """Get representation path that can be used for integration. + + When 'only_published' is set to true the validation of path is not + relevant. In that case we just need what is set in 'published_path' + as "reference". The reference is not used to get or upload the file but + for reference where the file was published. + + Args: + instance (pyblish.Instance): Processed instance object. Used + for source of staging dir if representation does not have + filled it. + repre (dict): Representation on instance which could be and + could not be integrated with main integrator. + only_published (bool): Care only about published paths and + ignore if filepath is not existing anymore. + + Returns: + str: Path to representation file. + None: Path is not filled or does not exists. + """ + + published_path = repre.get("published_path") + if published_path: + published_path = os.path.normpath(published_path) + if os.path.exists(published_path): + return published_path + + if only_published: + return published_path + + comp_files = repre["files"] + if isinstance(comp_files, (tuple, list, set)): + filename = comp_files[0] + else: + filename = comp_files + + staging_dir = repre.get("stagingDir") + if not staging_dir: + staging_dir = instance.data["stagingDir"] + src_path = os.path.normpath(os.path.join(staging_dir, filename)) + if os.path.exists(src_path): + return src_path + return None + + class IntegrateAsset(pyblish.api.InstancePlugin): """Register publish in the database and transfer files to destinations. From c13416b68570365b9ce3b247979f79f721cc3031 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Wed, 8 Feb 2023 19:39:47 +0100 Subject: [PATCH 15/55] Remove unintended code block and fix Hound lint warnings for <79 chars width --- .../publish/integrate_shotgrid_publish.py | 4 +- .../publish/integrate_shotgrid_version.py | 46 +++++-------------- .../plugins/publish/integrate_slack_api.py | 8 +++- 3 files changed, 21 insertions(+), 37 deletions(-) diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index ee6ece2e67..7789a47074 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -24,7 +24,9 @@ class IntegrateShotgridPublish(pyblish.api.InstancePlugin): for representation in instance.data.get("representations", []): - local_path = get_representation_path(instance, representation, False) + local_path = get_representation_path( + instance, representation, False + ) code = os.path.basename(local_path) if representation.get("tags", []): diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py index 60ad1ff91d..94fc4ae9e8 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py @@ -18,37 +18,15 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): # TODO: Use path template solver to build version code from settings anatomy = instance.data.get("anatomyData", {}) - ### Starts Alkemy-X Override ### - # code = "_".join( - # [ - # anatomy["project"]["code"], - # anatomy["parent"], - # anatomy["asset"], - # anatomy["task"]["name"], - # "v{:03}".format(int(anatomy["version"])), - # ] - # ) - # Initial editorial Shotgrid versions don't need task in name - if anatomy["app"] == "hiero": - code = "_".join( - [ - anatomy["project"]["code"], - anatomy["parent"], - anatomy["asset"], - "v{:03}".format(int(anatomy["version"])), - ] - ) - else: - code = "_".join( - [ - anatomy["project"]["code"], - anatomy["parent"], - anatomy["asset"], - anatomy["task"]["name"], - "v{:03}".format(int(anatomy["version"])), - ] - ) - ### Ends Alkemy-X Override ### + code = "_".join( + [ + anatomy["project"]["code"], + anatomy["parent"], + anatomy["asset"], + anatomy["task"]["name"], + "v{:03}".format(int(anatomy["version"])), + ] + ) version = self._find_existing_version(code, context) @@ -64,9 +42,9 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): data_to_update["sg_status_list"] = status for representation in instance.data.get("representations", []): - # Get representation path from published_path or create it from stagingDir if not existent - local_path = get_representation_path(instance, representation, False) - self.log.info("Local path: %s", local_path) + local_path = get_representation_path( + instance, representation, False + ) if "shotgridreview" in representation.get("tags", []): diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index ac918381c0..d486b2179a 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -168,7 +168,9 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): thumbnail_path = None for repre in instance.data.get("representations", []): if repre.get('thumbnail') or "thumbnail" in repre.get('tags', []): - repre_thumbnail_path = get_representation_path(instance, repre, False) + repre_thumbnail_path = get_representation_path( + instance, repre, False + ) if os.path.exists(repre_thumbnail_path): thumbnail_path = repre_thumbnail_path break @@ -182,7 +184,9 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): if (repre.get("review") or "review" in tags or "burnin" in tags): - repre_review_path = get_representation_path(instance, repre, False) + repre_review_path = get_representation_path( + instance, repre, False + ) if os.path.exists(repre_review_path): review_path = repre_review_path if "burnin" in tags: # burnin has precedence if exists From 27150e4abb148e7dfc4eb673ea4affa9c080f55f Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Wed, 8 Feb 2023 20:23:26 +0100 Subject: [PATCH 16/55] Address feedback on PR and move function to pipeline.publish.lib --- .../publish/integrate_ftrack_instances.py | 8 ++-- .../publish/integrate_shotgrid_publish.py | 4 +- .../publish/integrate_shotgrid_version.py | 4 +- .../plugins/publish/integrate_slack_api.py | 6 +-- openpype/pipeline/publish/__init__.py | 2 + openpype/pipeline/publish/lib.py | 46 +++++++++++++++++++ openpype/plugins/publish/integrate.py | 46 ------------------- 7 files changed, 59 insertions(+), 57 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index c3baecec67..d6cb3daf0d 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -3,6 +3,7 @@ import json import copy import pyblish.api +from openpype.pipeline.publish import get_publish_repre_path from openpype.lib.openpype_version import get_openpype_version from openpype.lib.transcoding import ( get_ffprobe_streams, @@ -10,7 +11,6 @@ from openpype.lib.transcoding import ( ) from openpype.lib.profiles_filtering import filter_profiles from openpype.lib.transcoding import VIDEO_EXTENSIONS -from openpype.plugins.publish.integrate import get_representation_path class IntegrateFtrackInstance(pyblish.api.InstancePlugin): @@ -154,7 +154,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): if not review_representations or has_movie_review: for repre in thumbnail_representations: - repre_path = get_representation_path(instance, repre, False) + repre_path = get_publish_repre_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -211,7 +211,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "from {}".format(repre)) continue - repre_path = get_representation_path(instance, repre, False) + repre_path = get_publish_repre_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -325,7 +325,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): # Add others representations as component for repre in other_representations: - published_path = get_representation_path(instance, repre, True) + published_path = get_publish_repre_path(instance, repre, True) if not published_path: continue # Create copy of base comp item and append it diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index 7789a47074..fc15d5515f 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -1,7 +1,7 @@ import os import pyblish.api -from openpype.plugins.publish.integrate import get_representation_path +from openpype.pipeline.publish import get_publish_repre_path class IntegrateShotgridPublish(pyblish.api.InstancePlugin): @@ -24,7 +24,7 @@ class IntegrateShotgridPublish(pyblish.api.InstancePlugin): for representation in instance.data.get("representations", []): - local_path = get_representation_path( + local_path = get_publish_repre_path( instance, representation, False ) code = os.path.basename(local_path) diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py index 94fc4ae9e8..adfdca718c 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py @@ -1,6 +1,6 @@ import pyblish.api -from openpype.plugins.publish.integrate import get_representation_path +from openpype.pipeline.publish import get_publish_repre_path class IntegrateShotgridVersion(pyblish.api.InstancePlugin): @@ -42,7 +42,7 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): data_to_update["sg_status_list"] = status for representation in instance.data.get("representations", []): - local_path = get_representation_path( + local_path = get_publish_repre_path( instance, representation, False ) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index d486b2179a..4e2557ccc7 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -8,8 +8,8 @@ from abc import ABCMeta, abstractmethod import time from openpype.client import OpenPypeMongoConnection +from openpype.pipeline.publish import get_publish_repre_path from openpype.lib.plugin_tools import prepare_template_data -from openpype.plugins.publish.integrate import get_representation_path class IntegrateSlackAPI(pyblish.api.InstancePlugin): @@ -168,7 +168,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): thumbnail_path = None for repre in instance.data.get("representations", []): if repre.get('thumbnail') or "thumbnail" in repre.get('tags', []): - repre_thumbnail_path = get_representation_path( + repre_thumbnail_path = get_publish_repre_path( instance, repre, False ) if os.path.exists(repre_thumbnail_path): @@ -184,7 +184,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): if (repre.get("review") or "review" in tags or "burnin" in tags): - repre_review_path = get_representation_path( + repre_review_path = get_publish_repre_path( instance, repre, False ) if os.path.exists(repre_review_path): diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index dc6fc0f97a..5be973ad86 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -36,6 +36,7 @@ from .lib import ( filter_instances_for_context_plugin, context_plugin_should_run, get_instance_staging_dir, + get_publish_repre_path, ) from .abstract_expected_files import ExpectedFiles @@ -79,6 +80,7 @@ __all__ = ( "filter_instances_for_context_plugin", "context_plugin_should_run", "get_instance_staging_dir", + "get_publish_repre_path", "ExpectedFiles", diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index c76671fa39..e206c4552c 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -632,3 +632,49 @@ def get_instance_staging_dir(instance): instance.data["stagingDir"] = staging_dir return staging_dir + + +def get_publish_repre_path(instance, repre, only_published): + """Get representation path that can be used for integration. + + When 'only_published' is set to true the validation of path is not + relevant. In that case we just need what is set in 'published_path' + as "reference". The reference is not used to get or upload the file but + for reference where the file was published. + + Args: + instance (pyblish.Instance): Processed instance object. Used + for source of staging dir if representation does not have + filled it. + repre (dict): Representation on instance which could be and + could not be integrated with main integrator. + only_published (bool): Care only about published paths and + ignore if filepath is not existing anymore. + + Returns: + str: Path to representation file. + None: Path is not filled or does not exists. + """ + + published_path = repre.get("published_path") + if published_path: + published_path = os.path.normpath(published_path) + if os.path.exists(published_path): + return published_path + + if only_published: + return published_path + + comp_files = repre["files"] + if isinstance(comp_files, (tuple, list, set)): + filename = comp_files[0] + else: + filename = comp_files + + staging_dir = repre.get("stagingDir") + if not staging_dir: + staging_dir = get_instance_staging_dir(instance) + src_path = os.path.normpath(os.path.join(staging_dir, filename)) + if os.path.exists(src_path): + return src_path + return None diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 854cf8b9ec..7b73943c37 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -53,52 +53,6 @@ def get_frame_padded(frame, padding): return "{frame:0{padding}d}".format(padding=padding, frame=frame) -def get_representation_path(instance, repre, only_published): - """Get representation path that can be used for integration. - - When 'only_published' is set to true the validation of path is not - relevant. In that case we just need what is set in 'published_path' - as "reference". The reference is not used to get or upload the file but - for reference where the file was published. - - Args: - instance (pyblish.Instance): Processed instance object. Used - for source of staging dir if representation does not have - filled it. - repre (dict): Representation on instance which could be and - could not be integrated with main integrator. - only_published (bool): Care only about published paths and - ignore if filepath is not existing anymore. - - Returns: - str: Path to representation file. - None: Path is not filled or does not exists. - """ - - published_path = repre.get("published_path") - if published_path: - published_path = os.path.normpath(published_path) - if os.path.exists(published_path): - return published_path - - if only_published: - return published_path - - comp_files = repre["files"] - if isinstance(comp_files, (tuple, list, set)): - filename = comp_files[0] - else: - filename = comp_files - - staging_dir = repre.get("stagingDir") - if not staging_dir: - staging_dir = instance.data["stagingDir"] - src_path = os.path.normpath(os.path.join(staging_dir, filename)) - if os.path.exists(src_path): - return src_path - return None - - class IntegrateAsset(pyblish.api.InstancePlugin): """Register publish in the database and transfer files to destinations. From 15d7a7589fa119010510232e3ae243f0e6834383 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 12:04:06 +0100 Subject: [PATCH 17/55] fix context collection from create context --- .../publish/collect_from_create_context.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openpype/plugins/publish/collect_from_create_context.py b/openpype/plugins/publish/collect_from_create_context.py index d3398c885e..5fcf8feb56 100644 --- a/openpype/plugins/publish/collect_from_create_context.py +++ b/openpype/plugins/publish/collect_from_create_context.py @@ -32,7 +32,7 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): thumbnail_paths_by_instance_id.get(None) ) - project_name = create_context.project_name + project_name = create_context.get_current_project_name() if project_name: context.data["projectName"] = project_name @@ -53,11 +53,15 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): context.data.update(create_context.context_data_to_store()) context.data["newPublishing"] = True # Update context data - for key in ("AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK"): - value = create_context.dbcon.Session.get(key) - if value is not None: - legacy_io.Session[key] = value - os.environ[key] = value + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() + for key, value in ( + ("AVALON_PROJECT", project_name), + ("AVALON_ASSET", asset_name), + ("AVALON_TASK", task_name) + ): + legacy_io.Session[key] = value + os.environ[key] = value def create_instance( self, From b148dec04843137622960b50c484d94ad9b0e82b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 12:58:17 +0100 Subject: [PATCH 18/55] Added helper method to return unified information on create error --- openpype/pipeline/create/context.py | 70 +++++++++++++++++------------ 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 190d542724..dfe60d438b 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1538,6 +1538,44 @@ class CreateContext: pre_create_data ) + def _create_with_unified_error( + self, identifier, creator, *args, **kwargs + ): + error_message = "Failed to run Creator with identifier \"{}\". {}" + + label = None + add_traceback = False + result = None + fail_info = None + success = False + + try: + # Try to get creator and his label + if creator is None: + creator = self._get_creator_in_create(identifier) + label = getattr(creator, "label", label) + + # Run create + result = creator.create(*args, **kwargs) + success = True + + except CreatorError: + exc_info = sys.exc_info() + self.log.warning(error_message.format(identifier, exc_info[1])) + + except: + add_traceback = True + exc_info = sys.exc_info() + self.log.warning( + error_message.format(identifier, ""), + exc_info=True + ) + + if not success: + fail_info = prepare_failed_creator_operation_info( + identifier, label, exc_info, add_traceback + ) + return result, fail_info def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. @@ -1663,37 +1701,11 @@ class CreateContext: Reset instances if any autocreator executed properly. """ - error_message = "Failed to run AutoCreator with identifier \"{}\". {}" failed_info = [] for identifier, creator in self.autocreators.items(): - label = creator.label - failed = False - add_traceback = False - try: - creator.create() - - except CreatorError: - failed = True - exc_info = sys.exc_info() - self.log.warning(error_message.format(identifier, exc_info[1])) - - # Use bare except because some hosts raise their exceptions that - # do not inherit from python's `BaseException` - except: - failed = True - add_traceback = True - exc_info = sys.exc_info() - self.log.warning( - error_message.format(identifier, ""), - exc_info=True - ) - - if failed: - failed_info.append( - prepare_failed_creator_operation_info( - identifier, label, exc_info, add_traceback - ) - ) + _, fail_info = self._create_with_unified_error(identifier, creator) + if fail_info is not None: + failed_info.append(fail_info) if failed_info: raise CreatorsCreateFailed(failed_info) From fac10d26337157bc253a1af90ae0a1040ff49533 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 12:58:51 +0100 Subject: [PATCH 19/55] added public method 'create_with_unified_error' used in publisher --- openpype/pipeline/create/context.py | 25 +++++++++++++++++++++++++ openpype/tools/publisher/control.py | 3 ++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index dfe60d438b..2a92d21225 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1576,6 +1576,31 @@ class CreateContext: identifier, label, exc_info, add_traceback ) return result, fail_info + + def create_with_unified_error(self, identifier, *args, **kwargs): + """Trigger create but raise only one error if anything fails. + + Added to raise unified exception. Capture any possible issues and + reraise it with unified information. + + Args: + identifier (str): Identifier of creator. + *args (Tuple[Any]): Arguments for create method. + **kwargs (Dict[Any, Any]): Keyword argument for create method. + + Raises: + CreatorsCreateFailed: When creation fails due to any possible + reason. If anything goes wrong this is only possible exception + the method should raise. + """ + + result, fail_info = self._create_with_unified_error( + identifier, None, *args, **kwargs + ) + if fail_info is not None: + raise CreatorsCreateFailed([fail_info]) + return result + def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 670c22a43e..11215b5ff8 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -2017,9 +2017,10 @@ class PublisherController(BasePublisherController): success = True try: - self._create_context.raw_create( + self._create_context.create_with_unified_error( creator_identifier, subset_name, instance_data, options ) + except CreatorsOperationFailed as exc: success = False self._emit_event( From cb84cf769eea9d5a018ef67dd6e4cb0d6d7276b8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 13:00:28 +0100 Subject: [PATCH 20/55] 'create' method is not triggering 'raw_create' --- openpype/pipeline/create/context.py | 34 ++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 2a92d21225..3287141970 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1415,6 +1415,30 @@ class CreateContext: with self.bulk_instances_collection(): self._bulk_instances_to_process.append(instance) + def _get_creator_in_create(self, identifier): + """Creator by identifier with unified error. + + Helper method to get creator by identifier with same error when creator + is not available. + + Args: + identifier (str): Identifier of creator plugin. + + Returns: + BaseCreator: Creator found by identifier. + + Raises: + CreatorError: When identifier is not known. + """ + + creator = self.creators.get(identifier) + # Fake CreatorError (Could be maybe specific exception?) + if creator is None: + raise CreatorError( + "Creator {} was not found".format(identifier) + ) + return creator + def raw_create(self, identifier, *args, **kwargs): """Wrapper for creators to trigger 'create' method. @@ -1497,14 +1521,9 @@ class CreateContext: Raises: CreatorError: If creator was not found or asset is empty. - CreatorsCreateFailed: When creation fails. """ - creator = self.creators.get(creator_identifier) - if creator is None: - raise CreatorError( - "Creator {} was not found".format(creator_identifier) - ) + creator = self._get_creator_in_create(creator_identifier) project_name = self.project_name if asset_doc is None: @@ -1531,8 +1550,7 @@ class CreateContext: "task": task_name, "variant": variant } - return self.raw_create( - creator_identifier, + return creator.create( subset_name, instance_data, pre_create_data From 0cb78a10e6cd7b0c7307c70be1827e2e8c1d1f2e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 13:00:49 +0100 Subject: [PATCH 21/55] removed unused 'raw_create' method --- openpype/pipeline/create/context.py | 53 ----------------------------- 1 file changed, 53 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 3287141970..078c50acc2 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1439,59 +1439,6 @@ class CreateContext: ) return creator - def raw_create(self, identifier, *args, **kwargs): - """Wrapper for creators to trigger 'create' method. - - Different types of creators may expect different arguments thus the - hints for args are blind. - - Args: - identifier (str): Creator's identifier. - *args (Tuple[Any]): Arguments for create method. - **kwargs (Dict[Any, Any]): Keyword argument for create method. - - Raises: - CreatorsCreateFailed: When creation fails. - """ - - error_message = "Failed to run Creator with identifier \"{}\". {}" - creator = self.creators.get(identifier) - label = getattr(creator, "label", None) - failed = False - add_traceback = False - exc_info = None - result = None - try: - # Fake CreatorError (Could be maybe specific exception?) - if creator is None: - raise CreatorError( - "Creator {} was not found".format(identifier) - ) - - result = creator.create(*args, **kwargs) - - except CreatorError: - failed = True - exc_info = sys.exc_info() - self.log.warning(error_message.format(identifier, exc_info[1])) - - except: - failed = True - add_traceback = True - exc_info = sys.exc_info() - self.log.warning( - error_message.format(identifier, ""), - exc_info=True - ) - - if failed: - raise CreatorsCreateFailed([ - prepare_failed_creator_operation_info( - identifier, label, exc_info, add_traceback - ) - ]) - return result - def create( self, creator_identifier, From 171b9bc3dbc079d08248cc2cafa40342b9bcd762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Thu, 9 Feb 2023 05:49:02 -0800 Subject: [PATCH 22/55] Make only_published argument optional Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/pipeline/publish/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index e206c4552c..92c43c99e8 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -634,7 +634,7 @@ def get_instance_staging_dir(instance): return staging_dir -def get_publish_repre_path(instance, repre, only_published): +def get_publish_repre_path(instance, repre, only_published=False): """Get representation path that can be used for integration. When 'only_published' is set to true the validation of path is not From 514ba7e79b55e1aedcfe32c1fe4b949c32af0c13 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 10 Feb 2023 07:25:38 +0000 Subject: [PATCH 23/55] Fix reset_frame_range --- openpype/hosts/maya/api/commands.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/maya/api/commands.py b/openpype/hosts/maya/api/commands.py index 4a36406632..19ad18d824 100644 --- a/openpype/hosts/maya/api/commands.py +++ b/openpype/hosts/maya/api/commands.py @@ -4,6 +4,7 @@ from maya import cmds from openpype.client import get_asset_by_name, get_project from openpype.pipeline import legacy_io +from . import lib class ToolWindows: @@ -59,25 +60,11 @@ def edit_shader_definitions(): def reset_frame_range(): """Set frame range to current asset""" - # Set FPS first - fps = {15: 'game', - 24: 'film', - 25: 'pal', - 30: 'ntsc', - 48: 'show', - 50: 'palf', - 60: 'ntscf', - 23.98: '23.976fps', - 23.976: '23.976fps', - 29.97: '29.97fps', - 47.952: '47.952fps', - 47.95: '47.952fps', - 59.94: '59.94fps', - 44100: '44100fps', - 48000: '48000fps' - }.get(float(legacy_io.Session.get("AVALON_FPS", 25)), "pal") - cmds.currentUnit(time=fps) + fps = lib.convert_to_maya_fps( + float(legacy_io.Session.get("AVALON_FPS", 25)) + ) + lib.set_scene_fps(fps) # Set frame start/end project_name = legacy_io.active_project() From f56e7bcbf879072c88fcf74944d41cf4366c4e79 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Feb 2023 10:51:21 +0100 Subject: [PATCH 24/55] removed deprecated functions from openpype lib --- openpype/lib/__init__.py | 43 -- openpype/lib/anatomy.py | 38 -- openpype/lib/avalon_context.py | 431 +----------------- openpype/lib/plugin_tools.py | 119 ----- .../tests/test_lib_restructuralization.py | 6 - openpype/tests/test_pyblish_filter.py | 6 +- 6 files changed, 7 insertions(+), 636 deletions(-) delete mode 100644 openpype/lib/anatomy.py diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index b5fb955a84..9eb7724a60 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -82,9 +82,6 @@ from .mongo import ( validate_mongo_connection, OpenPypeMongoConnection ) -from .anatomy import ( - Anatomy -) from .dateutils import ( get_datetime_data, @@ -119,36 +116,19 @@ from .transcoding import ( ) from .avalon_context import ( CURRENT_DOC_SCHEMAS, - PROJECT_NAME_ALLOWED_SYMBOLS, - PROJECT_NAME_REGEX, create_project, - is_latest, - any_outdated, - get_asset, - get_linked_assets, - get_latest_version, - get_system_general_anatomy_data, get_workfile_template_key, get_workfile_template_key_from_context, - get_workdir_data, - get_workdir, - get_workdir_with_workdir_data, get_last_workfile_with_version, get_last_workfile, - create_workfile_doc, - save_workfile_data_to_doc, - get_workfile_doc, - BuildWorkfile, get_creator_by_name, get_custom_workfile_template, - change_timer_to_current_context, - get_custom_workfile_template_by_context, get_custom_workfile_template_by_string_context, get_custom_workfile_template @@ -186,8 +166,6 @@ from .plugin_tools import ( get_subset_name, get_subset_name_with_asset_doc, prepare_template_data, - filter_pyblish_plugins, - set_plugin_attributes_from_settings, source_hash, ) @@ -278,34 +256,17 @@ __all__ = [ "convert_ffprobe_fps_to_float", "CURRENT_DOC_SCHEMAS", - "PROJECT_NAME_ALLOWED_SYMBOLS", - "PROJECT_NAME_REGEX", "create_project", - "is_latest", - "any_outdated", - "get_asset", - "get_linked_assets", - "get_latest_version", - "get_system_general_anatomy_data", "get_workfile_template_key", "get_workfile_template_key_from_context", - "get_workdir_data", - "get_workdir", - "get_workdir_with_workdir_data", "get_last_workfile_with_version", "get_last_workfile", - "create_workfile_doc", - "save_workfile_data_to_doc", - "get_workfile_doc", - "BuildWorkfile", "get_creator_by_name", - "change_timer_to_current_context", - "get_custom_workfile_template_by_context", "get_custom_workfile_template_by_string_context", "get_custom_workfile_template", @@ -338,8 +299,6 @@ __all__ = [ "TaskNotSetError", "get_subset_name", "get_subset_name_with_asset_doc", - "filter_pyblish_plugins", - "set_plugin_attributes_from_settings", "source_hash", "format_file_size", @@ -358,8 +317,6 @@ __all__ = [ "terminal", - "Anatomy", - "get_datetime_data", "get_formatted_current_time", diff --git a/openpype/lib/anatomy.py b/openpype/lib/anatomy.py deleted file mode 100644 index 6d339f058f..0000000000 --- a/openpype/lib/anatomy.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Code related to project Anatomy was moved -to 'openpype.pipeline.anatomy' please change your imports as soon as -possible. File will be probably removed in OpenPype 3.14.* -""" - -import warnings -import functools - - -class AnatomyDeprecatedWarning(DeprecationWarning): - pass - - -def anatomy_deprecated(func): - """Mark functions as deprecated. - - It will result in a warning being emitted when the function is used. - """ - - @functools.wraps(func) - def new_func(*args, **kwargs): - warnings.simplefilter("always", AnatomyDeprecatedWarning) - warnings.warn( - ( - "Deprecated import of 'Anatomy'." - " Class was moved to 'openpype.pipeline.anatomy'." - " Please change your imports of Anatomy in codebase." - ), - category=AnatomyDeprecatedWarning - ) - return func(*args, **kwargs) - return new_func - - -@anatomy_deprecated -def Anatomy(*args, **kwargs): - from openpype.pipeline.anatomy import Anatomy - return Anatomy(*args, **kwargs) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 12f4a5198b..a9ae27cb79 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -1,6 +1,5 @@ """Should be used only inside of hosts.""" -import os -import copy + import platform import logging import functools @@ -10,17 +9,12 @@ import six from openpype.client import ( get_project, - get_assets, get_asset_by_name, - get_last_version_by_subset_name, - get_workfile_info, ) from openpype.client.operations import ( CURRENT_ASSET_DOC_SCHEMA, CURRENT_PROJECT_SCHEMA, CURRENT_PROJECT_CONFIG_SCHEMA, - PROJECT_NAME_ALLOWED_SYMBOLS, - PROJECT_NAME_REGEX, ) from .profiles_filtering import filter_profiles from .path_templates import StringTemplate @@ -128,70 +122,6 @@ def with_pipeline_io(func): return wrapped -@deprecated("openpype.pipeline.context_tools.is_representation_from_latest") -def is_latest(representation): - """Return whether the representation is from latest version - - Args: - representation (dict): The representation document from the database. - - Returns: - bool: Whether the representation is of latest version. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.context_tools import is_representation_from_latest - - return is_representation_from_latest(representation) - - -@deprecated("openpype.pipeline.load.any_outdated_containers") -def any_outdated(): - """Return whether the current scene has any outdated content. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.load import any_outdated_containers - - return any_outdated_containers() - - -@deprecated("openpype.pipeline.context_tools.get_current_project_asset") -def get_asset(asset_name=None): - """ Returning asset document from database by its name. - - Doesn't count with duplicities on asset names! - - Args: - asset_name (str) - - Returns: - (MongoDB document) - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.context_tools import get_current_project_asset - - return get_current_project_asset(asset_name=asset_name) - - -@deprecated("openpype.pipeline.template_data.get_general_template_data") -def get_system_general_anatomy_data(system_settings=None): - """ - Deprecated: - Function will be removed after release version 3.15.* - """ - from openpype.pipeline.template_data import get_general_template_data - - return get_general_template_data(system_settings) - - @deprecated("openpype.client.get_linked_asset_ids") def get_linked_asset_ids(asset_doc): """Return linked asset ids for `asset_doc` from DB @@ -214,66 +144,6 @@ def get_linked_asset_ids(asset_doc): return get_linked_asset_ids(project_name, asset_doc=asset_doc) -@deprecated("openpype.client.get_linked_assets") -def get_linked_assets(asset_doc): - """Return linked assets for `asset_doc` from DB - - Args: - asset_doc (dict): Asset document from DB - - Returns: - (list) Asset documents of input links for passed asset doc. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline import legacy_io - from openpype.client import get_linked_assets - - project_name = legacy_io.active_project() - - return get_linked_assets(project_name, asset_doc=asset_doc) - - -@deprecated("openpype.client.get_last_version_by_subset_name") -def get_latest_version(asset_name, subset_name, dbcon=None, project_name=None): - """Retrieve latest version from `asset_name`, and `subset_name`. - - Do not use if you want to query more than 5 latest versions as this method - query 3 times to mongo for each call. For those cases is better to use - more efficient way, e.g. with help of aggregations. - - Args: - asset_name (str): Name of asset. - subset_name (str): Name of subset. - dbcon (AvalonMongoDB, optional): Avalon Mongo connection with Session. - project_name (str, optional): Find latest version in specific project. - - Returns: - None: If asset, subset or version were not found. - dict: Last version document for entered. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - if not project_name: - if not dbcon: - from openpype.pipeline import legacy_io - - log.debug("Using `legacy_io` for query.") - dbcon = legacy_io - # Make sure is installed - dbcon.install() - - project_name = dbcon.active_project() - - return get_last_version_by_subset_name( - project_name, subset_name, asset_name=asset_name - ) - - @deprecated( "openpype.pipeline.workfile.get_workfile_template_key_from_context") def get_workfile_template_key_from_context( @@ -361,142 +231,6 @@ def get_workfile_template_key( ) -@deprecated("openpype.pipeline.template_data.get_template_data") -def get_workdir_data(project_doc, asset_doc, task_name, host_name): - """Prepare data for workdir template filling from entered information. - - Args: - project_doc (dict): Mongo document of project from MongoDB. - asset_doc (dict): Mongo document of asset from MongoDB. - task_name (str): Task name for which are workdir data preapred. - host_name (str): Host which is used to workdir. This is required - because workdir template may contain `{app}` key. - - Returns: - dict: Data prepared for filling workdir template. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.template_data import get_template_data - - return get_template_data( - project_doc, asset_doc, task_name, host_name - ) - - -@deprecated("openpype.pipeline.workfile.get_workdir_with_workdir_data") -def get_workdir_with_workdir_data( - workdir_data, anatomy=None, project_name=None, template_key=None -): - """Fill workdir path from entered data and project's anatomy. - - It is possible to pass only project's name instead of project's anatomy but - one of them **must** be entered. It is preferred to enter anatomy if is - available as initialization of a new Anatomy object may be time consuming. - - Args: - workdir_data (dict): Data to fill workdir template. - anatomy (Anatomy): Anatomy object for specific project. Optional if - `project_name` is entered. - project_name (str): Project's name. Optional if `anatomy` is entered - otherwise Anatomy object is created with using the project name. - template_key (str): Key of work templates in anatomy templates. If not - passed `get_workfile_template_key_from_context` is used to get it. - dbcon(AvalonMongoDB): Mongo connection. Required only if 'template_key' - and 'project_name' are not passed. - - Returns: - TemplateResult: Workdir path. - - Raises: - ValueError: When both `anatomy` and `project_name` are set to None. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - if not anatomy and not project_name: - raise ValueError(( - "Missing required arguments one of `project_name` or `anatomy`" - " must be entered." - )) - - if not project_name: - project_name = anatomy.project_name - - from openpype.pipeline.workfile import get_workdir_with_workdir_data - - return get_workdir_with_workdir_data( - workdir_data, project_name, anatomy, template_key - ) - - -@deprecated("openpype.pipeline.workfile.get_workdir_with_workdir_data") -def get_workdir( - project_doc, - asset_doc, - task_name, - host_name, - anatomy=None, - template_key=None -): - """Fill workdir path from entered data and project's anatomy. - - Args: - project_doc (dict): Mongo document of project from MongoDB. - asset_doc (dict): Mongo document of asset from MongoDB. - task_name (str): Task name for which are workdir data preapred. - host_name (str): Host which is used to workdir. This is required - because workdir template may contain `{app}` key. In `Session` - is stored under `AVALON_APP` key. - anatomy (Anatomy): Optional argument. Anatomy object is created using - project name from `project_doc`. It is preferred to pass this - argument as initialization of a new Anatomy object may be time - consuming. - template_key (str): Key of work templates in anatomy templates. Default - value is defined in `get_workdir_with_workdir_data`. - - Returns: - TemplateResult: Workdir path. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.workfile import get_workdir - # Output is TemplateResult object which contain useful data - return get_workdir( - project_doc, - asset_doc, - task_name, - host_name, - anatomy, - template_key - ) - - -@deprecated("openpype.pipeline.context_tools.get_template_data_from_session") -def template_data_from_session(session=None): - """ Return dictionary with template from session keys. - - Args: - session (dict, Optional): The Session to use. If not provided use the - currently active global Session. - - Returns: - dict: All available data from session. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.context_tools import get_template_data_from_session - - return get_template_data_from_session(session) - - @deprecated("openpype.pipeline.context_tools.compute_session_changes") def compute_session_changes( session, task=None, asset=None, app=None, template_key=None @@ -588,133 +322,6 @@ def update_current_task(task=None, asset=None, app=None, template_key=None): return change_current_context(asset, task, template_key) -@deprecated("openpype.client.get_workfile_info") -def get_workfile_doc(asset_id, task_name, filename, dbcon=None): - """Return workfile document for entered context. - - Do not use this method to get more than one document. In that cases use - custom query as this will return documents from database one by one. - - Args: - asset_id (ObjectId): Mongo ID of an asset under which workfile belongs. - task_name (str): Name of task under which the workfile belongs. - filename (str): Name of a workfile. - dbcon (AvalonMongoDB): Optionally enter avalon AvalonMongoDB object and - `legacy_io` is used if not entered. - - Returns: - dict: Workfile document or None. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - # Use legacy_io if dbcon is not entered - if not dbcon: - from openpype.pipeline import legacy_io - dbcon = legacy_io - - project_name = dbcon.active_project() - return get_workfile_info(project_name, asset_id, task_name, filename) - - -@deprecated -def create_workfile_doc(asset_doc, task_name, filename, workdir, dbcon=None): - """Creates or replace workfile document in mongo. - - Do not use this method to update data. This method will remove all - additional data from existing document. - - Args: - asset_doc (dict): Document of asset under which workfile belongs. - task_name (str): Name of task for which is workfile related to. - filename (str): Filename of workfile. - workdir (str): Path to directory where `filename` is located. - dbcon (AvalonMongoDB): Optionally enter avalon AvalonMongoDB object and - `legacy_io` is used if not entered. - """ - - from openpype.pipeline import Anatomy - from openpype.pipeline.template_data import get_template_data - - # Use legacy_io if dbcon is not entered - if not dbcon: - from openpype.pipeline import legacy_io - dbcon = legacy_io - - # Filter of workfile document - doc_filter = { - "type": "workfile", - "parent": asset_doc["_id"], - "task_name": task_name, - "filename": filename - } - # Document data are copy of filter - doc_data = copy.deepcopy(doc_filter) - - # Prepare project for workdir data - project_name = dbcon.active_project() - project_doc = get_project(project_name) - workdir_data = get_template_data( - project_doc, asset_doc, task_name, dbcon.Session["AVALON_APP"] - ) - # Prepare anatomy - anatomy = Anatomy(project_name) - # Get workdir path (result is anatomy.TemplateResult) - template_workdir = get_workdir_with_workdir_data( - workdir_data, anatomy - ) - template_workdir_path = str(template_workdir).replace("\\", "/") - - # Replace slashses in workdir path where workfile is located - mod_workdir = workdir.replace("\\", "/") - - # Replace workdir from templates with rootless workdir - rootles_workdir = mod_workdir.replace( - template_workdir_path, - template_workdir.rootless.replace("\\", "/") - ) - - doc_data["schema"] = "pype:workfile-1.0" - doc_data["files"] = ["/".join([rootles_workdir, filename])] - doc_data["data"] = {} - - dbcon.replace_one( - doc_filter, - doc_data, - upsert=True - ) - - -@deprecated -def save_workfile_data_to_doc(workfile_doc, data, dbcon=None): - if not workfile_doc: - # TODO add log message - return - - if not data: - return - - # Use legacy_io if dbcon is not entered - if not dbcon: - from openpype.pipeline import legacy_io - dbcon = legacy_io - - # Convert data to mongo modification keys/values - # - this is naive implementation which does not expect nested - # dictionaries - set_data = {} - for key, value in data.items(): - new_key = "data.{}".format(key) - set_data[new_key] = value - - # Update workfile document with data - dbcon.update_one( - {"_id": workfile_doc["_id"]}, - {"$set": set_data} - ) - - @deprecated("openpype.pipeline.workfile.BuildWorkfile") def BuildWorkfile(): """Build workfile class was moved to workfile pipeline. @@ -747,38 +354,6 @@ def get_creator_by_name(creator_name, case_sensitive=False): return get_legacy_creator_by_name(creator_name, case_sensitive) -@deprecated -def change_timer_to_current_context(): - """Called after context change to change timers. - - Deprecated: - This method is specific for TimersManager module so please use the - functionality from there. Function will be removed after release - version 3.15.* - """ - - from openpype.pipeline import legacy_io - - webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") - if not webserver_url: - log.warning("Couldn't find webserver url") - return - - rest_api_url = "{}/timers_manager/start_timer".format(webserver_url) - try: - import requests - except Exception: - log.warning("Couldn't start timer") - return - data = { - "project_name": legacy_io.Session["AVALON_PROJECT"], - "asset_name": legacy_io.Session["AVALON_ASSET"], - "task_name": legacy_io.Session["AVALON_TASK"] - } - - requests.post(rest_api_url, json=data) - - def _get_task_context_data_for_anatomy( project_doc, asset_doc, task_name, anatomy=None ): @@ -800,6 +375,8 @@ def _get_task_context_data_for_anatomy( dict: With Anatomy context data. """ + from openpype.pipeline.template_data import get_general_template_data + if anatomy is None: from openpype.pipeline import Anatomy anatomy = Anatomy(project_doc["name"]) @@ -840,7 +417,7 @@ def _get_task_context_data_for_anatomy( } } - system_general_data = get_system_general_anatomy_data() + system_general_data = get_general_template_data() data.update(system_general_data) return data diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index 1e157dfbfd..10fd3940b8 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -8,7 +8,6 @@ import warnings import functools from openpype.client import get_asset_by_id -from openpype.settings import get_project_settings log = logging.getLogger(__name__) @@ -101,8 +100,6 @@ def get_subset_name_with_asset_doc( is not passed. dynamic_data (dict): Dynamic data specific for a creator which creates instance. - dbcon (AvalonMongoDB): Mongo connection to be able query asset document - if 'asset_doc' is not passed. """ from openpype.pipeline.create import get_subset_name @@ -202,122 +199,6 @@ def prepare_template_data(fill_pairs): return fill_data -@deprecated("openpype.pipeline.publish.lib.filter_pyblish_plugins") -def filter_pyblish_plugins(plugins): - """Filter pyblish plugins by presets. - - This servers as plugin filter / modifier for pyblish. It will load plugin - definitions from presets and filter those needed to be excluded. - - Args: - plugins (dict): Dictionary of plugins produced by :mod:`pyblish-base` - `discover()` method. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.publish.lib import filter_pyblish_plugins - - filter_pyblish_plugins(plugins) - - -@deprecated -def set_plugin_attributes_from_settings( - plugins, superclass, host_name=None, project_name=None -): - """Change attribute values on Avalon plugins by project settings. - - This function should be used only in host context. Modify - behavior of plugins. - - Args: - plugins (list): Plugins discovered by origin avalon discover method. - superclass (object): Superclass of plugin type (e.g. Cretor, Loader). - host_name (str): Name of host for which plugins are loaded and from. - Value from environment `AVALON_APP` is used if not entered. - project_name (str): Name of project for which settings will be loaded. - Value from environment `AVALON_PROJECT` is used if not entered. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - # Function is not used anymore - from openpype.pipeline import LegacyCreator, LoaderPlugin - - # determine host application to use for finding presets - if host_name is None: - host_name = os.environ.get("AVALON_APP") - - if project_name is None: - project_name = os.environ.get("AVALON_PROJECT") - - # map plugin superclass to preset json. Currently supported is load and - # create (LoaderPlugin and LegacyCreator) - plugin_type = None - if superclass is LoaderPlugin or issubclass(superclass, LoaderPlugin): - plugin_type = "load" - elif superclass is LegacyCreator or issubclass(superclass, LegacyCreator): - plugin_type = "create" - - if not host_name or not project_name or plugin_type is None: - msg = "Skipped attributes override from settings." - if not host_name: - msg += " Host name is not defined." - - if not project_name: - msg += " Project name is not defined." - - if plugin_type is None: - msg += " Plugin type is unsupported for class {}.".format( - superclass.__name__ - ) - - print(msg) - return - - print(">>> Finding presets for {}:{} ...".format(host_name, plugin_type)) - - project_settings = get_project_settings(project_name) - plugin_type_settings = ( - project_settings - .get(host_name, {}) - .get(plugin_type, {}) - ) - global_type_settings = ( - project_settings - .get("global", {}) - .get(plugin_type, {}) - ) - if not global_type_settings and not plugin_type_settings: - return - - for plugin in plugins: - plugin_name = plugin.__name__ - - plugin_settings = None - # Look for plugin settings in host specific settings - if plugin_name in plugin_type_settings: - plugin_settings = plugin_type_settings[plugin_name] - - # Look for plugin settings in global settings - elif plugin_name in global_type_settings: - plugin_settings = global_type_settings[plugin_name] - - if not plugin_settings: - continue - - print(">>> We have preset for {}".format(plugin_name)) - for option, value in plugin_settings.items(): - if option == "enabled" and value is False: - setattr(plugin, "active", False) - print(" - is disabled by preset") - else: - setattr(plugin, option, value) - print(" - setting `{}`: `{}`".format(option, value)) - - def source_hash(filepath, *args): """Generate simple identifier for a source file. This is used to identify whether a source file has previously been diff --git a/openpype/tests/test_lib_restructuralization.py b/openpype/tests/test_lib_restructuralization.py index c8952e5a1c..669706d470 100644 --- a/openpype/tests/test_lib_restructuralization.py +++ b/openpype/tests/test_lib_restructuralization.py @@ -5,11 +5,9 @@ def test_backward_compatibility(printer): printer("Test if imports still work") try: - from openpype.lib import filter_pyblish_plugins from openpype.lib import execute_hook from openpype.lib import PypeHook - from openpype.lib import get_latest_version from openpype.lib import ApplicationLaunchFailed from openpype.lib import get_ffmpeg_tool_path @@ -18,10 +16,6 @@ def test_backward_compatibility(printer): from openpype.lib import get_version_from_path from openpype.lib import version_up - from openpype.lib import is_latest - from openpype.lib import any_outdated - from openpype.lib import get_asset - from openpype.lib import get_linked_assets from openpype.lib import get_ffprobe_streams from openpype.hosts.fusion.lib import switch_item diff --git a/openpype/tests/test_pyblish_filter.py b/openpype/tests/test_pyblish_filter.py index ea23da26e4..b74784145f 100644 --- a/openpype/tests/test_pyblish_filter.py +++ b/openpype/tests/test_pyblish_filter.py @@ -1,9 +1,9 @@ -from . import lib +import os import pyblish.api import pyblish.util import pyblish.plugin -from openpype.lib import filter_pyblish_plugins -import os +from openpype.pipeline.publish.lib import filter_pyblish_plugins +from . import lib def test_pyblish_plugin_filter_modifier(printer, monkeypatch): From 5903dbce9e176d26d45681a86efe92f607a512e2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 14 Feb 2023 17:03:05 +0100 Subject: [PATCH 25/55] autofill precreate attributes if are not passed --- openpype/pipeline/create/context.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index ba566f93d4..1567acdb79 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -17,6 +17,7 @@ from openpype.lib.attribute_definitions import ( UnknownDef, serialize_attr_defs, deserialize_attr_defs, + get_default_values, ) from openpype.host import IPublishHost from openpype.pipeline import legacy_io @@ -1866,6 +1867,13 @@ class CreateContext: if pre_create_data is None: pre_create_data = {} + precreate_attr_defs = creator.get_pre_create_attr_defs() or [] + # Create default values of precreate data + _pre_create_data = get_default_values(precreate_attr_defs) + # Update passed precreate data to default values + # TODO validate types + _pre_create_data.update(pre_create_data) + subset_name = creator.get_subset_name( variant, task_name, @@ -1881,7 +1889,7 @@ class CreateContext: return creator.create( subset_name, instance_data, - pre_create_data + _pre_create_data ) def _create_with_unified_error( From ac4078259200edbdf88d58954b1e96e09468e5b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:14:19 +0100 Subject: [PATCH 26/55] fix used constant 'ActiveWindow' -> 'WindowActive' --- openpype/tools/publisher/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 097e289f32..a82f60d5a5 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -366,7 +366,7 @@ class PublisherWindow(QtWidgets.QDialog): def make_sure_is_visible(self): if self._window_is_visible: - self.setWindowState(QtCore.Qt.ActiveWindow) + self.setWindowState(QtCore.Qt.WindowActive) else: self.show() From 3e6a120eaa808bf69e0bdbe893b0dd8c21c6939a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 11:46:20 +0100 Subject: [PATCH 27/55] fix default settings of nuke --- openpype/settings/defaults/project_settings/nuke.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index cd8ea02272..2ec2028219 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -1,7 +1,6 @@ { "general": { "menu": { - "create": "ctrl+alt+c", "publish": "ctrl+alt+p", "load": "ctrl+alt+l", "manage": "ctrl+alt+m", @@ -246,6 +245,7 @@ "sourcetype": "python", "title": "Gizmo Note", "command": "nuke.nodes.StickyNote(label='You can create your own toolbar menu in the Nuke GizmoMenu of OpenPype')", + "icon": "", "shortcut": "" } ] From df532268a2e05b6b48074336453ab3e18b86e08f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:00:15 +0100 Subject: [PATCH 28/55] add family to instance data --- openpype/pipeline/create/context.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 1567acdb79..79c9805604 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1884,6 +1884,7 @@ class CreateContext: instance_data = { "asset": asset_doc["name"], "task": task_name, + "family": self.family, "variant": variant } return creator.create( From fb93780640ed5de588cb11499ccd68d1f6a91d75 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:08:45 +0100 Subject: [PATCH 29/55] use family form creator --- 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 79c9805604..89eec52676 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1884,7 +1884,7 @@ class CreateContext: instance_data = { "asset": asset_doc["name"], "task": task_name, - "family": self.family, + "family": creator.family, "variant": variant } return creator.create( From 222b39f024e5fcf8890145f08eba8298282ddb39 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Feb 2023 12:22:13 +0100 Subject: [PATCH 30/55] nuke: adding back Create shortcut it was removed accidentally --- openpype/settings/defaults/project_settings/nuke.json | 3 ++- .../schemas/projects_schema/schema_project_nuke.json | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 2ec2028219..d475c337d9 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -1,6 +1,7 @@ { "general": { "menu": { + "create": "ctrl+alt+c", "publish": "ctrl+alt+p", "load": "ctrl+alt+l", "manage": "ctrl+alt+m", @@ -532,4 +533,4 @@ "profiles": [] }, "filters": {} -} \ No newline at end of file +} diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json index b1a8cc1812..26c64e6219 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -17,6 +17,11 @@ "key": "menu", "label": "OpenPype Menu shortcuts", "children": [ + { + "type": "text", + "key": "create", + "label": "Create..." + }, { "type": "text", "key": "publish", @@ -288,4 +293,4 @@ "name": "schema_publish_gui_filter" } ] -} \ No newline at end of file +} From e93c5d0d4055db7e2ff8cd7067fda24ccc243250 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:26:27 +0100 Subject: [PATCH 31/55] OP-4928 - fix wrong usage of legacy_io Import was removed, but usage stayed. Now it should be replaced from context --- 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 cdea82cb05..3d82d6b6f0 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -193,7 +193,7 @@ class ImageCreator(Creator): instance_data.pop("uuid") if not instance_data.get("task"): - instance_data["task"] = legacy_io.Session.get("AVALON_TASK") + instance_data["task"] = self.create_context.get_current_task_name() if not instance_data.get("variant"): instance_data["variant"] = '' From 423f2bcbdadc723d6499f8f92a9ed391533a75e8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Feb 2023 12:26:50 +0100 Subject: [PATCH 32/55] removing python3 only code --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index 3216c596da..55a1346b08 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -54,6 +54,6 @@ def create_custom_tempdir(project_name, anatomy=None): os.makedirs(custom_tempdir) except IOError as error: raise IOError( - "Path couldn't be created: {}".format(error)) from error + "Path couldn't be created: {}".format(error)) return custom_tempdir From 410ea87e18a582628fbd456549207e2dac2ef164 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:27:44 +0100 Subject: [PATCH 33/55] OP-4928 - fix wrong usage of legacy_io Import should be removed. Now it should be replaced from context. --- openpype/hosts/aftereffects/plugins/create/create_render.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 10ded8b912..02f045b0ec 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -6,8 +6,7 @@ from openpype.hosts.aftereffects import api from openpype.pipeline import ( Creator, CreatedInstance, - CreatorError, - legacy_io, + CreatorError ) from openpype.hosts.aftereffects.api.pipeline import cache_and_get_instances from openpype.lib import prepare_template_data @@ -195,7 +194,7 @@ class RenderCreator(Creator): instance_data.pop("uuid") if not instance_data.get("task"): - instance_data["task"] = legacy_io.Session.get("AVALON_TASK") + instance_data["task"] = self.create_context.get_current_task_name() if not instance_data.get("creator_attributes"): is_old_farm = instance_data["family"] != "renderLocal" From eb5d1e3816b07760c6ffdc8c71999fe1167dfdf9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:29:52 +0100 Subject: [PATCH 34/55] resave to remove empty line --- openpype/settings/defaults/project_settings/nuke.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index d475c337d9..2999d1427d 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -533,4 +533,4 @@ "profiles": [] }, "filters": {} -} +} \ No newline at end of file From 66c42dde73174c8a3b288419a616f8c23b98064a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:32:41 +0100 Subject: [PATCH 35/55] OP-4928 - removed legacy_io in workfile creator in PS Legacy_io should be eradicated, replaced by abstracted methods --- .../photoshop/plugins/create/workfile_creator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/workfile_creator.py b/openpype/hosts/photoshop/plugins/create/workfile_creator.py index 8ee9a0d832..f5d56adcbc 100644 --- a/openpype/hosts/photoshop/plugins/create/workfile_creator.py +++ b/openpype/hosts/photoshop/plugins/create/workfile_creator.py @@ -2,8 +2,7 @@ import openpype.hosts.photoshop.api as api from openpype.client import get_asset_by_name from openpype.pipeline import ( AutoCreator, - CreatedInstance, - legacy_io + CreatedInstance ) from openpype.hosts.photoshop.api.pipeline import cache_and_get_instances @@ -38,10 +37,11 @@ class PSWorkfileCreator(AutoCreator): existing_instance = instance break - project_name = legacy_io.Session["AVALON_PROJECT"] - asset_name = legacy_io.Session["AVALON_ASSET"] - task_name = legacy_io.Session["AVALON_TASK"] - host_name = legacy_io.Session["AVALON_APP"] + context = self.create_context + project_name = context.get_current_project_name() + asset_name = context.get_current_asset_name() + task_name = context.get_current_task_name() + host_name = context.host_name if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) subset_name = self.get_subset_name( From 03013095023cdce494142740a70efdbce60cb03c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:33:35 +0100 Subject: [PATCH 36/55] OP-4928 - removed legacy_io in workfile creator in AE Legacy_io should be eradicated, replaced by abstracted methods --- .../aftereffects/plugins/create/workfile_creator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/workfile_creator.py b/openpype/hosts/aftereffects/plugins/create/workfile_creator.py index c698af896b..2e7b9d4a7e 100644 --- a/openpype/hosts/aftereffects/plugins/create/workfile_creator.py +++ b/openpype/hosts/aftereffects/plugins/create/workfile_creator.py @@ -2,8 +2,7 @@ import openpype.hosts.aftereffects.api as api from openpype.client import get_asset_by_name from openpype.pipeline import ( AutoCreator, - CreatedInstance, - legacy_io, + CreatedInstance ) from openpype.hosts.aftereffects.api.pipeline import cache_and_get_instances @@ -38,10 +37,11 @@ class AEWorkfileCreator(AutoCreator): existing_instance = instance break - project_name = legacy_io.Session["AVALON_PROJECT"] - asset_name = legacy_io.Session["AVALON_ASSET"] - task_name = legacy_io.Session["AVALON_TASK"] - host_name = legacy_io.Session["AVALON_APP"] + context = self.create_context + project_name = context.get_current_project_name() + asset_name = context.get_current_asset_name() + task_name = context.get_current_task_name() + host_name = context.host_name if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) From 6ab581df7da24302158d32c9a68a9baca33b1cb3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:45:22 +0100 Subject: [PATCH 37/55] on first reset always go to create tab --- openpype/tools/publisher/window.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 097e289f32..5ef25c9f8c 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -647,10 +647,7 @@ class PublisherWindow(QtWidgets.QDialog): # otherwise 'create' is used # - this happens only on first show if first_reset: - if self._overview_widget.has_items(): - self._go_to_publish_tab() - else: - self._go_to_create_tab() + self._go_to_create_tab() elif ( not self._is_on_create_tab() From 1cc9a7a90fd6deef343b45f0944bcefed6497521 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:45:45 +0100 Subject: [PATCH 38/55] change tab on reset only if is on report tab (Details for user) --- openpype/tools/publisher/window.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 5ef25c9f8c..ef9c99d998 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -649,11 +649,8 @@ class PublisherWindow(QtWidgets.QDialog): if first_reset: self._go_to_create_tab() - elif ( - not self._is_on_create_tab() - and not self._is_on_publish_tab() - ): - # If current tab is not 'Create' or 'Publish' go to 'Publish' + elif self._is_on_report_tab(): + # Go to 'Publish' tab if is on 'Details' tab # - this can happen when publishing started and was reset # at that moment it doesn't make sense to stay at publish # specific tabs. From 37a7841db8024341cbc4fa0c7881c6925ab7a188 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:46:02 +0100 Subject: [PATCH 39/55] reordered methods to match order of tabs in UI --- openpype/tools/publisher/window.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index ef9c99d998..86eed31afd 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -566,24 +566,24 @@ class PublisherWindow(QtWidgets.QDialog): def _go_to_publish_tab(self): self._set_current_tab("publish") - def _go_to_details_tab(self): - self._set_current_tab("details") - def _go_to_report_tab(self): self._set_current_tab("report") + def _go_to_details_tab(self): + self._set_current_tab("details") + def _is_on_create_tab(self): return self._is_current_tab("create") def _is_on_publish_tab(self): return self._is_current_tab("publish") - def _is_on_details_tab(self): - return self._is_current_tab("details") - def _is_on_report_tab(self): return self._is_current_tab("report") + def _is_on_details_tab(self): + return self._is_current_tab("details") + def _set_publish_overlay_visibility(self, visible): if visible: widget = self._publish_overlay From b70c6e4bfd433c1470efa1fde319834ec7068264 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 18:32:56 +0100 Subject: [PATCH 40/55] OP-4938 - fix obsolete access to instance change --- 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 02f045b0ec..c20b0ec51b 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -126,7 +126,7 @@ class RenderCreator(Creator): subset_change = _changes.get("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: From eef8990101eef7e79ad34b7b8429c164b93e14c0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:52:39 +0100 Subject: [PATCH 41/55] public 'discover' function can expect all possible arguments --- openpype/pipeline/plugin_discover.py | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/plugin_discover.py b/openpype/pipeline/plugin_discover.py index 7edd9ac290..e5257b801a 100644 --- a/openpype/pipeline/plugin_discover.py +++ b/openpype/pipeline/plugin_discover.py @@ -135,11 +135,12 @@ class PluginDiscoverContext(object): allow_duplicates (bool): Validate class name duplications. ignore_classes (list): List of classes that will be ignored and not added to result. + return_report (bool): Output will be full report if set to 'True'. Returns: - DiscoverResult: Object holding succesfully discovered plugins, - ignored plugins, plugins with missing abstract implementation - and duplicated plugin. + Union[DiscoverResult, list[Any]]: Object holding successfully + discovered plugins, ignored plugins, plugins with missing + abstract implementation and duplicated plugin. """ if not ignore_classes: @@ -268,9 +269,34 @@ class _GlobalDiscover: return cls._context -def discover(superclass, allow_duplicates=True): +def discover( + superclass, + allow_duplicates=True, + ignore_classes=None, + return_report=False +): + """Find and return subclasses of `superclass` + + Args: + superclass (type): Class which determines discovered subclasses. + allow_duplicates (bool): Validate class name duplications. + ignore_classes (list): List of classes that will be ignored + and not added to result. + return_report (bool): Output will be full report if set to 'True'. + + Returns: + Union[DiscoverResult, list[Any]]: Object holding successfully + discovered plugins, ignored plugins, plugins with missing + abstract implementation and duplicated plugin. + """ + context = _GlobalDiscover.get_context() - return context.discover(superclass, allow_duplicates) + return context.discover( + superclass, + allow_duplicates, + ignore_classes, + return_report + ) def get_last_discovered_plugins(superclass): From 542405775a36c08075dc118dc0801be312d0e5e1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:53:15 +0100 Subject: [PATCH 42/55] discover creators and convertors can pass other arguments to 'discover' function --- openpype/pipeline/create/creator_plugins.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 53acb618ed..74e6cb289a 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -605,12 +605,12 @@ class AutoCreator(BaseCreator): pass -def discover_creator_plugins(): - return discover(BaseCreator) +def discover_creator_plugins(*args, **kwargs): + return discover(BaseCreator, *args, **kwargs) -def discover_convertor_plugins(): - return discover(SubsetConvertorPlugin) +def discover_convertor_plugins(*args, **kwargs): + return discover(SubsetConvertorPlugin, *args, **kwargs) def discover_legacy_creator_plugins(): From 86a9c77c1e970a78d08888af5326c19c0fd51aa3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:53:47 +0100 Subject: [PATCH 43/55] reuse 'DiscoverResult' from plugin discover --- openpype/pipeline/create/context.py | 4 ++-- openpype/pipeline/publish/__init__.py | 2 -- openpype/pipeline/publish/lib.py | 23 +---------------------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 89eec52676..8b5da74bc7 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -21,6 +21,7 @@ from openpype.lib.attribute_definitions import ( ) from openpype.host import IPublishHost from openpype.pipeline import legacy_io +from openpype.pipeline.plugin_discover import DiscoverResult from .creator_plugins import ( Creator, @@ -1620,8 +1621,7 @@ class CreateContext: from openpype.pipeline import OpenPypePyblishPluginMixin from openpype.pipeline.publish import ( - publish_plugins_discover, - DiscoverResult + publish_plugins_discover ) # Reset publish plugins diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index dc6fc0f97a..86f3bde0dc 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -25,7 +25,6 @@ from .publish_plugins import ( from .lib import ( get_publish_template_name, - DiscoverResult, publish_plugins_discover, load_help_content_from_plugin, load_help_content_from_filepath, @@ -68,7 +67,6 @@ __all__ = ( "get_publish_template_name", - "DiscoverResult", "publish_plugins_discover", "load_help_content_from_plugin", "load_help_content_from_filepath", diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index d0a9396a42..50623e5110 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -21,6 +21,7 @@ from openpype.settings import ( from openpype.pipeline import ( tempdir ) +from openpype.pipeline.plugin_discover import DiscoverResult from .contants import ( DEFAULT_PUBLISH_TEMPLATE, @@ -202,28 +203,6 @@ def get_publish_template_name( return template or default_template -class DiscoverResult: - """Hold result of publish plugins discovery. - - Stores discovered plugins duplicated plugins and file paths which - crashed on execution of file. - """ - def __init__(self): - self.plugins = [] - self.crashed_file_paths = {} - self.duplicated_plugins = [] - - def __iter__(self): - for plugin in self.plugins: - yield plugin - - def __getitem__(self, item): - return self.plugins[item] - - def __setitem__(self, item, value): - self.plugins[item] = value - - class HelpContent: def __init__(self, title, description, detail=None): self.title = title From b3a86bdbf540c8e8f0df6c52cb0873f2b84b513b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:54:18 +0100 Subject: [PATCH 44/55] store reports of discovered plugins --- openpype/pipeline/create/context.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 8b5da74bc7..5f1371befa 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1379,6 +1379,8 @@ class CreateContext: # Instances by their ID self._instances_by_id = {} + self.creator_discover_result = None + self.convertor_discover_result = None # Discovered creators self.creators = {} # Prepare categories of creators @@ -1666,7 +1668,9 @@ class CreateContext: creators = {} autocreators = {} manual_creators = {} - for creator_class in discover_creator_plugins(): + report = discover_creator_plugins(return_report=True) + self.creator_discover_result = report + for creator_class in report.plugins: if inspect.isabstract(creator_class): self.log.info( "Skipping abstract Creator {}".format(str(creator_class)) @@ -1711,7 +1715,9 @@ class CreateContext: def _reset_convertor_plugins(self): convertors_plugins = {} - for convertor_class in discover_convertor_plugins(): + report = discover_convertor_plugins(return_report=True) + self.convertor_discover_result = report + for convertor_class in report.plugins: if inspect.isabstract(convertor_class): self.log.info( "Skipping abstract Creator {}".format(str(convertor_class)) From 0dc73617a65dfc947c3c2715a6948928403c9f54 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:54:44 +0100 Subject: [PATCH 45/55] use reports to store crashed files to publish report --- openpype/tools/publisher/control.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 9ab37f2a3e..023a20ca5e 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -169,6 +169,8 @@ class PublishReport: def __init__(self, controller): self.controller = controller + self._create_discover_result = None + self._convert_discover_result = None self._publish_discover_result = None self._plugin_data = [] self._plugin_data_with_plugin = [] @@ -181,6 +183,10 @@ class PublishReport: def reset(self, context, create_context): """Reset report and clear all data.""" + self._create_discover_result = create_context.creator_discover_result + self._convert_discover_result = ( + create_context.convertor_discover_result + ) self._publish_discover_result = create_context.publish_discover_result self._plugin_data = [] self._plugin_data_with_plugin = [] @@ -293,9 +299,19 @@ class PublishReport: if plugin not in self._stored_plugins: plugins_data.append(self._create_plugin_data_item(plugin)) - crashed_file_paths = {} + reports = [] + if self._create_discover_result is not None: + reports.append(self._create_discover_result) + + if self._convert_discover_result is not None: + reports.append(self._convert_discover_result) + if self._publish_discover_result is not None: - items = self._publish_discover_result.crashed_file_paths.items() + reports.append(self._publish_discover_result) + + crashed_file_paths = {} + for report in reports: + items = report.crashed_file_paths.items() for filepath, exc_info in items: crashed_file_paths[filepath] = "".join( traceback.format_exception(*exc_info) From 56470c47e23bcb337b48731252439c22e0300130 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:55:32 +0100 Subject: [PATCH 46/55] added Args to documentation --- openpype/pipeline/create/creator_plugins.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 74e6cb289a..628245faf2 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -153,6 +153,12 @@ class BaseCreator: Single object should be used for multiple instances instead of single instance per one creator object. Do not store temp data or mid-process data to `self` if it's not Plugin specific. + + Args: + project_settings (Dict[str, Any]): Project settings. + system_settings (Dict[str, Any]): System settings. + create_context (CreateContext): Context which initialized creator. + headless (bool): Running in headless mode. """ # Label shown in UI From 7739c3a54f0b12e106d05f8742dfa63266bbde7f Mon Sep 17 00:00:00 2001 From: mre7a <68907585+mre7a@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:43:13 +0100 Subject: [PATCH 47/55] Update openpype/hosts/maya/api/workfile_template_builder.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/maya/api/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index 56a53c070c..1e6094e996 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -2,7 +2,7 @@ import json from maya import cmds -from openpype.pipeline import registered_host, legacy_io +from openpype.pipeline import registered_host, get_current_asset_name from openpype.pipeline.workfile.workfile_template_builder import ( TemplateAlreadyImported, AbstractTemplateBuilder, From 035c888d9be86d546c49569b370dbe1264844fa7 Mon Sep 17 00:00:00 2001 From: mre7a <68907585+mre7a@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:43:55 +0100 Subject: [PATCH 48/55] Update openpype/hosts/maya/api/workfile_template_builder.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/maya/api/workfile_template_builder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index 1e6094e996..094f45221c 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -50,6 +50,7 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): return True # update imported sets information + asset_name = get_current_asset_name() for node in imported_sets: if not cmds.attributeQuery("id", node=node, exists=True): continue @@ -57,9 +58,9 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): continue if not cmds.attributeQuery("asset", node=node, exists=True): continue - asset = legacy_io.Session["AVALON_ASSET"] - cmds.setAttr("{}.asset".format(node), asset, type="string") + cmds.setAttr( + "{}.asset".format(node), asset_name, type="string") return True From c2b2dbb3f32aec8041b6cf6b1da08b3968d330ca Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 10:47:24 +0100 Subject: [PATCH 49/55] fix compatibility of QAction in Publisher --- openpype/tools/publisher/widgets/widgets.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 587bcb059d..8da3886419 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -250,21 +250,25 @@ class PublishReportBtn(PublishIconBtn): self._actions = [] def add_action(self, label, identifier): - action = QtWidgets.QAction(label) - action.setData(identifier) - action.triggered.connect( - functools.partial(self._on_action_trigger, action) + self._actions.append( + (label, identifier) ) - self._actions.append(action) - def _on_action_trigger(self, action): - identifier = action.data() + def _on_action_trigger(self, identifier): self.triggered.emit(identifier) def mouseReleaseEvent(self, event): super(PublishReportBtn, self).mouseReleaseEvent(event) menu = QtWidgets.QMenu(self) - menu.addActions(self._actions) + actions = [] + for item in self._actions: + label, identifier = item + action = QtWidgets.QAction(label, menu) + action.triggered.connect( + functools.partial(self._on_action_trigger, identifier) + ) + actions.append(action) + menu.addActions(actions) menu.exec_(event.globalPos()) From 8eef66c3df8583a8503caefd69e6bd7255fd1498 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 12:29:43 +0100 Subject: [PATCH 50/55] Fix creation of DiscoverResult for pyblish plugins --- openpype/pipeline/create/context.py | 7 ++++--- openpype/pipeline/publish/lib.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 5f1371befa..7672c49eb3 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -8,6 +8,9 @@ import inspect from uuid import uuid4 from contextlib import contextmanager +import pyblish.logic +import pyblish.api + from openpype.client import get_assets, get_asset_by_name from openpype.settings import ( get_system_settings, @@ -1619,8 +1622,6 @@ class CreateContext: self._reset_convertor_plugins() def _reset_publish_plugins(self, discover_publish_plugins): - import pyblish.logic - from openpype.pipeline import OpenPypePyblishPluginMixin from openpype.pipeline.publish import ( publish_plugins_discover @@ -1629,7 +1630,7 @@ class CreateContext: # Reset publish plugins self._attr_plugins_by_family = {} - discover_result = DiscoverResult() + discover_result = DiscoverResult(pyblish.api.Plugin) plugins_with_defs = [] plugins_by_targets = [] plugins_mismatch_targets = [] diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 50623e5110..5f95c6695e 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -270,7 +270,7 @@ def publish_plugins_discover(paths=None): """ # The only difference with `pyblish.api.discover` - result = DiscoverResult() + result = DiscoverResult(pyblish.api.Plugin) plugins = dict() plugin_names = [] From 70ab3cd2ab75d29e6ceb60e793a9ffc85ecb3f5c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 14:14:45 +0100 Subject: [PATCH 51/55] fix newly added creators on refresh --- openpype/tools/publisher/widgets/create_widget.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index dbf075c216..ef9c5b98fe 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -457,13 +457,14 @@ class CreateWidget(QtWidgets.QWidget): # TODO add details about creator new_creators.add(identifier) if identifier in existing_items: + is_new = False item = existing_items[identifier] else: + is_new = True item = QtGui.QStandardItem() item.setFlags( QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable ) - self._creators_model.appendRow(item) item.setData(creator_item.label, QtCore.Qt.DisplayRole) item.setData(creator_item.show_order, CREATOR_SORT_ROLE) @@ -473,6 +474,8 @@ class CreateWidget(QtWidgets.QWidget): CREATOR_THUMBNAIL_ENABLED_ROLE ) item.setData(creator_item.family, FAMILY_ROLE) + if is_new: + self._creators_model.appendRow(item) # Remove families that are no more available for identifier in (old_creators - new_creators): From a305de03f0116e06e490266d8f34c967f12d32f6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 15:31:33 +0000 Subject: [PATCH 52/55] Lower tolerance for framerate difference. --- openpype/hosts/maya/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 4c8b11ecd3..b920428b20 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3422,7 +3422,7 @@ def convert_to_maya_fps(fps): min_difference = min(differences) min_index = differences.index(min_difference) supported_framerate = float_framerates[min_index] - if round(min_difference) != 0: + if min_difference > 0.1: raise ValueError( "Framerate \"{}\" strays too far from any supported framerate" " in Maya. Closest supported framerate is \"{}\"".format( From 8a5831c6fe6e4bf434d45af457da013df4def88a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 17:11:00 +0100 Subject: [PATCH 53/55] check for 'pyside6' when filling kwargs for file dialog --- openpype/tools/workfiles/files_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/workfiles/files_widget.py b/openpype/tools/workfiles/files_widget.py index 765d32b3d5..18be746d49 100644 --- a/openpype/tools/workfiles/files_widget.py +++ b/openpype/tools/workfiles/files_widget.py @@ -621,7 +621,7 @@ class FilesWidget(QtWidgets.QWidget): "caption": "Work Files", "filter": ext_filter } - if qtpy.API in ("pyside", "pyside2"): + if qtpy.API in ("pyside", "pyside2", "pyside6"): kwargs["dir"] = self._workfiles_root else: kwargs["directory"] = self._workfiles_root From e246afe55b2ecee3f726b54c27b85a5a45f3dcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 17 Feb 2023 15:07:24 +0100 Subject: [PATCH 54/55] removing typo --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 485ae7f4ee..514ffb62c0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ OpenPype [![documentation](https://github.com/pypeclub/pype/actions/workflows/documentation.yml/badge.svg)](https://github.com/pypeclub/pype/actions/workflows/documentation.yml) ![GitHub VFX Platform](https://img.shields.io/badge/vfx%20platform-2022-lightgrey?labelColor=303846) -this Introduction ------------ From 8afd618a0d2b2abbe6441d6d9c3d5c57170424ac Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 17 Feb 2023 15:38:43 +0100 Subject: [PATCH 55/55] updating workflows --- .../workflows/miletone_release_trigger.yml | 47 ++++++++++++ .github/workflows/nightly_merge.yml | 29 ------- .github/workflows/prerelease.yml | 67 ---------------- .github/workflows/release.yml | 76 ------------------- 4 files changed, 47 insertions(+), 172 deletions(-) create mode 100644 .github/workflows/miletone_release_trigger.yml delete mode 100644 .github/workflows/nightly_merge.yml delete mode 100644 .github/workflows/prerelease.yml delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/miletone_release_trigger.yml b/.github/workflows/miletone_release_trigger.yml new file mode 100644 index 0000000000..b5b8aab1dc --- /dev/null +++ b/.github/workflows/miletone_release_trigger.yml @@ -0,0 +1,47 @@ +name: Milestone Release [trigger] + +on: + workflow_dispatch: + inputs: + milestone: + required: true + release-type: + type: choice + description: What release should be created + options: + - release + - pre-release + milestone: + types: closed + + +jobs: + milestone-title: + runs-on: ubuntu-latest + outputs: + milestone: ${{ steps.milestoneTitle.outputs.value }} + steps: + - name: Switch input milestone + uses: haya14busa/action-cond@v1 + id: milestoneTitle + with: + cond: ${{ inputs.milestone == '' }} + if_true: ${{ github.event.milestone.title }} + if_false: ${{ inputs.milestone }} + - name: Print resulted milestone + run: | + echo "${{ steps.milestoneTitle.outputs.value }}" + + call-ci-tools-milestone-release: + needs: milestone-title + uses: ynput/ci-tools/.github/workflows/milestone_release_ref.yml@main + with: + milestone: ${{ needs.milestone-title.outputs.milestone }} + repo-owner: ${{ github.event.repository.owner.login }} + repo-name: ${{ github.event.repository.name }} + version-py-path: "./openpype/version.py" + pyproject-path: "./pyproject.toml" + secrets: + token: ${{ secrets.YNPUT_BOT_TOKEN }} + user_email: ${{ secrets.CI_EMAIL }} + user_name: ${{ secrets.CI_USER }} diff --git a/.github/workflows/nightly_merge.yml b/.github/workflows/nightly_merge.yml deleted file mode 100644 index 1776d7a464..0000000000 --- a/.github/workflows/nightly_merge.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Dev -> Main - -on: - schedule: - - cron: '21 3 * * 3,6' - workflow_dispatch: - -jobs: - develop-to-main: - - runs-on: ubuntu-latest - - steps: - - name: 🚛 Checkout Code - uses: actions/checkout@v2 - - - name: 🔨 Merge develop to main - uses: everlytic/branch-merge@1.1.0 - with: - github_token: ${{ secrets.YNPUT_BOT_TOKEN }} - source_ref: 'develop' - target_branch: 'main' - commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' - - - name: Invoke pre-release workflow - uses: benc-uk/workflow-dispatch@v1 - with: - workflow: Nightly Prerelease - token: ${{ secrets.YNPUT_BOT_TOKEN }} diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml deleted file mode 100644 index 571b0339e1..0000000000 --- a/.github/workflows/prerelease.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Nightly Prerelease - -on: - workflow_dispatch: - - -jobs: - create_nightly: - runs-on: ubuntu-latest - - steps: - - name: 🚛 Checkout Code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - - name: Install Python requirements - run: pip install gitpython semver PyGithub - - - name: 🔎 Determine next version type - id: version_type - run: | - TYPE=$(python ./tools/ci_tools.py --bump --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) - echo "type=${TYPE}" >> $GITHUB_OUTPUT - - - name: 💉 Inject new version into files - id: version - if: steps.version_type.outputs.type != 'skip' - run: | - NEW_VERSION_TAG=$(python ./tools/ci_tools.py --nightly --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) - echo "next_tag=${NEW_VERSION_TAG}" >> $GITHUB_OUTPUT - - - name: 💾 Commit and Tag - id: git_commit - if: steps.version_type.outputs.type != 'skip' - run: | - git config user.email ${{ secrets.CI_EMAIL }} - git config user.name ${{ secrets.CI_USER }} - git checkout main - git pull - git add . - git commit -m "[Automated] Bump version" - tag_name="CI/${{ steps.version.outputs.next_tag }}" - echo $tag_name - git tag -a $tag_name -m "nightly build" - - - name: Push to protected main branch - uses: CasperWA/push-protected@v2.10.0 - with: - token: ${{ secrets.YNPUT_BOT_TOKEN }} - branch: main - tags: true - unprotect_reviews: true - - - name: 🔨 Merge main back to develop - uses: everlytic/branch-merge@1.1.0 - if: steps.version_type.outputs.type != 'skip' - with: - github_token: ${{ secrets.YNPUT_BOT_TOKEN }} - source_ref: 'main' - target_branch: 'develop' - commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 0b4c8af2c7..0000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Stable Release - -on: - release: - types: - - prereleased - -jobs: - create_release: - runs-on: ubuntu-latest - if: github.actor != 'pypebot' - - steps: - - name: 🚛 Checkout Code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - name: Install Python requirements - run: pip install gitpython semver PyGithub - - - name: 💉 Inject new version into files - id: version - run: | - NEW_VERSION=$(python ./tools/ci_tools.py --finalize ${GITHUB_REF#refs/*/}) - LAST_VERSION=$(python ./tools/ci_tools.py --lastversion release) - - echo "current_version=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - echo "last_release=${LAST_VERSION}" >> $GITHUB_OUTPUT - echo "release_tag=${NEW_VERSION}" >> $GITHUB_OUTPUT - - - name: 💾 Commit and Tag - id: git_commit - if: steps.version.outputs.release_tag != 'skip' - run: | - git config user.email ${{ secrets.CI_EMAIL }} - git config user.name ${{ secrets.CI_USER }} - git add . - git commit -m "[Automated] Release" - tag_name="${{ steps.version.outputs.release_tag }}" - git tag -a $tag_name -m "stable release" - - - name: 🔏 Push to protected main branch - if: steps.version.outputs.release_tag != 'skip' - uses: CasperWA/push-protected@v2.10.0 - with: - token: ${{ secrets.YNPUT_BOT_TOKEN }} - branch: main - tags: true - unprotect_reviews: true - - - name: 🚀 Github Release - if: steps.version.outputs.release_tag != 'skip' - uses: ncipollo/release-action@v1 - with: - tag: ${{ steps.version.outputs.release_tag }} - token: ${{ secrets.YNPUT_BOT_TOKEN }} - - - name: ☠ Delete Pre-release - if: steps.version.outputs.release_tag != 'skip' - uses: cb80/delrel@latest - with: - tag: "${{ steps.version.outputs.current_version }}" - - - name: 🔁 Merge main back to develop - if: steps.version.outputs.release_tag != 'skip' - uses: everlytic/branch-merge@1.1.0 - with: - github_token: ${{ secrets.YNPUT_BOT_TOKEN }} - source_ref: 'main' - target_branch: 'develop' - commit_message_template: '[Automated] Merged release {source_ref} into {target_branch}'