diff --git a/pype/hosts/nuke/api/menu.py b/pype/hosts/nuke/api/menu.py index 9ff1dc251a..3f97cc228a 100644 --- a/pype/hosts/nuke/api/menu.py +++ b/pype/hosts/nuke/api/menu.py @@ -85,6 +85,13 @@ def add_shortcuts_from_presets(): nuke_presets = get_current_project_settings()["nuke"] if nuke_presets.get("menu"): + menu_label_mapping = { + "manage": "Manage...", + "create": "Create...", + "load": "Load...", + "build_workfile": "Build Workfile", + "publish": "Publish..." + } for menu_name, menuitems in nuke_presets.get("menu").items(): menu = menubar.findItem(menu_name) for mitem_name, shortcut in menuitems.items(): @@ -92,7 +99,8 @@ def add_shortcuts_from_presets(): shortcut, mitem_name )) try: - menuitem = menu.findItem(mitem_name) + item_label = menu_label_mapping[mitem_name] + menuitem = menu.findItem(item_label) menuitem.setShortcut(shortcut) except AttributeError as e: log.error(e) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index a03a845770..4b2cab99aa 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -138,20 +138,16 @@ class ApplicationManager: app_group, app_name, host_name, app_data, self ) - tools_definitions = settings["tools"] + tools_definitions = settings["tools"]["tool_groups"] for tool_group_name, tool_group_data in tools_definitions.items(): - enabled = tool_group_data.get("enabled", True) tool_variants = tool_group_data.get("variants") or {} for tool_name, tool_data in tool_variants.items(): - if tool_name in self.tools: + tool = ApplicationTool(tool_name, tool_group_name) + if tool.full_name in self.tools: self.log.warning(( "Duplicated tool name in settings \"{}\"" - ).format(tool_name)) - - _enabled = tool_data.get("enabled", enabled) - self.tools[tool_name] = ApplicationTool( - tool_name, tool_group_name, _enabled - ) + ).format(tool.full_name)) + self.tools[tool.full_name] = tool def launch(self, app_name, **data): """Launch procedure. @@ -196,16 +192,15 @@ class ApplicationTool: Args: tool_name (str): Name of the tool. group_name (str): Name of group which wraps tool. - enabled (bool): Is tool enabled by studio. """ - def __init__(self, tool_name, group_name, enabled): + def __init__(self, tool_name, group_name): self.name = tool_name self.group_name = group_name - self.enabled = enabled - def __bool__(self): - return self.enabled + @property + def full_name(self): + return "/".join((self.group_name, self.name)) class ApplicationExecutable: diff --git a/pype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py b/pype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py index ae040fd630..8ff0cade7b 100644 --- a/pype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py +++ b/pype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py @@ -400,11 +400,10 @@ class CustomAttributes(BaseAction): def tools_attribute(self, event): tools_data = [] - for tool_name, tool in self.app_manager.tools.items(): - if tool.enabled: - tools_data.append({ - tool_name: tool_name - }) + for tool_name in self.app_manager.tools.keys(): + tools_data.append({ + tool_name: tool_name + }) # Make sure there is at least one item if not tools_data: diff --git a/pype/settings/constants.py b/pype/settings/constants.py index ce19ad3f93..f6077e826e 100644 --- a/pype/settings/constants.py +++ b/pype/settings/constants.py @@ -1,3 +1,6 @@ +import re + + # Metadata keys for work with studio and project overrides M_OVERRIDEN_KEY = "__overriden_keys__" # Metadata key for storing information about environments @@ -19,6 +22,10 @@ LOCAL_SETTING_KEY = "local_settings" DEFAULT_PROJECT_KEY = "__default_project__" +KEY_ALLOWED_SYMBOLS = "a-zA-Z0-9-_ " +KEY_REGEX = re.compile(r"^[{}]+$".format(KEY_ALLOWED_SYMBOLS)) + + __all__ = ( "M_OVERRIDEN_KEY", "M_ENVIRONMENT_KEY", @@ -29,5 +36,10 @@ __all__ = ( "SYSTEM_SETTINGS_KEY", "PROJECT_SETTINGS_KEY", "PROJECT_ANATOMY_KEY", - "LOCAL_SETTING_KEY" + "LOCAL_SETTING_KEY", + + "DEFAULT_PROJECT_KEY", + + "KEY_ALLOWED_SYMBOLS", + "KEY_REGEX" ) diff --git a/pype/settings/defaults/project_settings/nuke.json b/pype/settings/defaults/project_settings/nuke.json index f808f9caa5..d727a6ba1e 100644 --- a/pype/settings/defaults/project_settings/nuke.json +++ b/pype/settings/defaults/project_settings/nuke.json @@ -1,11 +1,11 @@ { "menu": { "Pype": { - "Create...": "ctrl+shift+alt+c", - "Publish...": "ctrl+alt+p", - "Load...": "ctrl+alt+l", - "Manage...": "ctrl+alt+m", - "Build Workfile": "ctrl+alt+b" + "create": "ctrl+shift+alt+c", + "publish": "ctrl+alt+p", + "load": "ctrl+alt+l", + "manage": "ctrl+alt+m", + "build_workfile": "ctrl+alt+b" } }, "create": { diff --git a/pype/settings/defaults/system_settings/applications.json b/pype/settings/defaults/system_settings/applications.json index 7ceef3c129..ea910e125d 100644 --- a/pype/settings/defaults/system_settings/applications.json +++ b/pype/settings/defaults/system_settings/applications.json @@ -253,7 +253,7 @@ } }, "variants": { - "nuke_12.2": { + "nuke_12-2": { "enabled": true, "label": "", "variant_label": "12.2", @@ -274,11 +274,11 @@ }, "environment": { "__environment_keys__": { - "nuke_12.2": [] + "nuke_12-2": [] } } }, - "nuke_12.0": { + "nuke_12-0": { "enabled": true, "label": "", "variant_label": "12.0", @@ -299,11 +299,11 @@ }, "environment": { "__environment_keys__": { - "nuke_12.0": [] + "nuke_12-0": [] } } }, - "nuke_11.3": { + "nuke_11-3": { "enabled": true, "label": "", "variant_label": "11.3", @@ -324,11 +324,11 @@ }, "environment": { "__environment_keys__": { - "nuke_11.3": [] + "nuke_11-3": [] } } }, - "nuke_11.2": { + "nuke_11-2": { "enabled": true, "label": "", "variant_label": "11.2", @@ -347,7 +347,7 @@ }, "environment": { "__environment_keys__": { - "nuke_11.2": [] + "nuke_11-2": [] } } } @@ -377,7 +377,7 @@ } }, "variants": { - "nukex_12.2": { + "nukex_12-2": { "enabled": true, "label": "", "variant_label": "12.2", @@ -404,11 +404,11 @@ }, "environment": { "__environment_keys__": { - "nukex_12.2": [] + "nukex_12-2": [] } } }, - "nukex_12.0": { + "nukex_12-0": { "enabled": true, "label": "", "variant_label": "12.0", @@ -435,11 +435,11 @@ }, "environment": { "__environment_keys__": { - "nukex_12.0": [] + "nukex_12-0": [] } } }, - "nukex_11.3": { + "nukex_11-3": { "enabled": true, "label": "", "variant_label": "11.3", @@ -466,11 +466,11 @@ }, "environment": { "__environment_keys__": { - "nukex_11.3": [] + "nukex_11-3": [] } } }, - "nukex_11.2": { + "nukex_11-2": { "enabled": true, "label": "", "variant_label": "11.2", @@ -495,7 +495,7 @@ }, "environment": { "__environment_keys__": { - "nukex_11.2": [] + "nukex_11-2": [] } } } @@ -527,7 +527,7 @@ } }, "variants": { - "nukestudio_12.2": { + "nukestudio_12-2": { "enabled": true, "label": "", "variant_label": "12.2", @@ -554,11 +554,11 @@ }, "environment": { "__environment_keys__": { - "nukestudio_12.2": [] + "nukestudio_12-2": [] } } }, - "nukestudio_12.0": { + "nukestudio_12-0": { "enabled": true, "label": "", "variant_label": "12.0", @@ -585,11 +585,11 @@ }, "environment": { "__environment_keys__": { - "nukestudio_12.0": [] + "nukestudio_12-0": [] } } }, - "nukestudio_11.3": { + "nukestudio_11-3": { "enabled": true, "label": "", "variant_label": "11.3", @@ -616,11 +616,11 @@ }, "environment": { "__environment_keys__": { - "nukestudio_11.3": [] + "nukestudio_11-3": [] } } }, - "nukestudio_11.2": { + "nukestudio_11-2": { "enabled": true, "label": "", "variant_label": "11.2", @@ -643,7 +643,7 @@ }, "environment": { "__environment_keys__": { - "nukestudio_11.2": [] + "nukestudio_11-2": [] } } } @@ -675,7 +675,7 @@ } }, "variants": { - "hiero_12.2": { + "hiero_12-2": { "enabled": true, "label": "", "variant_label": "12.2", @@ -702,11 +702,11 @@ }, "environment": { "__environment_keys__": { - "hiero_12.2": [] + "hiero_12-2": [] } } }, - "hiero_12.0": { + "hiero_12-0": { "enabled": true, "label": "", "variant_label": "12.0", @@ -733,11 +733,11 @@ }, "environment": { "__environment_keys__": { - "hiero_12.0": [] + "hiero_12-0": [] } } }, - "hiero_11.3": { + "hiero_11-3": { "enabled": true, "label": "", "variant_label": "11.3", @@ -764,11 +764,11 @@ }, "environment": { "__environment_keys__": { - "hiero_11.3": [] + "hiero_11-3": [] } } }, - "hiero_11.2": { + "hiero_11-2": { "enabled": true, "label": "", "variant_label": "11.2", @@ -793,7 +793,7 @@ }, "environment": { "__environment_keys__": { - "hiero_11.2": [] + "hiero_11-2": [] } } } @@ -992,7 +992,7 @@ } }, "variants": { - "houdini_18.5": { + "houdini_18-5": { "enabled": true, "label": "", "variant_label": "18.5", @@ -1011,7 +1011,7 @@ }, "environment": { "__environment_keys__": { - "houdini_18.5": [] + "houdini_18-5": [] } } }, @@ -1080,36 +1080,7 @@ } }, "variants": { - "blender_2.90": { - "enabled": true, - "label": "", - "variant_label": "2.90", - "icon": "", - "executables": { - "windows": [ - "C:\\Program Files\\Blender Foundation\\Blender 2.90\\blender.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [ - "--python-use-system-env" - ], - "darwin": [ - "--python-use-system-env" - ], - "linux": [ - "--python-use-system-env" - ] - }, - "environment": { - "__environment_keys__": { - "blender_2.90": [] - } - } - }, - "blender_2.83": { + "blender_2-83": { "enabled": true, "label": "", "variant_label": "2.83", @@ -1134,7 +1105,36 @@ }, "environment": { "__environment_keys__": { - "blender_2.83": [] + "blender_2-83": [] + } + } + }, + "blender_2-90": { + "enabled": true, + "label": "", + "variant_label": "2.90", + "icon": "", + "executables": { + "windows": [ + "C:\\Program Files\\Blender Foundation\\Blender 2.90\\blender.exe" + ], + "darwin": [], + "linux": [] + }, + "arguments": { + "windows": [ + "--python-use-system-env" + ], + "darwin": [ + "--python-use-system-env" + ], + "linux": [ + "--python-use-system-env" + ] + }, + "environment": { + "__environment_keys__": { + "blender_2-90": [] } } } @@ -1216,7 +1216,7 @@ } }, "variants": { - "tvpaint_Animation 11 (64bits)": { + "tvpaint_animation_11-64bits": { "enabled": true, "label": "", "variant_label": "11 (64bits)", @@ -1235,11 +1235,11 @@ }, "environment": { "__environment_keys__": { - "tvpaint_Animation 11 (64bits)": [] + "tvpaint_animation_11-64bits": [] } } }, - "tvpaint_Animation 11 (32bits)": { + "tvpaint_animation_11-32bits": { "enabled": true, "label": "", "variant_label": "11 (32bits)", @@ -1258,7 +1258,7 @@ }, "environment": { "__environment_keys__": { - "tvpaint_Animation 11 (32bits)": [] + "tvpaint_animation_11-32bits": [] } } } @@ -1430,23 +1430,6 @@ "celation_Local": [] } } - }, - "celation_Publish": { - "enabled": true, - "label": "", - "variant_label": "Pulblish", - "icon": "", - "executables": "", - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": { - "__environment_keys__": { - "celation_Publish": [] - } - } } } }, @@ -1468,7 +1451,7 @@ } }, "variants": { - "unreal_4.24": { + "unreal_4-24": { "enabled": true, "label": "", "variant_label": "4.24", @@ -1485,7 +1468,7 @@ }, "environment": { "__environment_keys__": { - "unreal_4.24": [] + "unreal_4-24": [] } } } @@ -1499,7 +1482,7 @@ } }, "variants": { - "python_Python 3.7": { + "python_python_3-7": { "enabled": true, "label": "Python", "variant_label": "3.7", @@ -1516,11 +1499,11 @@ }, "environment": { "__environment_keys__": { - "python_Python 3.7": [] + "python_python_3-7": [] } } }, - "python_Python 2.7": { + "python_python_2-7": { "enabled": true, "label": "Python", "variant_label": "2.7", @@ -1537,11 +1520,11 @@ }, "environment": { "__environment_keys__": { - "python_Python 2.7": [] + "python_python_2-7": [] } } }, - "terminal_Terminal": { + "terminal_terminal": { "enabled": true, "label": "Terminal", "variant_label": "", @@ -1558,7 +1541,7 @@ }, "environment": { "__environment_keys__": { - "terminal_Terminal": [] + "terminal_terminal": [] } } } @@ -1575,7 +1558,7 @@ } }, "variants": { - "djvview_1.1": { + "djvview_1-1": { "enabled": true, "label": "", "variant_label": "1.1", @@ -1592,7 +1575,7 @@ }, "environment": { "__environment_keys__": { - "djvview_1.1": [] + "djvview_1-1": [] } } } diff --git a/pype/settings/defaults/system_settings/tools.json b/pype/settings/defaults/system_settings/tools.json index af5772705c..214bfc95e5 100644 --- a/pype/settings/defaults/system_settings/tools.json +++ b/pype/settings/defaults/system_settings/tools.json @@ -1,78 +1,53 @@ { - "mtoa": { - "enabled": true, - "environment": { - "MTOA": "{PYPE_STUDIO_SOFTWARE}/arnold/mtoa_{MAYA_VERSION}_{MTOA_VERSION}", - "MAYA_RENDER_DESC_PATH": "{MTOA}", - "MAYA_MODULE_PATH": "{MTOA}", - "ARNOLD_PLUGIN_PATH": "{MTOA}/shaders", - "MTOA_EXTENSIONS_PATH": { - "darwin": "{MTOA}/extensions", - "linux": "{MTOA}/extensions", - "windows": "{MTOA}/extensions" + "tool_groups": { + "mtoa": { + "environment": { + "MTOA": "{PYPE_STUDIO_SOFTWARE}/arnold/mtoa_{MAYA_VERSION}_{MTOA_VERSION}", + "MAYA_RENDER_DESC_PATH": "{MTOA}", + "MAYA_MODULE_PATH": "{MTOA}", + "ARNOLD_PLUGIN_PATH": "{MTOA}/shaders", + "MTOA_EXTENSIONS_PATH": { + "darwin": "{MTOA}/extensions", + "linux": "{MTOA}/extensions", + "windows": "{MTOA}/extensions" + }, + "MTOA_EXTENSIONS": { + "darwin": "{MTOA}/extensions", + "linux": "{MTOA}/extensions", + "windows": "{MTOA}/extensions" + }, + "DYLD_LIBRARY_PATH": { + "darwin": "{MTOA}/bin" + }, + "PATH": { + "windows": "{PATH};{MTOA}/bin" + } }, - "MTOA_EXTENSIONS": { - "darwin": "{MTOA}/extensions", - "linux": "{MTOA}/extensions", - "windows": "{MTOA}/extensions" - }, - "DYLD_LIBRARY_PATH": { - "darwin": "{MTOA}/bin" - }, - "PATH": { - "windows": "{PATH};{MTOA}/bin" - }, - "__environment_keys__": { - "mtoa": [ - "MTOA", - "MAYA_RENDER_DESC_PATH", - "MAYA_MODULE_PATH", - "ARNOLD_PLUGIN_PATH", - "MTOA_EXTENSIONS_PATH", - "MTOA_EXTENSIONS", - "DYLD_LIBRARY_PATH", - "PATH" - ] + "variants": { + "3-2": { + "MTOA_VERSION": "3.2" + }, + "3-1": { + "MTOA_VERSION": "3.1" + }, + "__dynamic_keys_labels__": { + "3-2": "3.2", + "3-1": "3.2" + } } }, - "variants": { - "mtoa_3.2": { - "MTOA_VERSION": "3.2", - "__environment_keys__": { - "mtoa_3.2": [ - "MTOA_VERSION" - ] - } - }, - "mtoa_3.1": { - "MTOA_VERSION": "3.1", - "__environment_keys__": { - "mtoa_3.1": [ - "MTOA_VERSION" - ] - } - } + "vray": { + "environment": {}, + "variants": {} + }, + "yeti": { + "environment": {}, + "variants": {} + }, + "__dynamic_keys_labels__": { + "mtoa": "Autodesk Arnold", + "vray": "Chaos Group Vray", + "yeti": "Pergrine Labs Yeti" } - }, - "vray": { - "enabled": true, - "environment": { - "__environment_keys__": { - "vray": [] - } - }, - "variants": {} - }, - "yeti": { - "enabled": true, - "environment": { - "__environment_keys__": { - "yeti": [] - } - }, - "variants": {} - }, - "other": { - "variants": {} } } \ No newline at end of file diff --git a/pype/settings/entities/__init__.py b/pype/settings/entities/__init__.py index 1cb4be62e7..2ff911e7d4 100644 --- a/pype/settings/entities/__init__.py +++ b/pype/settings/entities/__init__.py @@ -58,6 +58,7 @@ from .exceptions import ( DefaultsNotDefined, StudioDefaultsNotDefined, InvalidValueType, + InvalidKeySymbols, SchemaMissingFileInfo, SchemeGroupHierarchyBug, SchemaDuplicatedKeys, @@ -114,6 +115,7 @@ __all__ = ( "DefaultsNotDefined", "StudioDefaultsNotDefined", "InvalidValueType", + "InvalidKeySymbols", "SchemaMissingFileInfo", "SchemeGroupHierarchyBug", "SchemaDuplicatedKeys", diff --git a/pype/settings/entities/dict_immutable_keys_entity.py b/pype/settings/entities/dict_immutable_keys_entity.py index 92a36b7dca..270e635736 100644 --- a/pype/settings/entities/dict_immutable_keys_entity.py +++ b/pype/settings/entities/dict_immutable_keys_entity.py @@ -7,7 +7,8 @@ from .lib import ( ) from pype.settings.constants import ( METADATA_KEYS, - M_OVERRIDEN_KEY + M_OVERRIDEN_KEY, + KEY_REGEX ) from . import ( BaseItemEntity, @@ -17,7 +18,8 @@ from . import ( ) from .exceptions import ( SchemaDuplicatedKeys, - EntitySchemaError + EntitySchemaError, + InvalidKeySymbols ) @@ -88,6 +90,10 @@ class DictImmutableKeysEntity(ItemEntity): else: raise SchemaDuplicatedKeys(self, child_entity.key) + for key in self.keys(): + if not KEY_REGEX.match(key): + raise InvalidKeySymbols(self.path, key) + if self.checkbox_key: checkbox_child = self.non_gui_children.get(self.checkbox_key) if not checkbox_child: diff --git a/pype/settings/entities/dict_mutable_keys_entity.py b/pype/settings/entities/dict_mutable_keys_entity.py index 7005d346c1..b465171734 100644 --- a/pype/settings/entities/dict_mutable_keys_entity.py +++ b/pype/settings/entities/dict_mutable_keys_entity.py @@ -1,3 +1,4 @@ +import re import copy from .lib import ( @@ -7,6 +8,7 @@ from .lib import ( from . import EndpointEntity from .exceptions import ( DefaultsNotDefined, + InvalidKeySymbols, StudioDefaultsNotDefined, RequiredKeyModified, EntitySchemaError @@ -14,7 +16,9 @@ from .exceptions import ( from pype.settings.constants import ( METADATA_KEYS, M_DYNAMIC_KEY_LABEL, - M_ENVIRONMENT_KEY + M_ENVIRONMENT_KEY, + KEY_REGEX, + KEY_ALLOWED_SYMBOLS ) @@ -92,6 +96,9 @@ class DictMutableKeysEntity(EndpointEntity): # TODO Check for value type if is Settings entity? child_obj = self.children_by_key.get(key) if not child_obj: + if not KEY_REGEX.match(key): + raise InvalidKeySymbols(self.path, key) + child_obj = self.add_key(key) child_obj.set(value) @@ -102,6 +109,10 @@ class DictMutableKeysEntity(EndpointEntity): if new_key == old_key: return + + if not KEY_REGEX.match(new_key): + raise InvalidKeySymbols(self.path, new_key) + self.children_by_key[new_key] = self.children_by_key.pop(old_key) self._on_key_label_change() @@ -116,6 +127,9 @@ class DictMutableKeysEntity(EndpointEntity): if key in self.children_by_key: self.pop(key) + if not KEY_REGEX.match(key): + raise InvalidKeySymbols(self.path, key) + if self.value_is_env_group: item_schema = copy.deepcopy(self.item_schema) item_schema["env_group_key"] = key @@ -188,7 +202,7 @@ class DictMutableKeysEntity(EndpointEntity): self.schema_data.get("highlight_content") or False ) - object_type = self.schema_data["object_type"] + object_type = self.schema_data.get("object_type") or {} if not isinstance(object_type, dict): # Backwards compatibility object_type = { @@ -212,6 +226,12 @@ class DictMutableKeysEntity(EndpointEntity): def schema_validations(self): super(DictMutableKeysEntity, self).schema_validations() + if not self.schema_data.get("object_type"): + reason = ( + "Modifiable dictionary must have specified `object_type`." + ) + raise EntitySchemaError(self, reason) + # TODO Ability to store labels should be defined with different key if self.collapsible_key and not self.file_item: reason = ( @@ -325,6 +345,15 @@ class DictMutableKeysEntity(EndpointEntity): children_label_by_id = {} metadata_labels = metadata.get(M_DYNAMIC_KEY_LABEL) or {} for _key, _value in new_value.items(): + if not KEY_REGEX.match(_key): + # Replace invalid characters with underscore + # - this is safety to not break already existing settings + _key = re.sub( + r"[^{}]+".format(KEY_ALLOWED_SYMBOLS), + "_", + _key + ) + child_entity = self._add_key(_key) child_entity.update_default_value(_value) if using_project_overrides: diff --git a/pype/settings/entities/enum_entity.py b/pype/settings/entities/enum_entity.py index f06ec97f4b..c486de397e 100644 --- a/pype/settings/entities/enum_entity.py +++ b/pype/settings/entities/enum_entity.py @@ -170,14 +170,32 @@ class ToolsEnumEntity(BaseEnumEntity): valid_keys = set() enum_items = [] - for tool_group in system_settings_entity["tools"].values(): - enabled_entity = tool_group.get("enabled") - if enabled_entity and not enabled_entity.value: - continue + tool_groups_entity = system_settings_entity["tools"]["tool_groups"] + for group_name, tool_group in tool_groups_entity.items(): + # Try to get group label from entity + group_label = None + if hasattr(tool_groups_entity, "get_key_label"): + group_label = tool_groups_entity.get_key_label(group_name) - for variant_name in tool_group["variants"].keys(): - enum_items.append({variant_name: variant_name}) - valid_keys.add(variant_name) + variants_entity = tool_group["variants"] + for variant_name in variants_entity.keys(): + # Prepare tool name (used as value) + tool_name = "/".join((group_name, variant_name)) + + # Try to get variant label from entity + variant_label = None + if hasattr(variants_entity, "get_key_label"): + variant_label = variants_entity.get_key_label(variant_name) + + # Tool label that will be shown + # - use tool name itself if labels are not filled + if group_label and variant_label: + tool_label = " ".join((group_label, variant_label)) + else: + tool_label = tool_name + + enum_items.append({tool_name: tool_label}) + valid_keys.add(tool_name) return enum_items, valid_keys def set_override_state(self, *args, **kwargs): diff --git a/pype/settings/entities/exceptions.py b/pype/settings/entities/exceptions.py index 2c3b262ff1..f86d08ab5f 100644 --- a/pype/settings/entities/exceptions.py +++ b/pype/settings/entities/exceptions.py @@ -1,3 +1,6 @@ +from pype.settings.constants import KEY_ALLOWED_SYMBOLS + + class DefaultsNotDefined(Exception): def __init__(self, obj): msg = "Default values for object are not set. {}".format(obj.path) @@ -34,6 +37,14 @@ class RequiredKeyModified(KeyError): super(RequiredKeyModified, self).__init__(msg.format(entity_path, key)) +class InvalidKeySymbols(KeyError): + def __init__(self, entity_path, key): + msg = "{} - Invalid key \"{}\". Allowed symbols are {}" + super(InvalidKeySymbols, self).__init__( + msg.format(entity_path, key, KEY_ALLOWED_SYMBOLS) + ) + + class SchemaError(Exception): pass diff --git a/pype/settings/entities/root_entities.py b/pype/settings/entities/root_entities.py index 82885e8479..e7cb098c67 100644 --- a/pype/settings/entities/root_entities.py +++ b/pype/settings/entities/root_entities.py @@ -13,11 +13,15 @@ from .lib import ( get_studio_settings_schema, get_project_settings_schema ) -from .exceptions import EntitySchemaError +from .exceptions import ( + SchemaError, + InvalidKeySymbols +) from pype.settings.constants import ( SYSTEM_SETTINGS_KEY, PROJECT_SETTINGS_KEY, - PROJECT_ANATOMY_KEY + PROJECT_ANATOMY_KEY, + KEY_REGEX ) from pype.settings.lib import ( @@ -150,9 +154,13 @@ class RootEntity(BaseItemEntity): "Root entity \"{}\" has child with `is_group`" " attribute set to True but root can't save overrides." ).format(self.__class__.__name__) - raise EntitySchemaError(self, reason) + raise SchemaError(reason) child_entity.schema_validations() + for key in self.non_gui_children.keys(): + if not KEY_REGEX.match(key): + raise InvalidKeySymbols(self.path, key) + def get_entity_from_path(self, path): """Return system settings entity.""" raise NotImplementedError(( diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json index 90e068ba33..3fe01cad09 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -20,27 +20,27 @@ "children": [ { "type": "text", - "key": "Create...", + "key": "create", "label": "Create..." }, { "type": "text", - "key": "Publish...", + "key": "publish", "label": "Publish..." }, { "type": "text", - "key": "Load...", + "key": "load", "label": "Load..." }, { "type": "text", - "key": "Manage...", + "key": "manage", "label": "Manage..." }, { "type": "text", - "key": "Build Workfile", + "key": "build_workfile", "label": "Build Workfile" } ] diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json index 4304c65445..6e1ba352fc 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json @@ -29,12 +29,14 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "2020", - "host_name": "aftereffects" + "app_variant_label": "2020", + "app_variant": "2020", + "app_name": "aftereffects" }, { - "host_version": "2021", - "host_name": "aftereffects" + "app_variant_label": "2021", + "app_variant": "2021", + "app_name": "aftereffects" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_blender.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_blender.json index 5d8cb45da8..5030f8280f 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_blender.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_blender.json @@ -29,12 +29,14 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "2.90", - "host_name": "blender" + "app_variant_label": "2.83", + "app_variant": "2-83", + "app_name": "blender" }, { - "host_version": "2.83", - "host_name": "blender" + "app_variant_label": "2.90", + "app_variant": "2-90", + "app_name": "blender" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_celaction.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_celaction.json index ab3f0f3f15..c5fe824f94 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_celaction.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_celaction.json @@ -29,14 +29,9 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "Local", - "host_name": "celation", - "multiplatform": false, - "multipath_executables": false - }, - { - "host_version": "Publish", - "host_name": "celation", + "app_variant_label": "Local", + "app_variant": "Local", + "app_name": "celation", "multiplatform": false, "multipath_executables": false } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_djv.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_djv.json index 02c90a92ad..3f3af3585a 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_djv.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_djv.json @@ -28,8 +28,9 @@ "type": "schema_template", "name": "template_host_variant", "template_data": { - "host_version": "1.1", - "host_name": "djvview" + "app_variant_label": "1.1", + "app_variant": "1-1", + "app_name": "djvview" } } ] diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_fusion.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_fusion.json index 1c1b7653d9..d693c39ffe 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_fusion.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_fusion.json @@ -29,12 +29,14 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "16", - "host_name": "fusion" + "app_variant_label": "16", + "app_variant": "16", + "app_name": "fusion" }, { - "host_version": "9", - "host_name": "fusion" + "app_variant_label": "9", + "app_variant": "9", + "app_name": "fusion" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_harmony.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_harmony.json index b0abf35bfa..8ad07c95ba 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_harmony.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_harmony.json @@ -29,12 +29,14 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "20", - "host_name": "harmony" + "app_variant_label": "20", + "app_variant": "20", + "app_name": "harmony" }, { - "host_version": "17", - "host_name": "harmony" + "app_variant_label": "17", + "app_variant": "17", + "app_name": "harmony" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_houdini.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_houdini.json index 125e6ac180..7698cb4cc1 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_houdini.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_houdini.json @@ -29,16 +29,19 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "18.5", - "host_name": "houdini" + "app_variant_label": "18.5", + "app_variant": "18-5", + "app_name": "houdini" }, { - "host_version": "18", - "host_name": "houdini" + "app_variant_label": "18", + "app_variant": "18", + "app_name": "houdini" }, { - "host_version": "17", - "host_name": "houdini" + "app_variant_label": "17", + "app_variant": "17", + "app_name": "houdini" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_maya.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_maya.json index 84782cb3d8..d8396b16cb 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_maya.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_maya.json @@ -29,16 +29,19 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "2020", - "host_name": "maya" + "app_variant_label": "2020", + "app_variant": "2020", + "app_name": "maya" }, { - "host_version": "2019", - "host_name": "maya" + "app_variant_label": "2019", + "app_variant": "2019", + "app_name": "maya" }, { - "host_version": "2018", - "host_name": "maya" + "app_variant_label": "2018", + "app_variant": "2018", + "app_name": "maya" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_mayabatch.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_mayabatch.json index dbd850dcd6..af7cc3d301 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_mayabatch.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_mayabatch.json @@ -29,16 +29,19 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "2020", - "host_name": "mayabatch" + "app_variant_label": "2020", + "app_variant": "2020", + "app_name": "mayabatch" }, { - "host_version": "2019", - "host_name": "mayabatch" + "app_variant_label": "2019", + "app_variant": "2019", + "app_name": "mayabatch" }, { - "host_version": "2018", - "host_name": "mayabatch" + "app_variant_label": "2018", + "app_variant": "2018", + "app_name": "mayabatch" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_photoshop.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_photoshop.json index 136eb16888..a8e3574aa3 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_photoshop.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_photoshop.json @@ -29,12 +29,14 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "2020", - "host_name": "photoshop" + "app_variant_label": "2020", + "app_variant": "2020", + "app_name": "photoshop" }, { - "host_version": "2021", - "host_name": "photoshop" + "app_variant_label": "2021", + "app_variant": "2021", + "app_name": "photoshop" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_resolve.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_resolve.json index 2d11e1def4..052a935410 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_resolve.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_resolve.json @@ -29,8 +29,9 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "16", - "host_name": "resolve" + "app_variant_label": "16", + "app_variant": "16", + "app_name": "resolve" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_shell.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_shell.json index 4fdbd65c24..3288fe2ffb 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_shell.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_shell.json @@ -25,16 +25,19 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "Python 3.7", - "host_name": "python" + "app_variant": "python_3-7", + "app_variant_label": "Python 3.7", + "app_name": "python" }, { - "host_version": "Python 2.7", - "host_name": "python" + "app_variant": "python_2-7", + "app_variant_label": "Python 2.7", + "app_name": "python" }, { - "host_version": "Terminal", - "host_name": "terminal" + "app_variant": "terminal", + "app_variant_label": "Terminal", + "app_name": "terminal" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_tvpaint.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_tvpaint.json index 1c88d12cab..a3cc6188ac 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_tvpaint.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_tvpaint.json @@ -29,12 +29,14 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "Animation 11 (64bits)", - "host_name": "tvpaint" + "app_variant_label": "Animation 11 (64bits)", + "app_variant": "animation_11-64bits", + "app_name": "tvpaint" }, { - "host_version": "Animation 11 (32bits)", - "host_name": "tvpaint" + "app_variant_label": "Animation 11 (32bits)", + "app_variant": "animation_11-32bits", + "app_name": "tvpaint" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/schema_unreal.json b/pype/settings/entities/schemas/system_schema/host_settings/schema_unreal.json index d7065ad3ff..c79f08b71a 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/schema_unreal.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/schema_unreal.json @@ -29,8 +29,9 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "4.24", - "host_name": "unreal" + "app_variant": "4-24", + "app_variant_label": "4.24", + "app_name": "unreal" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/template_host_variant.json b/pype/settings/entities/schemas/system_schema/host_settings/template_host_variant.json index ba009cf094..c809891b30 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/template_host_variant.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/template_host_variant.json @@ -7,8 +7,8 @@ }, { "type": "dict", - "key": "{host_name}_{host_version}", - "label": "{host_version}", + "key": "{app_name}_{app_variant}", + "label": "{app_variant_label}", "collapsible": true, "checkbox_key": "enabled", "children": [ @@ -78,7 +78,7 @@ "key": "environment", "label": "Environment", "type": "raw-json", - "env_group_key": "{host_name}_{host_version}" + "env_group_key": "{app_name}_{app_variant}" } ] } diff --git a/pype/settings/entities/schemas/system_schema/host_settings/template_nuke.json b/pype/settings/entities/schemas/system_schema/host_settings/template_nuke.json index 01c3be726a..c86c2aef61 100644 --- a/pype/settings/entities/schemas/system_schema/host_settings/template_nuke.json +++ b/pype/settings/entities/schemas/system_schema/host_settings/template_nuke.json @@ -30,24 +30,24 @@ "name": "template_host_variant", "template_data": [ { - "host_version": "12.2", - "host_name": "{nuke_type}", - "multipath_executables": true + "app_variant": "12-2", + "app_variant_label": "12.2", + "app_name": "{nuke_type}" }, { - "host_version": "12.0", - "host_name": "{nuke_type}", - "multipath_executables": true + "app_variant": "12-0", + "app_variant_label": "12.0", + "app_name": "{nuke_type}" }, { - "host_version": "11.3", - "host_name": "{nuke_type}", - "multipath_executables": true + "app_variant": "11-3", + "app_variant_label": "11.3", + "app_name": "{nuke_type}" }, { - "host_version": "11.2", - "host_name": "{nuke_type}", - "multipath_executables": true + "app_variant": "11-2", + "app_variant_label": "11.2", + "app_name": "{nuke_type}" } ] } diff --git a/pype/settings/entities/schemas/system_schema/schema_tools.json b/pype/settings/entities/schemas/system_schema/schema_tools.json index 2c04abc47c..2346bef36d 100644 --- a/pype/settings/entities/schemas/system_schema/schema_tools.json +++ b/pype/settings/entities/schemas/system_schema/schema_tools.json @@ -1,37 +1,35 @@ { - "key": "tools", "type": "dict", - "label": "Tools", + "key": "tools", "collapsible": true, "is_file": true, "children": [ { - "type": "schema", - "name": "schema_arnold" - }, - { - "type": "schema", - "name": "schema_vray" - }, - { - "type": "schema", - "name": "schema_yeti" - }, - { - "type": "dict", - "key": "other", - "children": [ - { - "type": "schema_template", - "name": "template_tool_variant", - "template_data": [ - { - "tool_name": "othertools", - "tool_label": "Other Tools and Plugins" + "type": "dict-modifiable", + "label": "Tools", + "key": "tool_groups", + "collapsible_key": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "environment", + "label": "Environments", + "type": "raw-json" + }, + { + "type": "separator" + }, + { + "type": "dict-modifiable", + "key": "variants", + "collapsible_key": true, + "object_type": { + "type": "raw-json" } - ] - } - ] + } + ] + } } ] } diff --git a/pype/settings/entities/schemas/system_schema/tool_settings/schema_arnold.json b/pype/settings/entities/schemas/system_schema/tool_settings/schema_arnold.json deleted file mode 100644 index db2be09c83..0000000000 --- a/pype/settings/entities/schemas/system_schema/tool_settings/schema_arnold.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "dict", - "key": "mtoa", - "label": "Autodesk Arnold", - "collapsible": true, - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "environment", - "label": "Environment (mtoa)", - "type": "raw-json", - "env_group_key": "mtoa" - }, - { - "type": "schema_template", - "name": "template_tool_variant", - "template_data": [ - { - "tool_label": "Arnold Versions" - } - ] - } - ] -} diff --git a/pype/settings/entities/schemas/system_schema/tool_settings/schema_vray.json b/pype/settings/entities/schemas/system_schema/tool_settings/schema_vray.json deleted file mode 100644 index 295b3ccac3..0000000000 --- a/pype/settings/entities/schemas/system_schema/tool_settings/schema_vray.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "dict", - "key": "vray", - "label": "Chaos Group Vray", - "collapsible": true, - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "environment", - "label": "Environment", - "type": "raw-json", - "env_group_key": "vray" - }, - { - "type": "schema_template", - "name": "template_tool_variant", - "template_data": [ - { - "tool_label": "Vray Versions" - } - ] - } - ] -} diff --git a/pype/settings/entities/schemas/system_schema/tool_settings/schema_yeti.json b/pype/settings/entities/schemas/system_schema/tool_settings/schema_yeti.json deleted file mode 100644 index 34bb09da8d..0000000000 --- a/pype/settings/entities/schemas/system_schema/tool_settings/schema_yeti.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "dict", - "key": "yeti", - "label": "Pergrine Labs Yeti", - "collapsible": true, - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "environment", - "label": "Environment", - "type": "raw-json", - "env_group_key": "yeti" - }, - { - "type": "schema_template", - "name": "template_tool_variant", - "template_data": [ - { - "tool_label": "Yeti Versions" - } - ] - } - ] -} diff --git a/pype/settings/entities/schemas/system_schema/tool_settings/template_tool_variant.json b/pype/settings/entities/schemas/system_schema/tool_settings/template_tool_variant.json deleted file mode 100644 index b0ba63469c..0000000000 --- a/pype/settings/entities/schemas/system_schema/tool_settings/template_tool_variant.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "type": "dict-modifiable", - "key": "variants", - "label": "{tool_label}", - "value_is_env_group": true, - "object_type": { - "type": "raw-json" - } - } -] diff --git a/pype/tools/settings/settings/widgets/dict_mutable_widget.py b/pype/tools/settings/settings/widgets/dict_mutable_widget.py index 0cb051082e..e44ffdf35a 100644 --- a/pype/tools/settings/settings/widgets/dict_mutable_widget.py +++ b/pype/tools/settings/settings/widgets/dict_mutable_widget.py @@ -12,6 +12,7 @@ from .lib import ( BTN_FIXED_SIZE, CHILD_OFFSET ) +from pype.settings.constants import KEY_REGEX def create_add_btn(parent): @@ -37,6 +38,7 @@ class ModifiableDictEmptyItem(QtWidgets.QWidget): self.collapsible_key = entity_widget.entity.collapsible_key self.is_duplicated = False + self.key_is_valid = False if self.collapsible_key: self.create_collapsible_ui() @@ -86,6 +88,9 @@ class ModifiableDictEmptyItem(QtWidgets.QWidget): if self.is_duplicated: return + if not self.key_is_valid: + return + key = self.key_input.text() if key: label = self.key_label_input.text() @@ -95,9 +100,10 @@ class ModifiableDictEmptyItem(QtWidgets.QWidget): def _on_key_change(self): key = self.key_input.text() + self.key_is_valid = KEY_REGEX.match(key) self.is_duplicated = self.entity_widget.is_key_duplicated(key) key_input_state = "" - if self.is_duplicated: + if self.is_duplicated or not self.key_is_valid: key_input_state = "invalid" elif key != "": key_input_state = "modified" @@ -157,6 +163,7 @@ class ModifiableDictItem(QtWidgets.QWidget): self.ignore_input_changes = entity_widget.ignore_input_changes self.is_key_duplicated = False + self.key_is_valid = False self.is_required = False self.origin_key = None @@ -334,7 +341,7 @@ class ModifiableDictItem(QtWidgets.QWidget): else: self._on_focus_lose() - if not self.is_key_duplicated: + if not self.is_key_duplicated and self.key_is_valid: self.entity_widget.change_key(self.key_value(), self) def set_key_label(self, key, label): @@ -382,11 +389,12 @@ class ModifiableDictItem(QtWidgets.QWidget): def _on_key_change(self): key = self.key_value() + self.key_is_valid = KEY_REGEX.match(key) is_key_duplicated = self.entity_widget.validate_key_duplication( self.temp_key, key, self ) self.temp_key = key - if is_key_duplicated: + if is_key_duplicated or not self.key_is_valid: return if key: @@ -458,6 +466,7 @@ class ModifiableDictItem(QtWidgets.QWidget): self.is_key_duplicated or self.key_value() == "" or self.child_invalid + or not self.key_is_valid ) @property @@ -473,7 +482,11 @@ class ModifiableDictItem(QtWidgets.QWidget): def update_style(self): key_input_state = "" - if self.is_key_duplicated or self.key_value() == "": + if ( + self.is_key_duplicated + or self.key_value() == "" + or not self.key_is_valid + ): key_input_state = "invalid" elif self.is_key_modified(): key_input_state = "modified" @@ -752,15 +765,17 @@ class DictMutableKeysWidget(BaseWidget): old_key_items.append(input_field) if duplicated_items: - widget.set_is_key_duplicated(True) for input_field in duplicated_items: input_field.set_is_key_duplicated(True) + widget.set_is_key_duplicated(True) else: widget.set_is_key_duplicated(False) if len(old_key_items) == 1: for input_field in old_key_items: input_field.set_is_key_duplicated(False) + input_field.set_key(old_key) + input_field.update_key_label() self.trigger_hierarchical_style_update() return bool(duplicated_items)