From 15bb321b1f25cac721a10fc357f612c5d5d9ad46 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 15:44:17 +0200 Subject: [PATCH 01/22] system widget can store environment fields --- pype/tools/settings/settings/widgets/base.py | 5 +++++ pype/tools/settings/settings/widgets/item_types.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 243a8448e5..9cff07ea90 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -47,6 +47,7 @@ class SystemWidget(QtWidgets.QWidget): self._ignore_value_changes = False self.input_fields = [] + self.environ_fields = [] scroll_widget = QtWidgets.QScrollArea(self) scroll_widget.setObjectName("GroupWidget") @@ -130,10 +131,14 @@ class SystemWidget(QtWidgets.QWidget): for input_field in self.input_fields: input_field.hierarchical_style_update() + def add_environ_field(self, input_field): + self.environ_fields.append(input_field) + def reset(self): reset_default_settings() self.input_fields.clear() + self.environ_fields.clear() while self.content_layout.count() != 0: widget = self.content_layout.itemAt(0).widget() self.content_layout.removeWidget(widget) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 4124c32ba8..9c55f5dc05 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -140,6 +140,9 @@ class SettingObject: """ return self._has_studio_override or self._parent.has_studio_override + def add_environ_field(self, input_field): + self._parent.add_environ_field(input_field) + @property def as_widget(self): """Item is used as widget in parent item. From 226ddab164af10a6c3447eca7dcf912a617e1748 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 16:15:38 +0200 Subject: [PATCH 02/22] preparation for storing to separated environments settings --- .../settings/settings/widgets/item_types.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 9c55f5dc05..d1c29b6ae1 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -24,6 +24,8 @@ class SettingObject: # Will allow to show actions for the item type (disabled for proxies) else # item is skipped and try to trigger actions on it's parent. allow_actions = True + # If item can store environment values + allow_to_environment = False # All item types must have implemented Qt signal which is emitted when # it's or it's children value has changed, value_changed = None @@ -49,6 +51,9 @@ class SettingObject: self._as_widget = False self._is_group = False + # If value should be stored to environments + self._env_group_key = None + self._any_parent_as_widget = None self._any_parent_is_group = None @@ -84,6 +89,20 @@ class SettingObject: self._is_group = input_data.get("is_group", False) # TODO not implemented yet self._is_nullable = input_data.get("is_nullable", False) + self._env_group_key = input_data.get("env_group_key") + + if self.is_environ: + if not self.allow_to_environment: + raise TypeError(( + "Item {} does not allow to store environment values" + ).format(input_data["type"])) + + if self.as_widget: + raise TypeError(( + "Item is used as widget and" + " marked to store environments at the same time." + )) + self.add_environ_field(self) any_parent_as_widget = parent.as_widget if not any_parent_as_widget: @@ -140,9 +159,25 @@ class SettingObject: """ return self._has_studio_override or self._parent.has_studio_override + @property + def is_environ(self): + return self._env_group_key is not None + + @property + def env_group_key(self): + return self._env_group_key + def add_environ_field(self, input_field): self._parent.add_environ_field(input_field) + @property + def has_only_environ_children(self): + raise NotImplementedError( + "{} does not have implemented `has_only_environ_children`".format( + self + ) + ) + @property def as_widget(self): """Item is used as widget in parent item. @@ -270,8 +305,17 @@ class SettingObject: def config_value(self): """Output for saving changes or overrides.""" + if self.has_only_environ_children: + return {} return {self.key: self.item_value()} + def environment_value(self): + raise NotImplementedError( + "{} Method `set_studio_default` not implemented!".format( + repr(self) + ) + ) + @classmethod def style_state( cls, has_studio_override, is_invalid, is_overriden, is_modified @@ -667,6 +711,10 @@ class InputObject(SettingObject): self.value_changed.emit(self) + @property + def has_only_environ_children(self): + return self.is_environ + def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -882,6 +930,7 @@ class NumberWidget(QtWidgets.QWidget, InputObject): class TextWidget(QtWidgets.QWidget, InputObject): default_input_value = "" value_changed = QtCore.Signal(object) + # allow_to_environment = True def __init__( self, input_data, parent, @@ -985,6 +1034,7 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): class EnumeratorWidget(QtWidgets.QWidget, InputObject): default_input_value = True value_changed = QtCore.Signal(object) + # allow_to_environment = True def __init__( self, input_data, parent, @@ -1136,6 +1186,7 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): class RawJsonWidget(QtWidgets.QWidget, InputObject): default_input_value = "{}" value_changed = QtCore.Signal(object) + allow_to_environment = True def __init__( self, input_data, parent, @@ -1182,6 +1233,12 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_invalid = self.input_field.has_invalid_value() return super(RawJsonWidget, self)._on_value_change(*args, **kwargs) + def environment_value(self): + output = {} + for key, value in self.item_value().items(): + output[key.upper()] = value + return output + def item_value(self): if self.is_invalid: return NOT_SET @@ -1372,6 +1429,7 @@ class ListItem(QtWidgets.QWidget, SettingObject): class ListWidget(QtWidgets.QWidget, InputObject): default_input_value = [] value_changed = QtCore.Signal(object) + # allow_to_environment = True def __init__( self, input_data, parent, @@ -1944,6 +2002,7 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) expand_in_grid = True + # allow_to_environment = True def __init__( self, input_data, parent, @@ -2209,6 +2268,8 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.input_fields = [] + self._has_only_environ_children = None + self.checkbox_widget = None self.checkbox_key = input_data.get("checkbox_key") @@ -2532,6 +2593,19 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return True return False + @property + def has_only_environ_children(self): + if self._has_only_environ_children is None: + has_only_environ_children = True + if not self.is_environ: + for input_field in self.input_fields: + if not input_field.has_only_environ_children: + has_only_environ_children = False + break + + self._has_only_environ_children = has_only_environ_children + return self._has_only_environ_children + def get_invalid(self): output = [] for input_field in self.input_fields: @@ -2600,6 +2674,8 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) + self._has_only_environ_children = None + if self._is_group: raise TypeError("DictInvisible can't be marked as group input.") @@ -2660,6 +2736,19 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): return True return False + @property + def has_only_environ_children(self): + if self._has_only_environ_children is None: + has_only_environ_children = True + if not self.is_environ: + for input_field in self.input_fields: + if not input_field.has_only_environ_children: + has_only_environ_children = False + break + + self._has_only_environ_children = has_only_environ_children + return self._has_only_environ_children + @property def child_modified(self): for input_field in self.input_fields: @@ -2840,6 +2929,7 @@ class PathWidget(QtWidgets.QWidget, SettingObject): "darwin": "MacOS", "linux": "Linux" } + # allow_to_environment = True def __init__( self, input_data, parent, @@ -3136,6 +3226,10 @@ class PathWidget(QtWidgets.QWidget, SettingObject): def set_as_overriden(self): self._is_overriden = True + @property + def has_only_environ_children(self): + return self.is_environ + @property def child_has_studio_override(self): return self.has_studio_override @@ -3200,6 +3294,8 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) + self._has_only_environ_children = None + self._as_widget = False self._is_group = False @@ -3307,6 +3403,19 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): return True return False + @property + def has_only_environ_children(self): + if self._has_only_environ_children is None: + has_only_environ_children = True + if not self.is_environ: + for input_field in self.input_fields: + if not input_field.has_only_environ_children: + has_only_environ_children = False + break + + self._has_only_environ_children = has_only_environ_children + return self._has_only_environ_children + @property def child_modified(self): for input_field in self.input_fields: From 227755669f444f903e6cf73d50d3944250c617e2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:57:21 +0200 Subject: [PATCH 03/22] METATADATA_KEY is object not type --- pype/tools/settings/settings/widgets/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index cf2bd7f8af..f9c0532a97 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -11,7 +11,7 @@ class TypeToKlass: NOT_SET = type("NOT_SET", (), {"__bool__": lambda obj: False})() -METADATA_KEY = type("METADATA_KEY", (), {}) +METADATA_KEY = type("METADATA_KEY", (), {})() OVERRIDE_VERSION = 1 CHILD_OFFSET = 15 From 8a8aa0422db964e2ed1162699e1f5ad33badb917 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:57:51 +0200 Subject: [PATCH 04/22] raise KeyError exception not just create --- pype/tools/settings/settings/widgets/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index f9c0532a97..88e4015198 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -30,7 +30,7 @@ def convert_gui_data_to_overrides(data, first=True): if key == "groups": output[OVERRIDEN_KEY] = value else: - KeyError("Unknown metadata key \"{}\"".format(key)) + raise KeyError("Unknown metadata key \"{}\"".format(key)) for key, value in data.items(): output[key] = convert_gui_data_to_overrides(value, False) From 9afcdd3546b79ad25c81b223f94410fb37250ddc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:59:04 +0200 Subject: [PATCH 05/22] renamed variable OVERRIDEN_KEY to M_OVERRIDEN_KEY --- pype/settings/lib.py | 6 +++--- pype/tools/settings/settings/widgets/lib.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 848bdeea92..f8b483cae9 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -6,7 +6,7 @@ import copy log = logging.getLogger(__name__) # Metadata keys for work with studio and project overrides -OVERRIDEN_KEY = "__overriden_keys__" +M_OVERRIDEN_KEY = "__overriden_keys__" # NOTE key popping not implemented yet POP_KEY = "__pop_key__" @@ -223,8 +223,8 @@ def project_anatomy_overrides(project_name): def merge_overrides(global_dict, override_dict): - if OVERRIDEN_KEY in override_dict: - overriden_keys = set(override_dict.pop(OVERRIDEN_KEY)) + if M_OVERRIDEN_KEY in override_dict: + overriden_keys = set(override_dict.pop(M_OVERRIDEN_KEY)) else: overriden_keys = set() diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 88e4015198..479653e4d4 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -1,7 +1,7 @@ import os import json import copy -from pype.settings.lib import OVERRIDEN_KEY +from pype.settings.lib import M_OVERRIDEN_KEY, M_ENVIRONMENT_KEY from queue import Queue @@ -28,7 +28,7 @@ def convert_gui_data_to_overrides(data, first=True): metadata = data.pop(METADATA_KEY) for key, value in metadata.items(): if key == "groups": - output[OVERRIDEN_KEY] = value + output[M_OVERRIDEN_KEY] = value else: raise KeyError("Unknown metadata key \"{}\"".format(key)) @@ -42,8 +42,8 @@ def convert_overrides_to_gui_data(data, first=True): return data output = {} - if OVERRIDEN_KEY in data: - groups = data.pop(OVERRIDEN_KEY) + if M_OVERRIDEN_KEY in data: + groups = data.pop(M_OVERRIDEN_KEY) if METADATA_KEY not in output: output[METADATA_KEY] = {} output[METADATA_KEY]["groups"] = groups From 4903c09f40a870c802f84f0c070d70c1e8d7052f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:59:20 +0200 Subject: [PATCH 06/22] variable POP_KEY renamed to M_POP_KEY --- pype/settings/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index f8b483cae9..4faf757198 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) # Metadata keys for work with studio and project overrides M_OVERRIDEN_KEY = "__overriden_keys__" # NOTE key popping not implemented yet -POP_KEY = "__pop_key__" +M_POP_KEY = "__pop_key__" # Folder where studio overrides are stored STUDIO_OVERRIDES_PATH = os.environ["PYPE_PROJECT_CONFIGS"] @@ -229,7 +229,7 @@ def merge_overrides(global_dict, override_dict): overriden_keys = set() for key, value in override_dict.items(): - if value == POP_KEY: + if value == M_POP_KEY: global_dict.pop(key) elif ( From ec445fcc2d28ea775c63227b265c3c03127aeabc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 17:59:32 +0200 Subject: [PATCH 07/22] added new M_ENVIRONMENT_KEY for storing environments --- pype/settings/lib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 4faf757198..b01c80038f 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -7,6 +7,8 @@ log = logging.getLogger(__name__) # Metadata keys for work with studio and project overrides M_OVERRIDEN_KEY = "__overriden_keys__" +# Metadata key for storing information about environments +M_ENVIRONMENT_KEY = "__environment_keys__" # NOTE key popping not implemented yet M_POP_KEY = "__pop_key__" From e6749a53f7f0285ab2b5ec767d9c6b1f463ec715 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:00:11 +0200 Subject: [PATCH 08/22] added 2 functions for converting gui data for storing and oposite --- pype/tools/settings/settings/widgets/lib.py | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 479653e4d4..e7ab00d7bd 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -16,6 +16,44 @@ OVERRIDE_VERSION = 1 CHILD_OFFSET = 15 +def convert_gui_data_with_metadata(data, ignored_keys=None): + if not data or not isinstance(data, dict): + return data + + if ignored_keys is None: + ignored_keys = tuple() + + output = {} + if METADATA_KEY in data: + metadata = data.pop(METADATA_KEY) + for key, value in metadata.items(): + if key in ignored_keys or key == "groups": + continue + + if key == "environments": + output[M_ENVIRONMENT_KEY] = value + else: + raise KeyError("Unknown metadata key \"{}\"".format(key)) + + for key, value in data.items(): + output[key] = convert_gui_data_with_metadata(value, ignored_keys) + return output + + +def convert_data_to_gui_data(data, first=True): + if not data or not isinstance(data, dict): + return data + + output = {} + if M_ENVIRONMENT_KEY in data: + data.pop(M_ENVIRONMENT_KEY) + + for key, value in data.items(): + output[key] = convert_data_to_gui_data(value, False) + + return output + + def convert_gui_data_to_overrides(data, first=True): if not data or not isinstance(data, dict): return data From 116c9469c8394ea55b03b2e1d56755c00bad1963 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:01:04 +0200 Subject: [PATCH 09/22] fix not implemented message --- pype/tools/settings/settings/widgets/item_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index d1c29b6ae1..bad1ce08fe 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -311,7 +311,7 @@ class SettingObject: def environment_value(self): raise NotImplementedError( - "{} Method `set_studio_default` not implemented!".format( + "{} Method `environment_value` not implemented!".format( repr(self) ) ) From 08554af5fdea03b76bf2a02bdb239848231f70bb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:01:38 +0200 Subject: [PATCH 10/22] bases use converting in and out data --- pype/tools/settings/settings/widgets/base.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 9cff07ea90..7cbe7c2f6f 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -219,7 +219,7 @@ class SystemWidget(QtWidgets.QWidget): all_values = _all_values # Skip first key - all_values = all_values["system"] + all_values = lib.convert_gui_data_with_metadata(all_values["system"]) prject_defaults_dir = os.path.join( DEFAULTS_DIR, SYSTEM_SETTINGS_KEY @@ -251,16 +251,19 @@ class SystemWidget(QtWidgets.QWidget): def _update_values(self): self.ignore_value_changes = True - default_values = { + default_values = lib.convert_data_to_gui_data({ "system": default_settings()[SYSTEM_SETTINGS_KEY] - } + }) for input_field in self.input_fields: input_field.update_default_values(default_values) if self._hide_studio_overrides: system_values = lib.NOT_SET else: - system_values = {"system": studio_system_settings()} + system_values = lib.convert_overrides_to_gui_data( + {"system": studio_system_settings()} + ) + for input_field in self.input_fields: input_field.update_studio_values(system_values) @@ -735,17 +738,20 @@ class ProjectWidget(QtWidgets.QWidget): def _update_values(self): self.ignore_value_changes = True - default_values = {"project": default_settings()} + default_values = default_values = lib.convert_data_to_gui_data( + {"project": default_settings()} + ) for input_field in self.input_fields: input_field.update_default_values(default_values) if self._hide_studio_overrides: studio_values = lib.NOT_SET else: - studio_values = {"project": { + studio_values = lib.convert_overrides_to_gui_data({"project": { PROJECT_SETTINGS_KEY: studio_project_settings(), PROJECT_ANATOMY_KEY: studio_project_anatomy() - }} + }}) + for input_field in self.input_fields: input_field.update_studio_values(studio_values) From e804c47214fb451f02fd988c1a5d0c74f59d17e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:02:05 +0200 Subject: [PATCH 11/22] converting functions for overrides use new implemented conversions --- pype/tools/settings/settings/widgets/lib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index e7ab00d7bd..31e4401ab0 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -61,6 +61,7 @@ def convert_gui_data_to_overrides(data, first=True): output = {} if first: output["__override_version__"] = OVERRIDE_VERSION + data = convert_gui_data_with_metadata(data, ("environments",)) if METADATA_KEY in data: metadata = data.pop(METADATA_KEY) @@ -79,6 +80,9 @@ def convert_overrides_to_gui_data(data, first=True): if not data or not isinstance(data, dict): return data + if first: + data = convert_data_to_gui_data(data) + output = {} if M_OVERRIDEN_KEY in data: groups = data.pop(M_OVERRIDEN_KEY) From d33de6856674304ba6008690c2906e7cf13e4b06 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:02:48 +0200 Subject: [PATCH 12/22] removed `has_only_environ_children` --- .../settings/settings/widgets/item_types.py | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index bad1ce08fe..76b8504685 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -170,14 +170,6 @@ class SettingObject: def add_environ_field(self, input_field): self._parent.add_environ_field(input_field) - @property - def has_only_environ_children(self): - raise NotImplementedError( - "{} does not have implemented `has_only_environ_children`".format( - self - ) - ) - @property def as_widget(self): """Item is used as widget in parent item. @@ -305,8 +297,6 @@ class SettingObject: def config_value(self): """Output for saving changes or overrides.""" - if self.has_only_environ_children: - return {} return {self.key: self.item_value()} def environment_value(self): @@ -711,10 +701,6 @@ class InputObject(SettingObject): self.value_changed.emit(self) - @property - def has_only_environ_children(self): - return self.is_environ - def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -2268,8 +2254,6 @@ class DictWidget(QtWidgets.QWidget, SettingObject): self.input_fields = [] - self._has_only_environ_children = None - self.checkbox_widget = None self.checkbox_key = input_data.get("checkbox_key") @@ -2593,19 +2577,6 @@ class DictWidget(QtWidgets.QWidget, SettingObject): return True return False - @property - def has_only_environ_children(self): - if self._has_only_environ_children is None: - has_only_environ_children = True - if not self.is_environ: - for input_field in self.input_fields: - if not input_field.has_only_environ_children: - has_only_environ_children = False - break - - self._has_only_environ_children = has_only_environ_children - return self._has_only_environ_children - def get_invalid(self): output = [] for input_field in self.input_fields: @@ -2674,8 +2645,6 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) - self._has_only_environ_children = None - if self._is_group: raise TypeError("DictInvisible can't be marked as group input.") @@ -2736,19 +2705,6 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): return True return False - @property - def has_only_environ_children(self): - if self._has_only_environ_children is None: - has_only_environ_children = True - if not self.is_environ: - for input_field in self.input_fields: - if not input_field.has_only_environ_children: - has_only_environ_children = False - break - - self._has_only_environ_children = has_only_environ_children - return self._has_only_environ_children - @property def child_modified(self): for input_field in self.input_fields: @@ -3226,10 +3182,6 @@ class PathWidget(QtWidgets.QWidget, SettingObject): def set_as_overriden(self): self._is_overriden = True - @property - def has_only_environ_children(self): - return self.is_environ - @property def child_has_studio_override(self): return self.has_studio_override @@ -3294,8 +3246,6 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): self.initial_attributes(input_data, parent, as_widget) - self._has_only_environ_children = None - self._as_widget = False self._is_group = False @@ -3403,19 +3353,6 @@ class DictFormWidget(QtWidgets.QWidget, SettingObject): return True return False - @property - def has_only_environ_children(self): - if self._has_only_environ_children is None: - has_only_environ_children = True - if not self.is_environ: - for input_field in self.input_fields: - if not input_field.has_only_environ_children: - has_only_environ_children = False - break - - self._has_only_environ_children = has_only_environ_children - return self._has_only_environ_children - @property def child_modified(self): for input_field in self.input_fields: From 9de4efc7c35fcd91557d865810db2e4585abfabc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:50:10 +0200 Subject: [PATCH 13/22] prepared method for merging metadata under same key --- .../settings/settings/widgets/item_types.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 76b8504685..6b35246db9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -32,6 +32,24 @@ class SettingObject: # Item will expand to full width in grid layout expand_in_grid = False + def merge_metadata(self, current_metadata, new_metadata): + for key, value in new_metadata.items(): + if key not in current_metadata: + current_metadata[key] = value + + elif key == "groups": + current_metadata[key].extend(value) + + elif key == "environments": + for group_key, subvalue in value.items(): + if group_key not in current_metadata[key]: + current_metadata[key][group_key] = [] + current_metadata[key][group_key].extend(subvalue) + + else: + raise KeyError("Unknown metadata key: \"{}\"".format(key)) + return current_metadata + def _set_default_attributes(self): """Create and reset attributes required for all item types. From 2933eab903716e43ea4c44b0d5395dd2dd46ac02 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:51:20 +0200 Subject: [PATCH 14/22] dicitonaries can merge metadata keys --- .../settings/settings/widgets/item_types.py | 106 ++++++++++-------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 6b35246db9..74eab078e1 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -2609,6 +2609,33 @@ class DictWidget(QtWidgets.QWidget, SettingObject): output.update(input_field.config_value()) return output + def _override_values(self, project_overrides): + values = {} + groups = [] + for input_field in self.input_fields: + if project_overrides: + value, is_group = input_field.overrides() + else: + value, is_group = input_field.studio_overrides() + if value is NOT_SET: + continue + + if METADATA_KEY in value and METADATA_KEY in values: + new_metadata = value.pop(METADATA_KEY) + values[METADATA_KEY] = self.merge_metadata( + values[METADATA_KEY], new_metadata + ) + + values.update(value) + if is_group: + groups.extend(value.keys()) + + if groups: + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups + return {self.key: values}, self.is_group + def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -2616,34 +2643,12 @@ class DictWidget(QtWidgets.QWidget, SettingObject): and not self.child_has_studio_override ): return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.studio_overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(False) def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(True) class DictInvisible(QtWidgets.QWidget, SettingObject): @@ -2858,6 +2863,33 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): ) self._was_overriden = bool(self._is_overriden) + def _override_values(self, project_overrides): + values = {} + groups = [] + for input_field in self.input_fields: + if project_overrides: + value, is_group = input_field.overrides() + else: + value, is_group = input_field.studio_overrides() + if value is NOT_SET: + continue + + if METADATA_KEY in value and METADATA_KEY in values: + new_metadata = value.pop(METADATA_KEY) + values[METADATA_KEY] = self.merge_metadata( + values[METADATA_KEY], new_metadata + ) + + values.update(value) + if is_group: + groups.extend(value.keys()) + + if groups: + if METADATA_KEY not in values: + values[METADATA_KEY] = {} + values[METADATA_KEY]["groups"] = groups + return {self.key: values}, self.is_group + def studio_overrides(self): if ( not (self.as_widget or self.any_parent_as_widget) @@ -2865,34 +2897,12 @@ class DictInvisible(QtWidgets.QWidget, SettingObject): and not self.child_has_studio_override ): return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.studio_overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(False) def overrides(self): if not self.is_overriden and not self.child_overriden: return NOT_SET, False - - values = {} - groups = [] - for input_field in self.input_fields: - value, is_group = input_field.overrides() - if value is not NOT_SET: - values.update(value) - if is_group: - groups.extend(value.keys()) - if groups: - values[METADATA_KEY] = {"groups": groups} - return {self.key: values}, self.is_group + return self._override_values(True) class PathWidget(QtWidgets.QWidget, SettingObject): From 65d51d56a80638c5e0a6c79d6a1bbc67dd42093b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 18:51:42 +0200 Subject: [PATCH 15/22] raw-json can store environments --- pype/tools/settings/settings/widgets/item_types.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 74eab078e1..70f0506a2b 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1246,7 +1246,15 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): def item_value(self): if self.is_invalid: return NOT_SET - return self.input_field.json_value() + + value = self.input_field.json_value() + if self.is_environ: + value[METADATA_KEY] = { + "environments": { + self.env_group_key: list(value.keys()) + } + } + return value class ListItem(QtWidgets.QWidget, SettingObject): From 03bbc6e839eb8f71df7dab7d5701a0916bc6b777 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:01:16 +0200 Subject: [PATCH 16/22] raw json can return env value/keys --- .../settings/settings/widgets/item_types.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 70f0506a2b..1adebddff7 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -1237,24 +1237,24 @@ class RawJsonWidget(QtWidgets.QWidget, InputObject): self._is_invalid = self.input_field.has_invalid_value() return super(RawJsonWidget, self)._on_value_change(*args, **kwargs) - def environment_value(self): - output = {} - for key, value in self.item_value().items(): - output[key.upper()] = value - return output - def item_value(self): if self.is_invalid: return NOT_SET value = self.input_field.json_value() - if self.is_environ: - value[METADATA_KEY] = { - "environments": { - self.env_group_key: list(value.keys()) - } + if not self.is_environ: + return value + + output = {} + for key, value in value.items(): + output[key.upper()] = value + + output[METADATA_KEY] = { + "environments": { + self.env_group_key: list(output.keys()) } - return value + } + return output class ListItem(QtWidgets.QWidget, SettingObject): From 484a35ae528dbf6e79610cc343591316098e5fad Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:15:23 +0200 Subject: [PATCH 17/22] modified way how environments are loaded --- pype/settings/lib.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index b01c80038f..7c1adcd8a7 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -113,6 +113,32 @@ def load_json(fpath): return {} +def find_environments(data): + if not data or not isinstance(data, dict): + return + + output = {} + if M_ENVIRONMENT_KEY in data: + metadata = data.pop(M_ENVIRONMENT_KEY) + for env_group_key, env_keys in metadata.items(): + output[env_group_key] = {} + for key in env_keys: + output[env_group_key][key] = data[key] + + for value in data.values(): + result = find_environments(value) + if not result: + continue + + for env_group_key, env_values in result.items(): + if env_group_key not in output: + output[env_group_key] = {} + + for env_key, env_value in env_values.items(): + output[env_group_key][env_key] = env_value + return output + + def subkey_merge(_dict, value, keys): key = keys.pop(0) if not keys: @@ -274,5 +300,5 @@ def project_settings(project_name): def environments(): default_values = default_settings()[ENVIRONMENTS_KEY] - studio_values = studio_system_settings() + studio_values = find_environments(system_settings()) return apply_overrides(default_values, studio_values) From 87df2ea1019505911e908ac862afc3761719af52 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:30:18 +0200 Subject: [PATCH 18/22] added validation for env group keys --- pype/tools/settings/settings/widgets/lib.py | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 31e4401ab0..6d8694dc7a 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -162,6 +162,21 @@ class SchemaDuplicatedKeys(Exception): super(SchemaDuplicatedKeys, self).__init__(msg) +class SchemaDuplicatedEnvGroupKeys(Exception): + def __init__(self, invalid): + items = [] + for key_path, keys in invalid.items(): + joined_keys = ", ".join([ + "\"{}\"".format(key) for key in keys + ]) + items.append("\"{}\" ({})".format(key_path, joined_keys)) + + msg = ( + "Schema items contain duplicated environment group keys. {}" + ).format(" || ".join(items)) + super(SchemaDuplicatedEnvGroupKeys, self).__init__(msg) + + def file_keys_from_schema(schema_data): output = [] item_type = schema_data["type"] @@ -319,10 +334,50 @@ def validate_keys_are_unique(schema_data, keys=None): raise SchemaDuplicatedKeys(invalid) +def validate_environment_groups_uniquenes( + schema_data, env_groups=None, keys=None +): + is_first = False + if env_groups is None: + is_first = True + env_groups = {} + keys = [] + + my_keys = copy.deepcopy(keys) + key = schema_data.get("key") + if key: + my_keys.append(key) + + env_group_key = schema_data.get("env_group_key") + if env_group_key: + if env_group_key not in env_groups: + env_groups[env_group_key] = [] + env_groups[env_group_key].append("/".join(my_keys)) + + children = schema_data.get("children") + if not children: + return + + for child in children: + validate_environment_groups_uniquenes( + child, env_groups, copy.deepcopy(my_keys) + ) + + if is_first: + invalid = {} + for env_group_key, key_paths in env_groups.items(): + if len(key_paths) > 1: + invalid[env_group_key] = key_paths + + if invalid: + raise SchemaDuplicatedEnvGroupKeys(invalid) + + def validate_schema(schema_data): validate_all_has_ending_file(schema_data) validate_is_group_is_unique_in_hierarchy(schema_data) validate_keys_are_unique(schema_data) + validate_environment_groups_uniquenes(schema_data) def gui_schema(subfolder, main_schema_name): From d7492a13c4c828f200b2de74af2ba5a521506a7d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:36:43 +0200 Subject: [PATCH 19/22] added example of usage raw json for environments --- .../gui_schemas/system_schema/1_examples.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json b/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json index dd0f7f20d1..06ce23321a 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/1_examples.json @@ -5,6 +5,18 @@ "is_file": true, "children": [ { + "key": "env_group_test", + "label": "EnvGroup Test", + "type": "dict", + "children": [ + { + "key": "key_to_store_in_system_settings", + "label": "Testing environment group", + "type": "raw-json", + "env_group_key": "test_group" + } + ] + }, { "key": "dict_wrapper", "type": "dict-invisible", "children": [ From fb6061e80be838e113039083b3d00a824fb85a60 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:44:02 +0200 Subject: [PATCH 20/22] changed logic of environment keys loading --- pype/settings/lib.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pype/settings/lib.py b/pype/settings/lib.py index 7c1adcd8a7..96c3829388 100644 --- a/pype/settings/lib.py +++ b/pype/settings/lib.py @@ -299,6 +299,8 @@ def project_settings(project_name): def environments(): - default_values = default_settings()[ENVIRONMENTS_KEY] - studio_values = find_environments(system_settings()) - return apply_overrides(default_values, studio_values) + envs = copy.deepcopy(default_settings()[ENVIRONMENTS_KEY]) + envs_from_system_settings = find_environments(system_settings()) + for env_group_key, values in envs_from_system_settings.items(): + envs[env_group_key] = values + return envs From de6daa65ca5e746dcf6dbc8ed8b90a7426846188 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 19:52:37 +0200 Subject: [PATCH 21/22] cleanup --- pype/tools/settings/settings/widgets/item_types.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index 1adebddff7..1127d611d7 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -934,7 +934,6 @@ class NumberWidget(QtWidgets.QWidget, InputObject): class TextWidget(QtWidgets.QWidget, InputObject): default_input_value = "" value_changed = QtCore.Signal(object) - # allow_to_environment = True def __init__( self, input_data, parent, @@ -1038,7 +1037,6 @@ class PathInputWidget(QtWidgets.QWidget, InputObject): class EnumeratorWidget(QtWidgets.QWidget, InputObject): default_input_value = True value_changed = QtCore.Signal(object) - # allow_to_environment = True def __init__( self, input_data, parent, @@ -1441,7 +1439,6 @@ class ListItem(QtWidgets.QWidget, SettingObject): class ListWidget(QtWidgets.QWidget, InputObject): default_input_value = [] value_changed = QtCore.Signal(object) - # allow_to_environment = True def __init__( self, input_data, parent, @@ -2014,7 +2011,6 @@ class ModifiableDict(QtWidgets.QWidget, InputObject): # TODO this is actually input field (do not care if is group or not) value_changed = QtCore.Signal(object) expand_in_grid = True - # allow_to_environment = True def __init__( self, input_data, parent, @@ -2921,7 +2917,6 @@ class PathWidget(QtWidgets.QWidget, SettingObject): "darwin": "MacOS", "linux": "Linux" } - # allow_to_environment = True def __init__( self, input_data, parent, From 14ce8eb4428fead21aaafa8df55d90e5b6a5d26e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 1 Oct 2020 22:54:54 +0200 Subject: [PATCH 22/22] fix saving of environmentes to studio overrides --- pype/tools/settings/settings/widgets/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/lib.py b/pype/tools/settings/settings/widgets/lib.py index 6d8694dc7a..87f6554612 100644 --- a/pype/tools/settings/settings/widgets/lib.py +++ b/pype/tools/settings/settings/widgets/lib.py @@ -61,7 +61,7 @@ def convert_gui_data_to_overrides(data, first=True): output = {} if first: output["__override_version__"] = OVERRIDE_VERSION - data = convert_gui_data_with_metadata(data, ("environments",)) + data = convert_gui_data_with_metadata(data) if METADATA_KEY in data: metadata = data.pop(METADATA_KEY)