From 521707340af3e2357c7764d906da3659dec95e9d Mon Sep 17 00:00:00 2001 From: Mustafa-Zarkash Date: Mon, 9 Oct 2023 12:30:58 +0300 Subject: [PATCH 01/22] allow using template keys in houdini shelves manager --- openpype/hosts/houdini/api/shelves.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index 21e44e494a..c961b0242d 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -6,6 +6,9 @@ import platform from openpype.settings import get_project_settings from openpype.pipeline import get_current_project_name +from openpype.lib import StringTemplate +from openpype.pipeline.context_tools import get_current_context_template_data + import hou log = logging.getLogger("openpype.hosts.houdini.shelves") @@ -26,9 +29,15 @@ def generate_shelves(): log.debug("No custom shelves found in project settings.") return + # Get Template data + template_data = get_current_context_template_data() + for shelf_set_config in shelves_set_config: shelf_set_filepath = shelf_set_config.get('shelf_set_source_path') shelf_set_os_filepath = shelf_set_filepath[current_os] + shelf_set_os_filepath = get_path_using_template_data( + shelf_set_os_filepath, template_data + ) if shelf_set_os_filepath: if not os.path.isfile(shelf_set_os_filepath): log.error("Shelf path doesn't exist - " @@ -81,7 +90,7 @@ def generate_shelves(): "script path of the tool.") continue - tool = get_or_create_tool(tool_definition, shelf) + tool = get_or_create_tool(tool_definition, shelf, template_data) if not tool: continue @@ -144,7 +153,7 @@ def get_or_create_shelf(shelf_label): return new_shelf -def get_or_create_tool(tool_definition, shelf): +def get_or_create_tool(tool_definition, shelf, template_data): """This function verifies if the tool exists and updates it. If not, creates a new one. @@ -162,6 +171,7 @@ def get_or_create_tool(tool_definition, shelf): return script_path = tool_definition["script"] + script_path = get_path_using_template_data(script_path, template_data) if not script_path or not os.path.exists(script_path): log.warning("This path doesn't exist - {}".format(script_path)) return @@ -184,3 +194,10 @@ def get_or_create_tool(tool_definition, shelf): tool_name = re.sub(r"[^\w\d]+", "_", tool_label).lower() return hou.shelves.newTool(name=tool_name, **tool_definition) + + +def get_path_using_template_data(path, template_data): + path = StringTemplate.format_template(path, template_data) + path = path.replace("\\", "/") + + return path From 587beadd4d430ba2ae33f0a6fee8461f5e9abb7f Mon Sep 17 00:00:00 2001 From: Mustafa-Zarkash Date: Mon, 9 Oct 2023 12:36:23 +0300 Subject: [PATCH 02/22] resolve hound --- openpype/hosts/houdini/api/shelves.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index c961b0242d..a93f8becfb 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -90,7 +90,9 @@ def generate_shelves(): "script path of the tool.") continue - tool = get_or_create_tool(tool_definition, shelf, template_data) + tool = get_or_create_tool( + tool_definition, shelf, template_data + ) if not tool: continue From 1824670bcce30df524820bb9880002e3f72d8b71 Mon Sep 17 00:00:00 2001 From: Mustafa-Zarkash Date: Mon, 9 Oct 2023 14:46:32 +0300 Subject: [PATCH 03/22] save unnecessary call --- openpype/hosts/houdini/api/shelves.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index a93f8becfb..4d6a05b79d 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -35,10 +35,10 @@ def generate_shelves(): for shelf_set_config in shelves_set_config: shelf_set_filepath = shelf_set_config.get('shelf_set_source_path') shelf_set_os_filepath = shelf_set_filepath[current_os] - shelf_set_os_filepath = get_path_using_template_data( - shelf_set_os_filepath, template_data - ) if shelf_set_os_filepath: + shelf_set_os_filepath = get_path_using_template_data( + shelf_set_os_filepath, template_data + ) if not os.path.isfile(shelf_set_os_filepath): log.error("Shelf path doesn't exist - " "{}".format(shelf_set_os_filepath)) From 15aec6db161f86bf4258f2148fa853ccbfe24ad1 Mon Sep 17 00:00:00 2001 From: Mustafa-Zarkash Date: Mon, 9 Oct 2023 17:52:30 +0300 Subject: [PATCH 04/22] allow icon path to include template keys --- openpype/hosts/houdini/api/shelves.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index 4d6a05b79d..4b5ebd4202 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -178,6 +178,11 @@ def get_or_create_tool(tool_definition, shelf, template_data): log.warning("This path doesn't exist - {}".format(script_path)) return + icon_path = tool_definition["icon"] + if icon_path: + icon_path = get_path_using_template_data(icon_path, template_data) + tool_definition["icon"] = icon_path + existing_tools = shelf.tools() existing_tool = next( (tool for tool in existing_tools if tool.label() == tool_label), From 2f2e100231089b3bb321ed1e72962b929f75621f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 10 Oct 2023 09:43:28 +0200 Subject: [PATCH 05/22] Fix Show in usdview loader action --- .../houdini/plugins/load/show_usdview.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/houdini/plugins/load/show_usdview.py b/openpype/hosts/houdini/plugins/load/show_usdview.py index 7b03a0738a..d56c4acc4f 100644 --- a/openpype/hosts/houdini/plugins/load/show_usdview.py +++ b/openpype/hosts/houdini/plugins/load/show_usdview.py @@ -1,4 +1,5 @@ import os +import platform import subprocess from openpype.lib.vendor_bin_utils import find_executable @@ -8,17 +9,31 @@ from openpype.pipeline import load class ShowInUsdview(load.LoaderPlugin): """Open USD file in usdview""" - families = ["colorbleed.usd"] label = "Show in usdview" - representations = ["usd", "usda", "usdlc", "usdnc"] - order = 10 + representations = ["*"] + families = ["*"] + extensions = {"usd", "usda", "usdlc", "usdnc", "abc"} + order = 15 icon = "code-fork" color = "white" def load(self, context, name=None, namespace=None, data=None): + from pathlib import Path - usdview = find_executable("usdview") + if platform.system() == "Windows": + executable = "usdview.bat" + else: + executable = "usdview" + + usdview = find_executable(executable) + if not usdview: + raise RuntimeError("Unable to find usdview") + + # For some reason Windows can return the path like: + # C:/PROGRA~1/SIDEEF~1/HOUDIN~1.435/bin/usdview + # convert to resolved path so `subprocess` can take it + usdview = str(Path(usdview).resolve().as_posix()) filepath = self.filepath_from_context(context) filepath = os.path.normpath(filepath) @@ -30,14 +45,4 @@ class ShowInUsdview(load.LoaderPlugin): self.log.info("Start houdini variant of usdview...") - # For now avoid some pipeline environment variables that initialize - # Avalon in Houdini as it is redundant for usdview and slows boot time - env = os.environ.copy() - env.pop("PYTHONPATH", None) - env.pop("HOUDINI_SCRIPT_PATH", None) - env.pop("HOUDINI_MENU_PATH", None) - - # Force string to avoid unicode issues - env = {str(key): str(value) for key, value in env.items()} - - subprocess.Popen([usdview, filepath, "--renderer", "GL"], env=env) + subprocess.Popen([usdview, filepath, "--renderer", "GL"]) From f214751be375016d4464b1faf4e427c1851f36a4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Oct 2023 16:29:55 +0200 Subject: [PATCH 06/22] modules can be loaded in dev mode correctly --- openpype/modules/base.py | 87 ++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index a3c21718b9..e5741728d9 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -37,7 +37,6 @@ from openpype.lib import ( import_filepath, import_module_from_dirpath, ) -from openpype.lib.openpype_version import is_staging_enabled from .interfaces import ( OpenPypeInterface, @@ -317,21 +316,10 @@ def load_modules(force=False): time.sleep(0.1) -def _get_ayon_addons_information(): - """Receive information about addons to use from server. - - Todos: - Actually ask server for the information. - Allow project name as optional argument to be able to query information - about used addons for specific project. - Returns: - List[Dict[str, Any]]: List of addon information to use. - """ - - output = [] +def _get_ayon_bundle_data(): bundle_name = os.getenv("AYON_BUNDLE_NAME") bundles = ayon_api.get_bundles()["bundles"] - final_bundle = next( + return next( ( bundle for bundle in bundles @@ -339,10 +327,32 @@ def _get_ayon_addons_information(): ), None ) - if final_bundle is None: - return output - bundle_addons = final_bundle["addons"] + +def _is_dev_mode_enabled(): + """Dev mode is enabled in AYON. + + Returns: + bool: True if dev mode is enabled. + """ + + return os.getenv("AYON_DEV_MODE") == "1" + + +def _get_ayon_addons_information(bundle_info): + """Receive information about addons to use from server. + + Todos: + Actually ask server for the information. + Allow project name as optional argument to be able to query information + about used addons for specific project. + + Returns: + List[Dict[str, Any]]: List of addon information to use. + """ + + output = [] + bundle_addons = bundle_info["addons"] addons = ayon_api.get_addons_info()["addons"] for addon in addons: name = addon["name"] @@ -378,31 +388,56 @@ def _load_ayon_addons(openpype_modules, modules_key, log): v3_addons_to_skip = [] - addons_info = _get_ayon_addons_information() + bundle_info = _get_ayon_bundle_data() + addons_info = _get_ayon_addons_information(bundle_info) if not addons_info: return v3_addons_to_skip + addons_dir = os.environ.get("AYON_ADDONS_DIR") if not addons_dir: addons_dir = os.path.join( appdirs.user_data_dir("AYON", "Ynput"), "addons" ) - if not os.path.exists(addons_dir): + + dev_mode_enabled = _is_dev_mode_enabled() + dev_addons_info = {} + if dev_mode_enabled: + # Get dev addons info only when dev mode is enabled + dev_addons_info = bundle_info.get("addonDevelopment", dev_addons_info) + + addons_dir_exists = os.path.exists(addons_dir) + if not addons_dir_exists: log.warning("Addons directory does not exists. Path \"{}\"".format( addons_dir )) - return v3_addons_to_skip for addon_info in addons_info: addon_name = addon_info["name"] addon_version = addon_info["version"] - folder_name = "{}_{}".format(addon_name, addon_version) - addon_dir = os.path.join(addons_dir, folder_name) - if not os.path.exists(addon_dir): - log.debug(( - "No localized client code found for addon {} {}." - ).format(addon_name, addon_version)) + dev_addon_info = dev_addons_info.get(addon_name, {}) + use_dev_path = dev_addon_info.get("enabled", False) + + addon_dir = None + if use_dev_path: + addon_dir = dev_addon_info["path"] + if not addon_dir or not os.path.exists(addon_dir): + log.warning(( + "Dev addon {} {} path does not exists. Path \"{}\"" + ).format(addon_name, addon_version, addon_dir)) + continue + + elif addons_dir_exists: + folder_name = "{}_{}".format(addon_name, addon_version) + addon_dir = os.path.join(addons_dir, folder_name) + if not os.path.exists(addon_dir): + log.debug(( + "No localized client code found for addon {} {}." + ).format(addon_name, addon_version)) + continue + + if not addon_dir: continue sys.path.insert(0, addon_dir) From 74acdd63eee751eecfe2d92933d3d3752d89b4ce Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Oct 2023 16:41:27 +0200 Subject: [PATCH 07/22] change env variable key --- openpype/modules/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index e5741728d9..355fee0e0a 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -336,7 +336,7 @@ def _is_dev_mode_enabled(): bool: True if dev mode is enabled. """ - return os.getenv("AYON_DEV_MODE") == "1" + return os.getenv("AYON_USE_DEV") == "1" def _get_ayon_addons_information(bundle_info): From a32e3996956b78db172f3a08989650fdeff9e58d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Oct 2023 17:06:21 +0200 Subject: [PATCH 08/22] use dev variant in dev mode --- openpype/settings/ayon_settings.py | 40 +++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/openpype/settings/ayon_settings.py b/openpype/settings/ayon_settings.py index 3ccb18111a..eb64480dc3 100644 --- a/openpype/settings/ayon_settings.py +++ b/openpype/settings/ayon_settings.py @@ -290,6 +290,16 @@ def _convert_modules_system( modules_settings[key] = value +def is_dev_mode_enabled(): + """Dev mode is enabled in AYON. + + Returns: + bool: True if dev mode is enabled. + """ + + return os.getenv("AYON_USE_DEV") == "1" + + def convert_system_settings(ayon_settings, default_settings, addon_versions): default_settings = copy.deepcopy(default_settings) output = { @@ -1400,15 +1410,39 @@ class _AyonSettingsCache: if _AyonSettingsCache.variant is None: from openpype.lib.openpype_version import is_staging_enabled - _AyonSettingsCache.variant = ( - "staging" if is_staging_enabled() else "production" - ) + variant = "production" + if is_dev_mode_enabled(): + variant = cls._get_dev_mode_settings_variant() + elif is_staging_enabled(): + variant = "staging" + _AyonSettingsCache.variant = variant return _AyonSettingsCache.variant @classmethod def _get_bundle_name(cls): return os.environ["AYON_BUNDLE_NAME"] + @classmethod + def _get_dev_mode_settings_variant(cls): + """Develop mode settings variant. + + Returns: + str: Name of settings variant. + """ + + bundles = ayon_api.get_bundles() + user = ayon_api.get_user() + username = user["name"] + for bundle in bundles: + if ( + bundle.get("isDev") + and bundle.get("activeUser") == username + ): + return bundle["name"] + # Return fake variant - distribution logic will tell user that he does not + # have set any dev bundle + return "dev" + @classmethod def get_value_by_project(cls, project_name): cache_item = _AyonSettingsCache.cache_by_project_name[project_name] From 2088c7d7e6109b589724a13905547cbb1d6aa28f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Oct 2023 17:06:38 +0200 Subject: [PATCH 09/22] use 'is_dev_mode_enabled' from 'ayon_'settings' --- openpype/modules/base.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 355fee0e0a..6f3e4566f3 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -31,6 +31,7 @@ from openpype.settings.lib import ( get_studio_system_settings_overrides, load_json_file ) +from openpype.settings.ayon_settings import is_dev_mode_enabled from openpype.lib import ( Logger, @@ -329,16 +330,6 @@ def _get_ayon_bundle_data(): ) -def _is_dev_mode_enabled(): - """Dev mode is enabled in AYON. - - Returns: - bool: True if dev mode is enabled. - """ - - return os.getenv("AYON_USE_DEV") == "1" - - def _get_ayon_addons_information(bundle_info): """Receive information about addons to use from server. @@ -400,7 +391,7 @@ def _load_ayon_addons(openpype_modules, modules_key, log): "addons" ) - dev_mode_enabled = _is_dev_mode_enabled() + dev_mode_enabled = is_dev_mode_enabled() dev_addons_info = {} if dev_mode_enabled: # Get dev addons info only when dev mode is enabled From 68f7826cf610b8160cb8ce21bc764f1964eb4559 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 18 Oct 2023 10:16:17 +0200 Subject: [PATCH 10/22] updated 'ayon_api' to '0.5.1' --- .../vendor/python/common/ayon_api/_api.py | 14 +- .../python/common/ayon_api/graphql_queries.py | 5 + .../python/common/ayon_api/server_api.py | 177 ++++++++++++------ .../vendor/python/common/ayon_api/utils.py | 83 ++++++-- .../vendor/python/common/ayon_api/version.py | 2 +- 5 files changed, 194 insertions(+), 87 deletions(-) diff --git a/openpype/vendor/python/common/ayon_api/_api.py b/openpype/vendor/python/common/ayon_api/_api.py index 22e137d6e5..9f89d3d59e 100644 --- a/openpype/vendor/python/common/ayon_api/_api.py +++ b/openpype/vendor/python/common/ayon_api/_api.py @@ -602,12 +602,12 @@ def delete_installer(*args, **kwargs): def download_installer(*args, **kwargs): con = get_server_api_connection() - con.download_installer(*args, **kwargs) + return con.download_installer(*args, **kwargs) def upload_installer(*args, **kwargs): con = get_server_api_connection() - con.upload_installer(*args, **kwargs) + return con.upload_installer(*args, **kwargs) # Dependency packages @@ -753,12 +753,12 @@ def get_secrets(*args, **kwargs): def get_secret(*args, **kwargs): con = get_server_api_connection() - return con.delete_secret(*args, **kwargs) + return con.get_secret(*args, **kwargs) def save_secret(*args, **kwargs): con = get_server_api_connection() - return con.delete_secret(*args, **kwargs) + return con.save_secret(*args, **kwargs) def delete_secret(*args, **kwargs): @@ -978,12 +978,14 @@ def delete_project(project_name): def get_thumbnail_by_id(project_name, thumbnail_id): con = get_server_api_connection() - con.get_thumbnail_by_id(project_name, thumbnail_id) + return con.get_thumbnail_by_id(project_name, thumbnail_id) def get_thumbnail(project_name, entity_type, entity_id, thumbnail_id=None): con = get_server_api_connection() - con.get_thumbnail(project_name, entity_type, entity_id, thumbnail_id) + return con.get_thumbnail( + project_name, entity_type, entity_id, thumbnail_id + ) def get_folder_thumbnail(project_name, folder_id, thumbnail_id=None): diff --git a/openpype/vendor/python/common/ayon_api/graphql_queries.py b/openpype/vendor/python/common/ayon_api/graphql_queries.py index 2435fc8a17..cedb3ed2ac 100644 --- a/openpype/vendor/python/common/ayon_api/graphql_queries.py +++ b/openpype/vendor/python/common/ayon_api/graphql_queries.py @@ -144,6 +144,7 @@ def product_types_query(fields): query_queue.append((k, v, field)) return query + def project_product_types_query(fields): query = GraphQlQuery("ProjectProductTypes") project_query = query.add_field("project") @@ -175,6 +176,8 @@ def folders_graphql_query(fields): parent_folder_ids_var = query.add_variable("parentFolderIds", "[String!]") folder_paths_var = query.add_variable("folderPaths", "[String!]") folder_names_var = query.add_variable("folderNames", "[String!]") + folder_types_var = query.add_variable("folderTypes", "[String!]") + statuses_var = query.add_variable("folderStatuses", "[String!]") has_products_var = query.add_variable("folderHasProducts", "Boolean!") project_field = query.add_field("project") @@ -185,6 +188,8 @@ def folders_graphql_query(fields): folders_field.set_filter("parentIds", parent_folder_ids_var) folders_field.set_filter("names", folder_names_var) folders_field.set_filter("paths", folder_paths_var) + folders_field.set_filter("folderTypes", folder_types_var) + folders_field.set_filter("statuses", statuses_var) folders_field.set_filter("hasProducts", has_products_var) nested_fields = fields_to_dict(fields) diff --git a/openpype/vendor/python/common/ayon_api/server_api.py b/openpype/vendor/python/common/ayon_api/server_api.py index 511a239a83..3bac59c192 100644 --- a/openpype/vendor/python/common/ayon_api/server_api.py +++ b/openpype/vendor/python/common/ayon_api/server_api.py @@ -75,6 +75,7 @@ from .utils import ( TransferProgress, create_dependency_package_basename, ThumbnailContent, + get_default_timeout, ) PatternType = type(re.compile("")) @@ -351,7 +352,6 @@ class ServerAPI(object): timeout (Optional[float]): Timeout for requests. max_retries (Optional[int]): Number of retries for requests. """ - _default_timeout = 10.0 _default_max_retries = 3 def __init__( @@ -500,20 +500,13 @@ class ServerAPI(object): def get_default_timeout(cls): """Default value for requests timeout. - First looks for environment variable SERVER_TIMEOUT_ENV_KEY which - can affect timeout value. If not available then use class - attribute '_default_timeout'. + Utils function 'get_default_timeout' is used by default. Returns: float: Timeout value in seconds. """ - try: - return float(os.environ.get(SERVER_TIMEOUT_ENV_KEY)) - except (ValueError, TypeError): - pass - - return cls._default_timeout + return get_default_timeout() @classmethod def get_default_max_retries(cls): @@ -662,13 +655,10 @@ class ServerAPI(object): as default variant. Args: - variant (Literal['production', 'staging']): Settings variant name. + variant (str): Settings variant name. It is possible to use + 'production', 'staging' or name of dev bundle. """ - if variant not in ("production", "staging"): - raise ValueError(( - "Invalid variant name {}. Expected 'production' or 'staging'" - ).format(variant)) self._default_settings_variant = variant default_settings_variant = property( @@ -938,8 +928,8 @@ class ServerAPI(object): int(re_match.group("major")), int(re_match.group("minor")), int(re_match.group("patch")), - re_match.group("prerelease"), - re_match.group("buildmetadata") + re_match.group("prerelease") or "", + re_match.group("buildmetadata") or "", ) return self._server_version_tuple @@ -1140,31 +1130,41 @@ class ServerAPI(object): response = None new_response = None - for _ in range(max_retries): + for retry_idx in reversed(range(max_retries)): try: response = function(url, **kwargs) break except ConnectionRefusedError: + if retry_idx == 0: + self.log.warning( + "Connection error happened.", exc_info=True + ) + # Server may be restarting new_response = RestApiResponse( None, {"detail": "Unable to connect the server. Connection refused"} ) + except requests.exceptions.Timeout: # Connection timed out new_response = RestApiResponse( None, {"detail": "Connection timed out."} ) + except requests.exceptions.ConnectionError: - # Other connection error (ssl, etc) - does not make sense to - # try call server again + # Log warning only on last attempt + if retry_idx == 0: + self.log.warning( + "Connection error happened.", exc_info=True + ) + new_response = RestApiResponse( None, {"detail": "Unable to connect the server. Connection error"} ) - break time.sleep(0.1) @@ -1349,7 +1349,9 @@ class ServerAPI(object): status=None, description=None, summary=None, - payload=None + payload=None, + progress=None, + retries=None ): kwargs = { key: value @@ -1360,9 +1362,27 @@ class ServerAPI(object): ("description", description), ("summary", summary), ("payload", payload), + ("progress", progress), + ("retries", retries), ) if value is not None } + # 'progress' and 'retries' are available since 0.5.x server version + major, minor, _, _, _ = self.server_version_tuple + if (major, minor) < (0, 5): + args = [] + if progress is not None: + args.append("progress") + if retries is not None: + args.append("retries") + fields = ", ".join("'{}'".format(f) for f in args) + ending = "s" if len(args) > 1 else "" + raise ValueError(( + "Your server version '{}' does not support update" + " of {} field{} on event. The fields are supported since" + " server version '0.5'." + ).format(self.get_server_version(), fields, ending)) + response = self.patch( "events/{}".format(event_id), **kwargs @@ -1434,6 +1454,7 @@ class ServerAPI(object): description=None, sequential=None, events_filter=None, + max_retries=None, ): """Enroll job based on events. @@ -1475,8 +1496,12 @@ class ServerAPI(object): in target event. sequential (Optional[bool]): The source topic must be processed in sequence. - events_filter (Optional[ayon_server.sqlfilter.Filter]): A dict-like - with conditions to filter the source event. + events_filter (Optional[dict[str, Any]]): Filtering conditions + to filter the source event. For more technical specifications + look to server backed 'ayon_server.sqlfilter.Filter'. + TODO: Add example of filters. + max_retries (Optional[int]): How many times can be event retried. + Default value is based on server (3 at the time of this PR). Returns: Union[None, dict[str, Any]]: None if there is no event matching @@ -1487,6 +1512,7 @@ class ServerAPI(object): "sourceTopic": source_topic, "targetTopic": target_topic, "sender": sender, + "maxRetries": max_retries, } if sequential is not None: kwargs["sequential"] = sequential @@ -2236,6 +2262,34 @@ class ServerAPI(object): response.raise_for_status("Failed to create/update dependency") return response.data + def _get_dependency_package_route( + self, filename=None, platform_name=None + ): + major, minor, patch, _, _ = self.server_version_tuple + if (major, minor, patch) <= (0, 2, 0): + # Backwards compatibility for AYON server 0.2.0 and lower + self.log.warning(( + "Using deprecated dependency package route." + " Please update your AYON server to version 0.2.1 or higher." + " Backwards compatibility for this route will be removed" + " in future releases of ayon-python-api." + )) + if platform_name is None: + platform_name = platform.system().lower() + base = "dependencies" + if not filename: + return base + return "{}/{}/{}".format(base, filename, platform_name) + + if (major, minor) <= (0, 3): + endpoint = "desktop/dependency_packages" + else: + endpoint = "desktop/dependencyPackages" + + if filename: + return "{}/{}".format(endpoint, filename) + return endpoint + def get_dependency_packages(self): """Information about dependency packages on server. @@ -2263,33 +2317,11 @@ class ServerAPI(object): server. """ - endpoint = "desktop/dependencyPackages" - major, minor, _, _, _ = self.server_version_tuple - if major == 0 and minor <= 3: - endpoint = "desktop/dependency_packages" - + endpoint = self._get_dependency_package_route() result = self.get(endpoint) result.raise_for_status() return result.data - def _get_dependency_package_route( - self, filename=None, platform_name=None - ): - major, minor, patch, _, _ = self.server_version_tuple - if major == 0 and (minor > 2 or (minor == 2 and patch >= 1)): - base = "desktop/dependency_packages" - if not filename: - return base - return "{}/{}".format(base, filename) - - # Backwards compatibility for AYON server 0.2.0 and lower - if platform_name is None: - platform_name = platform.system().lower() - base = "dependencies" - if not filename: - return base - return "{}/{}/{}".format(base, filename, platform_name) - def create_dependency_package( self, filename, @@ -3515,7 +3547,9 @@ class ServerAPI(object): folder_ids=None, folder_paths=None, folder_names=None, + folder_types=None, parent_ids=None, + statuses=None, active=True, fields=None, own_attributes=False @@ -3536,8 +3570,12 @@ class ServerAPI(object): for filtering. folder_names (Optional[Iterable[str]]): Folder names used for filtering. + folder_types (Optional[Iterable[str]]): Folder types used + for filtering. parent_ids (Optional[Iterable[str]]): Ids of folder parents. Use 'None' if folder is direct child of project. + statuses (Optional[Iterable[str]]): Folder statuses used + for filtering. active (Optional[bool]): Filter active/inactive folders. Both are returned if is set to None. fields (Optional[Iterable[str]]): Fields to be queried for @@ -3574,6 +3612,18 @@ class ServerAPI(object): return filters["folderNames"] = list(folder_names) + if folder_types is not None: + folder_types = set(folder_types) + if not folder_types: + return + filters["folderTypes"] = list(folder_types) + + if statuses is not None: + statuses = set(statuses) + if not statuses: + return + filters["folderStatuses"] = list(statuses) + if parent_ids is not None: parent_ids = set(parent_ids) if not parent_ids: @@ -4312,9 +4362,6 @@ class ServerAPI(object): fields.remove("attrib") fields |= self.get_attributes_fields_for_type("version") - if active is not None: - fields.add("active") - # Make sure fields have minimum required fields fields |= {"id", "version"} @@ -4323,6 +4370,9 @@ class ServerAPI(object): use_rest = True fields = {"id"} + if active is not None: + fields.add("active") + if own_attributes: fields.add("ownAttrib") @@ -5845,19 +5895,22 @@ class ServerAPI(object): """Helper method to get links from server for entity types. Example output: - [ - { - "id": "59a212c0d2e211eda0e20242ac120002", - "linkType": "reference", - "description": "reference link between folders", - "projectName": "my_project", - "author": "frantadmin", - "entityId": "b1df109676db11ed8e8c6c9466b19aa8", - "entityType": "folder", - "direction": "out" - }, + { + "59a212c0d2e211eda0e20242ac120001": [ + { + "id": "59a212c0d2e211eda0e20242ac120002", + "linkType": "reference", + "description": "reference link between folders", + "projectName": "my_project", + "author": "frantadmin", + "entityId": "b1df109676db11ed8e8c6c9466b19aa8", + "entityType": "folder", + "direction": "out" + }, + ... + ], ... - ] + } Args: project_name (str): Project where links are. diff --git a/openpype/vendor/python/common/ayon_api/utils.py b/openpype/vendor/python/common/ayon_api/utils.py index 314d13faec..502d24f713 100644 --- a/openpype/vendor/python/common/ayon_api/utils.py +++ b/openpype/vendor/python/common/ayon_api/utils.py @@ -1,3 +1,4 @@ +import os import re import datetime import uuid @@ -15,6 +16,7 @@ except ImportError: import requests import unidecode +from .constants import SERVER_TIMEOUT_ENV_KEY from .exceptions import UrlError REMOVED_VALUE = object() @@ -27,6 +29,23 @@ RepresentationParents = collections.namedtuple( ) +def get_default_timeout(): + """Default value for requests timeout. + + First looks for environment variable SERVER_TIMEOUT_ENV_KEY which + can affect timeout value. If not available then use 10.0 s. + + Returns: + float: Timeout value in seconds. + """ + + try: + return float(os.environ.get(SERVER_TIMEOUT_ENV_KEY)) + except (ValueError, TypeError): + pass + return 10.0 + + class ThumbnailContent: """Wrapper for thumbnail content. @@ -231,30 +250,36 @@ def _try_parse_url(url): return None -def _try_connect_to_server(url): +def _try_connect_to_server(url, timeout=None): + if timeout is None: + timeout = get_default_timeout() try: # TODO add validation if the url lead to Ayon server - # - thiw won't validate if the url lead to 'google.com' - requests.get(url) + # - this won't validate if the url lead to 'google.com' + requests.get(url, timeout=timeout) except BaseException: return False return True -def login_to_server(url, username, password): +def login_to_server(url, username, password, timeout=None): """Use login to the server to receive token. Args: url (str): Server url. username (str): User's username. password (str): User's password. + timeout (Optional[float]): Timeout for request. Value from + 'get_default_timeout' is used if not specified. Returns: Union[str, None]: User's token if login was successfull. Otherwise 'None'. """ + if timeout is None: + timeout = get_default_timeout() headers = {"Content-Type": "application/json"} response = requests.post( "{}/api/auth/login".format(url), @@ -262,7 +287,8 @@ def login_to_server(url, username, password): json={ "name": username, "password": password - } + }, + timeout=timeout, ) token = None # 200 - success @@ -273,47 +299,67 @@ def login_to_server(url, username, password): return token -def logout_from_server(url, token): +def logout_from_server(url, token, timeout=None): """Logout from server and throw token away. Args: url (str): Url from which should be logged out. token (str): Token which should be used to log out. + timeout (Optional[float]): Timeout for request. Value from + 'get_default_timeout' is used if not specified. """ + if timeout is None: + timeout = get_default_timeout() headers = { "Content-Type": "application/json", "Authorization": "Bearer {}".format(token) } requests.post( url + "/api/auth/logout", - headers=headers + headers=headers, + timeout=timeout, ) -def is_token_valid(url, token): +def is_token_valid(url, token, timeout=None): """Check if token is valid. + Token can be a user token or service api key. + Args: url (str): Server url. token (str): User's token. + timeout (Optional[float]): Timeout for request. Value from + 'get_default_timeout' is used if not specified. Returns: bool: True if token is valid. """ - headers = { + if timeout is None: + timeout = get_default_timeout() + + base_headers = { "Content-Type": "application/json", - "Authorization": "Bearer {}".format(token) } - response = requests.get( - "{}/api/users/me".format(url), - headers=headers - ) - return response.status_code == 200 + for header_value in ( + {"Authorization": "Bearer {}".format(token)}, + {"X-Api-Key": token}, + ): + headers = base_headers.copy() + headers.update(header_value) + response = requests.get( + "{}/api/users/me".format(url), + headers=headers, + timeout=timeout, + ) + if response.status_code == 200: + return True + return False -def validate_url(url): +def validate_url(url, timeout=None): """Validate url if is valid and server is available. Validation checks if can be parsed as url and contains scheme. @@ -334,6 +380,7 @@ def validate_url(url): Args: url (str): Server url. + timeout (Optional[int]): Timeout in seconds for connection to server. Returns: Url which was used to connect to server. @@ -369,10 +416,10 @@ def validate_url(url): # - this will trigger UrlError if both will crash if not parsed_url.scheme: new_url = "https://" + modified_url - if _try_connect_to_server(new_url): + if _try_connect_to_server(new_url, timeout=timeout): return new_url - if _try_connect_to_server(modified_url): + if _try_connect_to_server(modified_url, timeout=timeout): return modified_url hints = [] diff --git a/openpype/vendor/python/common/ayon_api/version.py b/openpype/vendor/python/common/ayon_api/version.py index f3826a6407..ac4f32997f 100644 --- a/openpype/vendor/python/common/ayon_api/version.py +++ b/openpype/vendor/python/common/ayon_api/version.py @@ -1,2 +1,2 @@ """Package declaring Python API for Ayon server.""" -__version__ = "0.4.1" +__version__ = "0.5.1" From a704fd44e8731cbe0ee17b042a5cc4808f87b2d9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 18 Oct 2023 10:45:52 +0200 Subject: [PATCH 11/22] ignore some predefined names to import --- openpype/modules/base.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 6f3e4566f3..080be251f3 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -434,8 +434,18 @@ def _load_ayon_addons(openpype_modules, modules_key, log): sys.path.insert(0, addon_dir) imported_modules = [] for name in os.listdir(addon_dir): + # Ignore of files is implemented to be able to run code from code + # where usually is more files than just the addon + # Ignore start and setup scripts + if name in ("setup.py", "start.py"): + continue + path = os.path.join(addon_dir, name) basename, ext = os.path.splitext(name) + # Ignore folders/files with dot in name + # - dot names cannot be imported in Python + if "." in basename: + continue is_dir = os.path.isdir(path) is_py_file = ext.lower() == ".py" if not is_py_file and not is_dir: From b20f59e87ed1c98f678b136ee011918bb54e9b7b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 18 Oct 2023 11:53:50 +0200 Subject: [PATCH 12/22] formatting fix --- openpype/settings/ayon_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/ayon_settings.py b/openpype/settings/ayon_settings.py index eb64480dc3..7d4675c0f3 100644 --- a/openpype/settings/ayon_settings.py +++ b/openpype/settings/ayon_settings.py @@ -1439,8 +1439,8 @@ class _AyonSettingsCache: and bundle.get("activeUser") == username ): return bundle["name"] - # Return fake variant - distribution logic will tell user that he does not - # have set any dev bundle + # Return fake variant - distribution logic will tell user that he + # does not have set any dev bundle return "dev" @classmethod From 65cfa7751c2b9805ff7e60bb24e6dae7c97e81be Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 18 Oct 2023 12:22:10 +0200 Subject: [PATCH 13/22] added disk mapping settings to core addon --- server_addon/core/server/settings/main.py | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/server_addon/core/server/settings/main.py b/server_addon/core/server/settings/main.py index ca8f7e63ed..433d0ef2f0 100644 --- a/server_addon/core/server/settings/main.py +++ b/server_addon/core/server/settings/main.py @@ -12,6 +12,27 @@ from .publish_plugins import PublishPuginsModel, DEFAULT_PUBLISH_VALUES from .tools import GlobalToolsModel, DEFAULT_TOOLS_VALUES +class DiskMappingItemModel(BaseSettingsModel): + _layout = "expanded" + source: str = Field("", title="Source") + destination: str = Field("", title="Destination") + + +class DiskMappingModel(BaseSettingsModel): + windows: list[DiskMappingItemModel] = Field( + title="Windows", + default_factory=list, + ) + linux: list[DiskMappingItemModel] = Field( + title="Linux", + default_factory=list, + ) + darwin: list[DiskMappingItemModel] = Field( + title="MacOS", + default_factory=list, + ) + + class ImageIOFileRuleModel(BaseSettingsModel): name: str = Field("", title="Rule name") pattern: str = Field("", title="Regex pattern") @@ -97,6 +118,10 @@ class CoreSettings(BaseSettingsModel): widget="textarea", scope=["studio"], ) + disk_mapping: DiskMappingModel = Field( + default_factory=DiskMappingModel, + title="Disk mapping", + ) tools: GlobalToolsModel = Field( default_factory=GlobalToolsModel, title="Tools" From 105720ff0d3f999a735cfaeb0783997c13131b4e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 18 Oct 2023 12:55:17 +0200 Subject: [PATCH 14/22] add dev icons --- openpype/resources/__init__.py | 7 ++++++- openpype/resources/icons/AYON_icon_dev.png | Bin 0 -> 17344 bytes openpype/resources/icons/AYON_splash_dev.png | Bin 0 -> 21796 bytes 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 openpype/resources/icons/AYON_icon_dev.png create mode 100644 openpype/resources/icons/AYON_splash_dev.png diff --git a/openpype/resources/__init__.py b/openpype/resources/__init__.py index b8671f517a..b33d1bf023 100644 --- a/openpype/resources/__init__.py +++ b/openpype/resources/__init__.py @@ -55,6 +55,9 @@ def get_openpype_staging_icon_filepath(): def get_openpype_icon_filepath(staging=None): + if AYON_SERVER_ENABLED and os.getenv("AYON_USE_DEV") == "1": + return get_resource("icons", "AYON_icon_dev.png") + if staging is None: staging = is_running_staging() @@ -68,7 +71,9 @@ def get_openpype_splash_filepath(staging=None): staging = is_running_staging() if AYON_SERVER_ENABLED: - if staging: + if os.getenv("AYON_USE_DEV") == "1": + splash_file_name = "AYON_splash_dev.png" + elif staging: splash_file_name = "AYON_splash_staging.png" else: splash_file_name = "AYON_splash.png" diff --git a/openpype/resources/icons/AYON_icon_dev.png b/openpype/resources/icons/AYON_icon_dev.png new file mode 100644 index 0000000000000000000000000000000000000000..e99a64d475d1e5841e4994bcc7705aea8202b0fd GIT binary patch literal 17344 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 V7AFPXfnNdvX=~`BO0HQw|9|wzLIwZ; literal 0 HcmV?d00001 diff --git a/openpype/resources/icons/AYON_splash_dev.png b/openpype/resources/icons/AYON_splash_dev.png new file mode 100644 index 0000000000000000000000000000000000000000..3b366e755d841bde1b82b6e5c40457adff4d39dc GIT binary patch literal 21796 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 y7sY-0mXdy4=HZ|fKIJ9}0RQi Date: Wed, 18 Oct 2023 14:08:57 +0200 Subject: [PATCH 15/22] use white background icon --- openpype/resources/icons/AYON_icon_dev.png | Bin 17344 -> 15928 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/openpype/resources/icons/AYON_icon_dev.png b/openpype/resources/icons/AYON_icon_dev.png index e99a64d475d1e5841e4994bcc7705aea8202b0fd..3e867ef372db7e861663e672e1dd93e72ee8c7e1 100644 GIT binary patch literal 15928 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 literal 17344 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 V7AFPXfnNdvX=~`BO0HQw|9|wzLIwZ; From 0e22a2ef3cdb32ee013bd0ea51c4685768d53997 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 18 Oct 2023 19:25:32 +0200 Subject: [PATCH 16/22] Remove `setParms` call since it's responsibility of `self.imprint` to set the values --- openpype/hosts/houdini/api/plugin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index d79ccc71bd..72565f7211 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -250,14 +250,12 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): key: changes[key].new_value for key in changes.changed_keys } - # Update ParmTemplates + # Update parm templates and values self.imprint( instance_node, new_values, update=True ) - # Update values - instance_node.setParms(new_values) def imprint(self, node, values, update=False): # Never store instance node and instance id since that data comes From a2c5934a1e2c9b88d445762a623647d8c80fdc08 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 18 Oct 2023 22:14:13 +0200 Subject: [PATCH 17/22] Fix updating parms of same name that user had taken off of default value manually + refactor deprecated `Node.replaceSpareParmTuple` to use `ParmTemplateGroup.replace` instead --- openpype/hosts/houdini/api/lib.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py index f258dda36e..2440ded6ad 100644 --- a/openpype/hosts/houdini/api/lib.py +++ b/openpype/hosts/houdini/api/lib.py @@ -369,16 +369,18 @@ def imprint(node, data, update=False): # is for some reason lost every call to `appendToFolder()` parm_folder = parm_group.findFolder("Extra") + for parm_template in update_parms: + parm_group.replace(parm_template.name(), parm_template) + + # When replacing a parm with a parm of the same name it preserves its + # value if before the replacement the parm was not at the default, + # because it has a value override set. Since we're trying to update the + # parm by using the new value as `default` we enforce the parm is at + # default state + node.parm(parm_template.name()).revertToDefaults() + node.setParmTemplateGroup(parm_group) - # TODO: Updating is done here, by calling probably deprecated functions. - # This needs to be addressed in the future. - if not update_parms: - return - - for parm in update_parms: - node.replaceSpareParmTuple(parm.name(), parm) - def lsattr(attr, value=None, root="/"): """Return nodes that have `attr` From e001b2632a69a32bcc07dbdf5d16c5ed70927bbd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 18 Oct 2023 22:16:18 +0200 Subject: [PATCH 18/22] Refactor `parm` to `parm_template` to clarify variable refers to a parm template --- openpype/hosts/houdini/api/lib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py index 2440ded6ad..a777d15581 100644 --- a/openpype/hosts/houdini/api/lib.py +++ b/openpype/hosts/houdini/api/lib.py @@ -339,7 +339,7 @@ def imprint(node, data, update=False): if value is None: continue - parm = get_template_from_value(key, value) + parm_template = get_template_from_value(key, value) if key in current_parms: if node.evalParm(key) == data[key]: @@ -348,10 +348,10 @@ def imprint(node, data, update=False): log.debug(f"{key} already exists on {node}") else: log.debug(f"replacing {key}") - update_parms.append(parm) + update_parms.append(parm_template) continue - templates.append(parm) + templates.append(parm_template) parm_group = node.parmTemplateGroup() parm_folder = parm_group.findFolder("Extra") From f93e27ac41fbcbe20e4cb1d14034ef781eaedbc4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 18 Oct 2023 22:23:10 +0200 Subject: [PATCH 19/22] Simplify logic --- openpype/hosts/houdini/api/lib.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py index a777d15581..7263a79e53 100644 --- a/openpype/hosts/houdini/api/lib.py +++ b/openpype/hosts/houdini/api/lib.py @@ -363,11 +363,10 @@ def imprint(node, data, update=False): parm_folder.setParmTemplates(templates) parm_group.append(parm_folder) else: + # Add to parm folder instance, then replace with updated one in group for template in templates: - parm_group.appendToFolder(parm_folder, template) - # this is needed because the pointer to folder - # is for some reason lost every call to `appendToFolder()` - parm_folder = parm_group.findFolder("Extra") + parm_folder.addParmTemplate(template) + parm_group.replace(parm_folder.name(), parm_folder) for parm_template in update_parms: parm_group.replace(parm_template.name(), parm_template) From b2a86e22d053986630fb7173ede638bc4ff8d57d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 18 Oct 2023 22:26:48 +0200 Subject: [PATCH 20/22] Simplify --- openpype/hosts/houdini/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py index 7263a79e53..a91da396ec 100644 --- a/openpype/hosts/houdini/api/lib.py +++ b/openpype/hosts/houdini/api/lib.py @@ -342,7 +342,7 @@ def imprint(node, data, update=False): parm_template = get_template_from_value(key, value) if key in current_parms: - if node.evalParm(key) == data[key]: + if node.evalParm(key) == value: continue if not update: log.debug(f"{key} already exists on {node}") From 3506c810b17916ef93c12e84d69737773f1939d8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 18 Oct 2023 22:31:49 +0200 Subject: [PATCH 21/22] Refactor variable names to refer to parm templates as opposed to parms + do nothing if both no new and no update parms to process --- openpype/hosts/houdini/api/lib.py | 42 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py index a91da396ec..3031e2d2bd 100644 --- a/openpype/hosts/houdini/api/lib.py +++ b/openpype/hosts/houdini/api/lib.py @@ -332,8 +332,8 @@ def imprint(node, data, update=False): return current_parms = {p.name(): p for p in node.spareParms()} - update_parms = [] - templates = [] + update_parm_templates = [] + new_parm_templates = [] for key, value in data.items(): if value is None: @@ -348,27 +348,35 @@ def imprint(node, data, update=False): log.debug(f"{key} already exists on {node}") else: log.debug(f"replacing {key}") - update_parms.append(parm_template) + update_parm_templates.append(parm_template) continue - templates.append(parm_template) + new_parm_templates.append(parm_template) + + if not new_parm_templates and not update_parm_templates: + return parm_group = node.parmTemplateGroup() - parm_folder = parm_group.findFolder("Extra") - # if folder doesn't exist yet, create one and append to it, - # else append to existing one - if not parm_folder: - parm_folder = hou.FolderParmTemplate("folder", "Extra") - parm_folder.setParmTemplates(templates) - parm_group.append(parm_folder) - else: - # Add to parm folder instance, then replace with updated one in group - for template in templates: - parm_folder.addParmTemplate(template) - parm_group.replace(parm_folder.name(), parm_folder) + # Add new parm templates + if new_parm_templates: + parm_folder = parm_group.findFolder("Extra") - for parm_template in update_parms: + # if folder doesn't exist yet, create one and append to it, + # else append to existing one + if not parm_folder: + parm_folder = hou.FolderParmTemplate("folder", "Extra") + parm_folder.setParmTemplates(new_parm_templates) + parm_group.append(parm_folder) + else: + # Add to parm template folder instance then replace with updated + # one in parm template group + for template in new_parm_templates: + parm_folder.addParmTemplate(template) + parm_group.replace(parm_folder.name(), parm_folder) + + # Update existing parm templates + for parm_template in update_parm_templates: parm_group.replace(parm_template.name(), parm_template) # When replacing a parm with a parm of the same name it preserves its From e3111239a0e3e0b6489f1d9daac546e49ba0c849 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:48:57 +0200 Subject: [PATCH 22/22] trigger 'selection_changed' signal to propagate selection change (#5793) --- openpype/tools/publisher/widgets/card_view_widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/card_view_widgets.py b/openpype/tools/publisher/widgets/card_view_widgets.py index eae8e0420a..5cdd429cd4 100644 --- a/openpype/tools/publisher/widgets/card_view_widgets.py +++ b/openpype/tools/publisher/widgets/card_view_widgets.py @@ -797,6 +797,7 @@ class InstanceCardView(AbstractInstanceView): widget.set_active(value) else: self._select_item_clear(instance_id, group_name, instance_widget) + self.selection_changed.emit() self.active_changed.emit() def _on_widget_selection(self, instance_id, group_name, selection_type):