From f45caa0e4c3f97c8efce0234b5bbb68e2a32f12c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:21:51 +0200 Subject: [PATCH 1/4] Fix 'ownAttrib' not always returned with 'own_attributes' - 'get_tasks' and 'get_folders' prepare fields also when default fields are used, so 'ownAttrib' is queried. - 'get_tasks_by_folder_paths' passes 'own_attributes' to 'get_tasks'. - Attribute values are queried for folders and tasks when own attributes are requested, otherwise 'ownAttrib' stayed a list of names. - 'get_project' and 'get_projects' query attributes when own attributes are requested, so the REST projects list is not used. - 'fill_own_attribs' handles empty 'attrib'. Co-Authored-By: Claude Opus 5.5 --- ayon_api/_api_helpers/folders.py | 5 +---- ayon_api/_api_helpers/projects.py | 4 ++++ ayon_api/_api_helpers/tasks.py | 4 ++-- ayon_api/server_api.py | 2 ++ ayon_api/utils.py | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ayon_api/_api_helpers/folders.py b/ayon_api/_api_helpers/folders.py index d10cfeb7a..dc0ddfce2 100644 --- a/ayon_api/_api_helpers/folders.py +++ b/ayon_api/_api_helpers/folders.py @@ -323,14 +323,11 @@ def get_folders( fields = self.get_default_fields_for_type("folder") else: fields = set(fields) - self._prepare_fields("folder", fields) + self._prepare_fields("folder", fields, own_attributes) if active is not None: fields.add("active") - if own_attributes: - fields.add("ownAttrib") - self._prepare_link_fields(fields) query = folders_graphql_query(fields) diff --git a/ayon_api/_api_helpers/projects.py b/ayon_api/_api_helpers/projects.py index df602d808..7b9174306 100644 --- a/ayon_api/_api_helpers/projects.py +++ b/ayon_api/_api_helpers/projects.py @@ -298,6 +298,8 @@ def get_projects( """ if fields is not None: fields = set(fields) + if own_attributes: + fields.add("attrib") graphql_fields, fetch_type = self._get_project_graphql_fields(fields) if fetch_type == ProjectFetchType.RESTList: @@ -358,6 +360,8 @@ def get_project( """ if fields is not None: fields = set(fields) + if own_attributes: + fields.add("attrib") graphql_fields, fetch_type = self._get_project_graphql_fields(fields) graphql_project = None diff --git a/ayon_api/_api_helpers/tasks.py b/ayon_api/_api_helpers/tasks.py index 1cae5caa5..9ab098bf7 100644 --- a/ayon_api/_api_helpers/tasks.py +++ b/ayon_api/_api_helpers/tasks.py @@ -98,7 +98,7 @@ def get_tasks( fields = self.get_default_fields_for_type("task") else: fields = set(fields) - self._prepare_fields("task", fields, own_attributes) + self._prepare_fields("task", fields, own_attributes) if active is not None: fields.add("active") @@ -253,7 +253,6 @@ def get_tasks_by_folder_paths( fields = self.get_default_fields_for_type("task") else: fields = set(fields) - self._prepare_fields("task", fields, own_attributes) fields.add("folderId") @@ -269,6 +268,7 @@ def get_tasks_by_folder_paths( active=active, filters=filters, fields=fields, + own_attributes=own_attributes, ): folder_id = task_entity["folderId"] folder_path = folder_path_by_id[folder_id] diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index 7cb03c4d4..8c6b85f64 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -2842,6 +2842,8 @@ def _prepare_fields( if entity_type == "project": add_all_attrib = True elif entity_type in {"folder", "task"}: + # Attribute values are needed to fill 'ownAttrib' + add_all_attrib = True fields.add("ownAttrib") if add_all_attrib: diff --git a/ayon_api/utils.py b/ayon_api/utils.py index f5769e7d8..714c98e6a 100644 --- a/ayon_api/utils.py +++ b/ayon_api/utils.py @@ -276,7 +276,7 @@ def fill_own_attribs(entity: AnyEntityDict) -> None: entity (dict): Entity dictionary. """ - if not entity or not entity.get("attrib"): + if not entity or entity.get("attrib") is None: return attributes = entity.get("ownAttrib") From 35e2b38fb5a1a5ce260753a9f0eca14be268b78d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:13:47 +0100 Subject: [PATCH 2/4] Convert user 'ownAttrib' to dictionary only with 'own_attributes' 'get_users', 'get_user_by_name' and 'get_user' have 'own_attributes' argument. Without it 'ownAttrib' is a list of attribute names, as for projects, folders and tasks. Co-Authored-By: Claude Opus 5.5 --- ayon_api/_api.py | 15 +++++++++++++++ ayon_api/_api_helpers/base.py | 4 +++- ayon_api/server_api.py | 26 +++++++++++++++++++++----- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/ayon_api/_api.py b/ayon_api/_api.py index 444f085af..13090066b 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -801,6 +801,7 @@ def get_users( usernames: Iterable[str] | None = None, emails: Iterable[str] | None = None, fields: Iterable[str] | None = None, + own_attributes: bool = False, ) -> Generator[dict[str, Any], None, None]: """Get Users. @@ -813,6 +814,9 @@ def get_users( emails (Iterable[str] | None): Filter by emails. fields (Iterable[str] | None): Fields to be queried for users. + own_attributes (bool): Attribute values that are + not explicitly set on user will have 'None' value + in 'ownAttrib'. Returns: Generator[dict[str, Any], None, None]: Queried users. @@ -824,6 +828,7 @@ def get_users( usernames=usernames, emails=emails, fields=fields, + own_attributes=own_attributes, ) @@ -831,6 +836,7 @@ def get_user_by_name( username: str, project_name: str | None = None, fields: Iterable[str] | None = None, + own_attributes: bool = False, ) -> dict[str, Any] | None: """Get user by name using GraphQl. @@ -841,6 +847,9 @@ def get_user_by_name( username (str): Username. project_name (str | None): Define scope of project. fields (Iterable[str] | None): Fields to be queried for users. + own_attributes (bool): Attribute values that are + not explicitly set on user will have 'None' value + in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not found. @@ -851,11 +860,13 @@ def get_user_by_name( username=username, project_name=project_name, fields=fields, + own_attributes=own_attributes, ) def get_user( username: str | None = None, + own_attributes: bool = False, ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -863,6 +874,9 @@ def get_user( Args: username (str | None): Username. + own_attributes (bool): Attribute values that are + not explicitly set on user will have 'None' value + in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not @@ -872,6 +886,7 @@ def get_user( con = get_server_api_connection() return con.get_user( username=username, + own_attributes=own_attributes, ) diff --git a/ayon_api/_api_helpers/base.py b/ayon_api/_api_helpers/base.py index d852a95b4..6110b5e0d 100644 --- a/ayon_api/_api_helpers/base.py +++ b/ayon_api/_api_helpers/base.py @@ -129,7 +129,9 @@ def get_project( raise NotImplementedError() def get_user( - self, username: Optional[str] = None + self, + username: Optional[str] = None, + own_attributes: bool = False, ) -> Optional[dict[str, Any]]: raise NotImplementedError() diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index fface69a0..2b00d3879 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -1127,6 +1127,7 @@ def get_users( usernames: Iterable[str] | None = None, emails: Iterable[str] | None = None, fields: Iterable[str] | None = None, + own_attributes: bool = False, ) -> Generator[dict[str, Any], None, None]: """Get Users. @@ -1139,6 +1140,9 @@ def get_users( emails (Iterable[str] | None): Filter by emails. fields (Iterable[str] | None): Fields to be queried for users. + own_attributes (bool): Attribute values that are + not explicitly set on user will have 'None' value + in 'ownAttrib'. Returns: Generator[dict[str, Any], None, None]: Queried users. @@ -1195,14 +1199,15 @@ def get_users( attrib = json.loads(attrib) if attrib is not None: - own_attrib = copy.deepcopy(attrib) - user["ownAttrib"] = own_attrib + user["ownAttrib"] = list(attrib) for name, attr_data in attributes.items(): attrib.setdefault(name, attr_data["default"]) - own_attrib.setdefault(name, None) user["attrib"] = attrib + if own_attributes: + fill_own_attribs(user) + yield user def get_user_by_name( @@ -1210,6 +1215,7 @@ def get_user_by_name( username: str, project_name: str | None = None, fields: Iterable[str] | None = None, + own_attributes: bool = False, ) -> dict[str, Any] | None: """Get user by name using GraphQl. @@ -1220,6 +1226,9 @@ def get_user_by_name( username (str): Username. project_name (str | None): Define scope of project. fields (Iterable[str] | None): Fields to be queried for users. + own_attributes (bool): Attribute values that are + not explicitly set on user will have 'None' value + in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not found. @@ -1232,12 +1241,15 @@ def get_user_by_name( project_name=project_name, usernames={username}, fields=fields, + own_attributes=own_attributes, ): return user return None def get_user( - self, username: str | None = None + self, + username: str | None = None, + own_attributes: bool = False, ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -1245,6 +1257,9 @@ def get_user( Args: username (str | None): Username. + own_attributes (bool): Attribute values that are + not explicitly set on user will have 'None' value + in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not @@ -1264,7 +1279,8 @@ def get_user( for attr_name, attr_data in attributes.items(): user["attrib"].setdefault(attr_name, attr_data["default"]) - fill_own_attribs(user) + if own_attributes: + fill_own_attribs(user) return user def get_headers( From 734679bd6d2aff31d51e64a7ad6824212737dfde Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:23:49 +0100 Subject: [PATCH 3/4] Revert user 'ownAttrib' conversion change Users keep 'ownAttrib' as dictionary, code in other addons relies on it. This reverts commit 35e2b38fb5. Co-Authored-By: Claude Opus 5.5 --- ayon_api/_api.py | 15 --------------- ayon_api/_api_helpers/base.py | 4 +--- ayon_api/server_api.py | 26 +++++--------------------- 3 files changed, 6 insertions(+), 39 deletions(-) diff --git a/ayon_api/_api.py b/ayon_api/_api.py index 13090066b..444f085af 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -801,7 +801,6 @@ def get_users( usernames: Iterable[str] | None = None, emails: Iterable[str] | None = None, fields: Iterable[str] | None = None, - own_attributes: bool = False, ) -> Generator[dict[str, Any], None, None]: """Get Users. @@ -814,9 +813,6 @@ def get_users( emails (Iterable[str] | None): Filter by emails. fields (Iterable[str] | None): Fields to be queried for users. - own_attributes (bool): Attribute values that are - not explicitly set on user will have 'None' value - in 'ownAttrib'. Returns: Generator[dict[str, Any], None, None]: Queried users. @@ -828,7 +824,6 @@ def get_users( usernames=usernames, emails=emails, fields=fields, - own_attributes=own_attributes, ) @@ -836,7 +831,6 @@ def get_user_by_name( username: str, project_name: str | None = None, fields: Iterable[str] | None = None, - own_attributes: bool = False, ) -> dict[str, Any] | None: """Get user by name using GraphQl. @@ -847,9 +841,6 @@ def get_user_by_name( username (str): Username. project_name (str | None): Define scope of project. fields (Iterable[str] | None): Fields to be queried for users. - own_attributes (bool): Attribute values that are - not explicitly set on user will have 'None' value - in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not found. @@ -860,13 +851,11 @@ def get_user_by_name( username=username, project_name=project_name, fields=fields, - own_attributes=own_attributes, ) def get_user( username: str | None = None, - own_attributes: bool = False, ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -874,9 +863,6 @@ def get_user( Args: username (str | None): Username. - own_attributes (bool): Attribute values that are - not explicitly set on user will have 'None' value - in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not @@ -886,7 +872,6 @@ def get_user( con = get_server_api_connection() return con.get_user( username=username, - own_attributes=own_attributes, ) diff --git a/ayon_api/_api_helpers/base.py b/ayon_api/_api_helpers/base.py index 6110b5e0d..d852a95b4 100644 --- a/ayon_api/_api_helpers/base.py +++ b/ayon_api/_api_helpers/base.py @@ -129,9 +129,7 @@ def get_project( raise NotImplementedError() def get_user( - self, - username: Optional[str] = None, - own_attributes: bool = False, + self, username: Optional[str] = None ) -> Optional[dict[str, Any]]: raise NotImplementedError() diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index 2b00d3879..fface69a0 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -1127,7 +1127,6 @@ def get_users( usernames: Iterable[str] | None = None, emails: Iterable[str] | None = None, fields: Iterable[str] | None = None, - own_attributes: bool = False, ) -> Generator[dict[str, Any], None, None]: """Get Users. @@ -1140,9 +1139,6 @@ def get_users( emails (Iterable[str] | None): Filter by emails. fields (Iterable[str] | None): Fields to be queried for users. - own_attributes (bool): Attribute values that are - not explicitly set on user will have 'None' value - in 'ownAttrib'. Returns: Generator[dict[str, Any], None, None]: Queried users. @@ -1199,15 +1195,14 @@ def get_users( attrib = json.loads(attrib) if attrib is not None: - user["ownAttrib"] = list(attrib) + own_attrib = copy.deepcopy(attrib) + user["ownAttrib"] = own_attrib for name, attr_data in attributes.items(): attrib.setdefault(name, attr_data["default"]) + own_attrib.setdefault(name, None) user["attrib"] = attrib - if own_attributes: - fill_own_attribs(user) - yield user def get_user_by_name( @@ -1215,7 +1210,6 @@ def get_user_by_name( username: str, project_name: str | None = None, fields: Iterable[str] | None = None, - own_attributes: bool = False, ) -> dict[str, Any] | None: """Get user by name using GraphQl. @@ -1226,9 +1220,6 @@ def get_user_by_name( username (str): Username. project_name (str | None): Define scope of project. fields (Iterable[str] | None): Fields to be queried for users. - own_attributes (bool): Attribute values that are - not explicitly set on user will have 'None' value - in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not found. @@ -1241,15 +1232,12 @@ def get_user_by_name( project_name=project_name, usernames={username}, fields=fields, - own_attributes=own_attributes, ): return user return None def get_user( - self, - username: str | None = None, - own_attributes: bool = False, + self, username: str | None = None ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -1257,9 +1245,6 @@ def get_user( Args: username (str | None): Username. - own_attributes (bool): Attribute values that are - not explicitly set on user will have 'None' value - in 'ownAttrib'. Returns: dict[str, Any] | None: User info or None if user is not @@ -1279,8 +1264,7 @@ def get_user( for attr_name, attr_data in attributes.items(): user["attrib"].setdefault(attr_name, attr_data["default"]) - if own_attributes: - fill_own_attribs(user) + fill_own_attribs(user) return user def get_headers( From 639ed143f8166fad0a31543b6756f215ef49ce36 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:39:15 +0100 Subject: [PATCH 4/4] rever attrib value check --- ayon_api/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ayon_api/utils.py b/ayon_api/utils.py index 714c98e6a..f5769e7d8 100644 --- a/ayon_api/utils.py +++ b/ayon_api/utils.py @@ -276,7 +276,7 @@ def fill_own_attribs(entity: AnyEntityDict) -> None: entity (dict): Entity dictionary. """ - if not entity or entity.get("attrib") is None: + if not entity or not entity.get("attrib"): return attributes = entity.get("ownAttrib")