From 6cf511c76727e5509b538e98eb4f7c391c17c74c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:14:05 +0100 Subject: [PATCH 1/8] EnumDef has explicit definitions of items --- openpype/lib/attribute_definitions.py | 72 ++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index 0df7b16e64..a688962b5a 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -3,6 +3,7 @@ import re import collections import uuid import json +import copy from abc import ABCMeta, abstractmethod, abstractproperty import six @@ -433,38 +434,83 @@ class EnumDef(AbtractAttrDef): " defined values on initialization." ).format(self.__class__.__name__)) - items = collections.OrderedDict(items) - if default not in items: - for _key in items.keys(): - default = _key + items = self.prepare_enum_items(items) + item_values = [item["value"] for item in items] + if default not in item_values: + for value in item_values: + default = value break super(EnumDef, self).__init__(key, default=default, **kwargs) self.items = items + self._item_values = set(item_values) def __eq__(self, other): if not super(EnumDef, self).__eq__(other): return False - if set(self.items.keys()) != set(other.items.keys()): - return False - - for key, label in self.items.items(): - if other.items[key] != label: - return False - return True + return self.items == other.items def convert_value(self, value): - if value in self.items: + if value in self._item_values: return value return self.default def serialize(self): data = super(TextDef, self).serialize() - data["items"] = list(self.items) + data["items"] = copy.deepcopy(self.items) return data + @staticmethod + def prepare_enum_items(items): + """Convert items to unified structure. + + Output is a list where each item is dictionary with 'value' + and 'label'. + + ```python + # Example output + [ + {"label": "Option 1", "value": 1}, + {"label": "Option 2", "value": 2}, + {"label": "Option 3", "value": 3} + ] + ``` + + Args: + items (Union[Dict[str, Any], List[Any], List[Dict[str, Any]]): The + items to convert. + + Returns: + List[Dict[str, Any]]: Unified structure of items. + """ + + output = [] + if isinstance(items, dict): + for key, value in items.items(): + output.append({"label": key, "value": value}) + + elif isinstance(items, (tuple, list, set)): + for item in items: + if isinstance(item, dict): + # Test if value is available + item["value"] + if "label" not in item: + item["label"] = str(item["value"]) + elif isinstance(item, (list, tuple)) and len(item) == 2: + value, label = item + item = {"label": label, "value": value} + else: + item = {"label": str(item), "value": item} + output.append(item) + + else: + raise TypeError( + "Unknown type for enum items '{}'".format(type(items)) + ) + + return output class BoolDef(AbtractAttrDef): """Boolean representation. From 9772a6dcc54f783c9cafcba5c7ba0a6797ad7576 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:14:33 +0100 Subject: [PATCH 2/8] enum widget is using attr def items correctly --- openpype/tools/attribute_defs/widgets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/tools/attribute_defs/widgets.py b/openpype/tools/attribute_defs/widgets.py index 1ffb3d3799..353a424b00 100644 --- a/openpype/tools/attribute_defs/widgets.py +++ b/openpype/tools/attribute_defs/widgets.py @@ -401,9 +401,8 @@ class EnumAttrWidget(_BaseAttrDefWidget): if self.attr_def.tooltip: input_widget.setToolTip(self.attr_def.tooltip) - items = self.attr_def.items - for key, label in items.items(): - input_widget.addItem(label, key) + for item in self.attr_def.items: + input_widget.addItem(item["label"], item["value"]) idx = input_widget.findData(self.attr_def.default) if idx >= 0: From 15fd19337a18f48184ec0581e0bfcbdea61c6632 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:15:25 +0100 Subject: [PATCH 3/8] fix usages of 'EnumDef' in codebase --- .../plugins/create/create_editorial.py | 16 ++++++++-------- .../workfile/workfile_template_builder.py | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 28a115629e..98a0b8320a 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -33,14 +33,14 @@ from openpype.lib import ( CLIP_ATTR_DEFS = [ EnumDef( "fps", - items={ - "from_selection": "From selection", - 23.997: "23.976", - 24: "24", - 25: "25", - 29.97: "29.97", - 30: "30" - }, + items=[ + {"value": "from_selection", "label": "From selection"}, + {"value": 23.997, "label" : "23.976"}, + {"value": 24, "label": "24"}, + {"value": 25, "label": "25"}, + {"value": 29.97, "label": "29.97"}, + {"value": 30, "label": "30"} + ], label="FPS" ), NumberDef( diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index e3821bb4d7..c632454366 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -1147,11 +1147,11 @@ class PlaceholderLoadMixin(object): loaders_by_name = self.builder.get_loaders_by_name() loader_items = [ - (loader_name, loader.label or loader_name) + {"value": loader_name, "label": loader.label or loader_name} for loader_name, loader in loaders_by_name.items() ] - loader_items = list(sorted(loader_items, key=lambda i: i[1])) + loader_items = list(sorted(loader_items, key=lambda i: i["label"])) options = options or {} return [ attribute_definitions.UISeparatorDef(), @@ -1163,9 +1163,9 @@ class PlaceholderLoadMixin(object): label="Asset Builder Type", default=options.get("builder_type"), items=[ - ("context_asset", "Current asset"), - ("linked_asset", "Linked assets"), - ("all_assets", "All assets") + {"label": "Current asset", "value": "context_asset"}, + {"label": "Linked assets", "value": "linked_asset"}, + {"label": "All assets", "value": "all_assets"}, ], tooltip=( "Asset Builder Type\n" From ead387c5ff50b217da2603b45e4d9bb71767aaa6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:24:29 +0100 Subject: [PATCH 4/8] added more validations --- openpype/lib/attribute_definitions.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index a688962b5a..e9597f41fb 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -498,8 +498,18 @@ class EnumDef(AbtractAttrDef): item["value"] if "label" not in item: item["label"] = str(item["value"]) - elif isinstance(item, (list, tuple)) and len(item) == 2: - value, label = item + elif isinstance(item, (list, tuple)): + if len(item) == 2: + value, label = item + elif len(item) == 1: + value = item[0] + label = str(value) + else: + raise ValueError(( + "Invalid items count {}." + " Expected 1 or 2. Value: {}" + ).format(len(item), str(item))) + item = {"label": label, "value": value} else: item = {"label": str(item), "value": item} From 15ee2a79bcd69cd00ec81c1f1a5d29349e5f710e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:25:59 +0100 Subject: [PATCH 5/8] keep backwards compatibility --- openpype/lib/attribute_definitions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index e9597f41fb..f6c34cebf3 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -488,8 +488,8 @@ class EnumDef(AbtractAttrDef): output = [] if isinstance(items, dict): - for key, value in items.items(): - output.append({"label": key, "value": value}) + for value, label in items.items(): + output.append({"label": label, "value": value}) elif isinstance(items, (tuple, list, set)): for item in items: From 51a529a1d401faaa00fcde344c4274b34ec08c3a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 18:54:02 +0100 Subject: [PATCH 6/8] fix formatting --- openpype/hosts/traypublisher/plugins/create/create_editorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 98a0b8320a..2a9c92bca7 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -35,7 +35,7 @@ CLIP_ATTR_DEFS = [ "fps", items=[ {"value": "from_selection", "label": "From selection"}, - {"value": 23.997, "label" : "23.976"}, + {"value": 23.997, "label": "23.976"}, {"value": 24, "label": "24"}, {"value": 25, "label": "25"}, {"value": 29.97, "label": "29.97"}, From 397acd41642b010d128c016a851f6ca1e3c99da6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 13:55:57 +0100 Subject: [PATCH 7/8] better validation of "value" key --- openpype/lib/attribute_definitions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index f6c34cebf3..bcb2b4a39c 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -419,9 +419,8 @@ class EnumDef(AbtractAttrDef): """Enumeration of single item from items. Args: - items: Items definition that can be coverted to - `collections.OrderedDict`. Dictionary represent {value: label} - relation. + items: Items definition that can be coverted using + 'prepare_enum_items'. default: Default value. Must be one key(value) from passed items. """ @@ -495,7 +494,9 @@ class EnumDef(AbtractAttrDef): for item in items: if isinstance(item, dict): # Test if value is available - item["value"] + if "value" not in item: + raise KeyError("Item does not contain 'value' key.") + if "label" not in item: item["label"] = str(item["value"]) elif isinstance(item, (list, tuple)): From 9741104d09bb8338dbfd2c7422ebbe5ae733a710 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 13:56:33 +0100 Subject: [PATCH 8/8] change comment --- openpype/lib/attribute_definitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index bcb2b4a39c..04db0edc64 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -493,7 +493,7 @@ class EnumDef(AbtractAttrDef): elif isinstance(items, (tuple, list, set)): for item in items: if isinstance(item, dict): - # Test if value is available + # Validate if 'value' is available if "value" not in item: raise KeyError("Item does not contain 'value' key.")