From d5f04482ecd49e074ddd35039a6c8dda802b92fc Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 2 Nov 2026 14:13:06 +0100 Subject: [PATCH 1/2] Add 'attrib' argument to 'get_user' to skip filling default attributes --- ayon_api/_api.py | 5 +++ ayon_api/_api_helpers/base.py | 1 + ayon_api/_api_helpers/workfiles.py | 2 +- ayon_api/server_api.py | 13 ++++-- tests/test_get_user_attrib.py | 66 ++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 tests/test_get_user_attrib.py diff --git a/ayon_api/_api.py b/ayon_api/_api.py index e7af02f25..9d0bcd13d 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -868,6 +868,7 @@ def get_user_by_name( def get_user( username: str | None = None, own_attributes: bool = False, + attrib: bool = True, ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -878,6 +879,9 @@ def get_user( own_attributes (bool): Fill 'own_attributes' with attribute values that are explicitly set on user, other attributes have 'None' value. + attrib (bool): Fill missing attributes in 'attrib' with their + default values. Requires an additional request for user + attributes. Use 'False' when attributes are not needed. Returns: dict[str, Any] | None: User info or None if user is not @@ -888,6 +892,7 @@ def get_user( return con.get_user( username=username, own_attributes=own_attributes, + attrib=attrib, ) diff --git a/ayon_api/_api_helpers/base.py b/ayon_api/_api_helpers/base.py index 6110b5e0d..953a83873 100644 --- a/ayon_api/_api_helpers/base.py +++ b/ayon_api/_api_helpers/base.py @@ -132,6 +132,7 @@ def get_user( self, username: Optional[str] = None, own_attributes: bool = False, + attrib: bool = True, ) -> Optional[dict[str, Any]]: raise NotImplementedError() diff --git a/ayon_api/_api_helpers/workfiles.py b/ayon_api/_api_helpers/workfiles.py index 67991da54..72506d66f 100644 --- a/ayon_api/_api_helpers/workfiles.py +++ b/ayon_api/_api_helpers/workfiles.py @@ -224,7 +224,7 @@ def create_workfile_entity( major, minor, patch, _, _ = self.get_server_version_tuple() if (major, minor, patch) < (1, 1, 3): - user = self.get_user() + user = self.get_user(attrib=False) username = user["name"] create_data["createdBy"] = username create_data["updatedBy"] = username diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index 41ac0eaf9..d18e39e6a 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -1259,6 +1259,7 @@ def get_user( self, username: str | None = None, own_attributes: bool = False, + attrib: bool = True, ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -1269,6 +1270,9 @@ def get_user( own_attributes (bool): Fill 'own_attributes' with attribute values that are explicitly set on user, other attributes have 'None' value. + attrib (bool): Fill missing attributes in 'attrib' with their + default values. Requires an additional request for user + attributes. Use 'False' when attributes are not needed. Returns: dict[str, Any] | None: User info or None if user is not @@ -1284,9 +1288,10 @@ def get_user( response.raise_for_status() user = response.data - attributes = self.get_attributes_for_type("user") - for attr_name, attr_data in attributes.items(): - user["attrib"].setdefault(attr_name, attr_data["default"]) + if attrib: + attributes = self.get_attributes_for_type("user") + for attr_name, attr_data in attributes.items(): + user["attrib"].setdefault(attr_name, attr_data["default"]) # NOTE 'ownAttrib' is always filled for backwards compatibility fill_own_attribs(user) @@ -1372,7 +1377,7 @@ def login( """ if self.has_valid_token: try: - user_info = self.get_user() + user_info = self.get_user(attrib=False) except UnauthorizedError: user_info = {} diff --git a/tests/test_get_user_attrib.py b/tests/test_get_user_attrib.py new file mode 100644 index 000000000..12fcc520e --- /dev/null +++ b/tests/test_get_user_attrib.py @@ -0,0 +1,66 @@ +"""'get_user' fills default attribute values only if 'attrib' is 'True'. + +Does not require running AYON server. +""" +import pytest + +from ayon_api.server_api import ServerAPI +from ayon_api.utils import RestApiResponse + + +class FakeResponse: + status_code = 200 + + def raise_for_status(self): + pass + + +@pytest.fixture +def con(monkeypatch): + con = ServerAPI("http://localhost:0", create_session=False) + requested = [] + + def get(entrypoint, **kwargs): + requested.append(entrypoint) + return RestApiResponse(FakeResponse(), { + "name": "artist", + "attrib": {"email": "artist@example.com"}, + "ownAttrib": ["email"], + }) + + def get_attributes_for_type(entity_type): + requested.append(f"attributes/{entity_type}") + return { + "email": {"default": None}, + "fullName": {"default": ""}, + } + + monkeypatch.setattr(con, "get", get) + monkeypatch.setattr( + con, "get_attributes_for_type", get_attributes_for_type + ) + con.requested = requested + return con + + +def test_get_user_fills_default_attributes(con): + user = con.get_user("artist") + assert con.requested == ["users/artist", "attributes/user"] + assert user["attrib"] == { + "email": "artist@example.com", + "fullName": "", + } + + +def test_get_user_without_attributes(con): + user = con.get_user("artist", attrib=False) + assert con.requested == ["users/artist"] + assert user["name"] == "artist" + assert user["attrib"] == {"email": "artist@example.com"} + assert "own_attributes" not in user + + +def test_get_user_without_attributes_own_attributes(con): + user = con.get_user("artist", own_attributes=True, attrib=False) + assert con.requested == ["users/artist"] + assert user["own_attributes"] == {"email": "artist@example.com"} From 4b069df71bcc815065e0ab912aa242c490afae0d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 3 Nov 2026 09:28:38 +0100 Subject: [PATCH 2/2] Rename 'attrib' argument of 'get_user' to 'fill_attrib' --- ayon_api/_api.py | 6 +++--- ayon_api/_api_helpers/base.py | 2 +- ayon_api/_api_helpers/workfiles.py | 2 +- ayon_api/server_api.py | 8 ++++---- tests/test_get_user_attrib.py | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ayon_api/_api.py b/ayon_api/_api.py index 9d0bcd13d..acdd838d9 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -868,7 +868,7 @@ def get_user_by_name( def get_user( username: str | None = None, own_attributes: bool = False, - attrib: bool = True, + fill_attrib: bool = True, ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -879,7 +879,7 @@ def get_user( own_attributes (bool): Fill 'own_attributes' with attribute values that are explicitly set on user, other attributes have 'None' value. - attrib (bool): Fill missing attributes in 'attrib' with their + fill_attrib (bool): Fill missing attributes in 'attrib' with their default values. Requires an additional request for user attributes. Use 'False' when attributes are not needed. @@ -892,7 +892,7 @@ def get_user( return con.get_user( username=username, own_attributes=own_attributes, - attrib=attrib, + fill_attrib=fill_attrib, ) diff --git a/ayon_api/_api_helpers/base.py b/ayon_api/_api_helpers/base.py index 953a83873..28b9798c0 100644 --- a/ayon_api/_api_helpers/base.py +++ b/ayon_api/_api_helpers/base.py @@ -132,7 +132,7 @@ def get_user( self, username: Optional[str] = None, own_attributes: bool = False, - attrib: bool = True, + fill_attrib: bool = True, ) -> Optional[dict[str, Any]]: raise NotImplementedError() diff --git a/ayon_api/_api_helpers/workfiles.py b/ayon_api/_api_helpers/workfiles.py index 72506d66f..0278c60f7 100644 --- a/ayon_api/_api_helpers/workfiles.py +++ b/ayon_api/_api_helpers/workfiles.py @@ -224,7 +224,7 @@ def create_workfile_entity( major, minor, patch, _, _ = self.get_server_version_tuple() if (major, minor, patch) < (1, 1, 3): - user = self.get_user(attrib=False) + user = self.get_user(fill_attrib=False) username = user["name"] create_data["createdBy"] = username create_data["updatedBy"] = username diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index d18e39e6a..999a2f55b 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -1259,7 +1259,7 @@ def get_user( self, username: str | None = None, own_attributes: bool = False, - attrib: bool = True, + fill_attrib: bool = True, ) -> dict[str, Any] | None: """Get user info using REST endpoint. @@ -1270,7 +1270,7 @@ def get_user( own_attributes (bool): Fill 'own_attributes' with attribute values that are explicitly set on user, other attributes have 'None' value. - attrib (bool): Fill missing attributes in 'attrib' with their + fill_attrib (bool): Fill missing attributes in 'attrib' with their default values. Requires an additional request for user attributes. Use 'False' when attributes are not needed. @@ -1288,7 +1288,7 @@ def get_user( response.raise_for_status() user = response.data - if attrib: + if fill_attrib: attributes = self.get_attributes_for_type("user") for attr_name, attr_data in attributes.items(): user["attrib"].setdefault(attr_name, attr_data["default"]) @@ -1377,7 +1377,7 @@ def login( """ if self.has_valid_token: try: - user_info = self.get_user(attrib=False) + user_info = self.get_user(fill_attrib=False) except UnauthorizedError: user_info = {} diff --git a/tests/test_get_user_attrib.py b/tests/test_get_user_attrib.py index 12fcc520e..dee572a69 100644 --- a/tests/test_get_user_attrib.py +++ b/tests/test_get_user_attrib.py @@ -1,4 +1,4 @@ -"""'get_user' fills default attribute values only if 'attrib' is 'True'. +"""'get_user' fills default attribute values only if 'fill_attrib' is 'True'. Does not require running AYON server. """ @@ -53,7 +53,7 @@ def test_get_user_fills_default_attributes(con): def test_get_user_without_attributes(con): - user = con.get_user("artist", attrib=False) + user = con.get_user("artist", fill_attrib=False) assert con.requested == ["users/artist"] assert user["name"] == "artist" assert user["attrib"] == {"email": "artist@example.com"} @@ -61,6 +61,6 @@ def test_get_user_without_attributes(con): def test_get_user_without_attributes_own_attributes(con): - user = con.get_user("artist", own_attributes=True, attrib=False) + user = con.get_user("artist", own_attributes=True, fill_attrib=False) assert con.requested == ["users/artist"] assert user["own_attributes"] == {"email": "artist@example.com"}