From 03c16f5a38ac67fac9c8fd10f11225a2881a4099 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:26:27 +0200 Subject: [PATCH 1/6] added type hints to operations logic --- ayon_api/operations.py | 641 ++++++++++++++++++++++------------------- 1 file changed, 344 insertions(+), 297 deletions(-) diff --git a/ayon_api/operations.py b/ayon_api/operations.py index 8a9dac465..24c1f507d 100644 --- a/ayon_api/operations.py +++ b/ayon_api/operations.py @@ -1,14 +1,21 @@ +from __future__ import annotations + import os import copy import collections import uuid from abc import ABC, abstractmethod +import typing +from typing import Optional, Any from ._api import get_server_api_connection from .utils import create_entity_id, REMOVED_VALUE, NOT_SET +if typing.TYPE_CHECKING: + from .server_api import ServerAPI + -def _create_or_convert_to_id(entity_id=None): +def _create_or_convert_to_id(entity_id: Optional[str] = None) -> str: if entity_id is None: return create_entity_id() @@ -17,7 +24,11 @@ def _create_or_convert_to_id(entity_id=None): return entity_id -def prepare_changes(old_entity, new_entity, entity_type): +def prepare_changes( + old_entity: dict[str, Any], + new_entity: dict[str, Any], + entity_type: str, +) -> dict[str, Any]: """Prepare changes for entity update. Notes: @@ -54,16 +65,16 @@ def prepare_changes(old_entity, new_entity, entity_type): def new_folder_entity( - name, - folder_type, - parent_id=None, - status=None, - tags=None, - attribs=None, - data=None, - thumbnail_id=None, - entity_id=None -): + name: str, + folder_type: str, + parent_id: Optional[str] = None, + status: Optional[str] = None, + tags: Optional[list[str]] = None, + attribs: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + thumbnail_id: Optional[str] = None, + entity_id: Optional[str] = None +) -> dict[str, Any]: """Create skeleton data of folder entity. Args: @@ -71,17 +82,17 @@ def new_folder_entity( folder_type (str): Type of folder. parent_id (Optional[str]): Parent folder id. status (Optional[str]): Product status. - tags (Optional[List[str]]): List of tags. - attribs (Optional[Dict[str, Any]]): Explicitly set attributes + tags (Optional[list[str]]): List of tags. + attribs (Optional[dict[str, Any]]): Explicitly set attributes of folder. - data (Optional[Dict[str, Any]]): Custom folder data. Empty dictionary + data (Optional[dict[str, Any]]): Custom folder data. Empty dictionary is used if not passed. thumbnail_id (Optional[str]): Thumbnail id related to folder. entity_id (Optional[str]): Predefined id of entity. New id is created if not passed. Returns: - Dict[str, Any]: Skeleton of folder entity. + dict[str, Any]: Skeleton of folder entity. """ if attribs is None: @@ -111,15 +122,15 @@ def new_folder_entity( def new_product_entity( - name, - product_type, - folder_id, - status=None, - tags=None, - attribs=None, - data=None, - entity_id=None -): + name: str, + product_type: str, + folder_id: str, + status: Optional[str] = None, + tags: Optional[list[str]] = None, + attribs: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + entity_id: Optional[str] = None, +) -> dict[str, Any]: """Create skeleton data of product entity. Args: @@ -128,16 +139,16 @@ def new_product_entity( product_type (str): Product type. folder_id (str): Parent folder id. status (Optional[str]): Product status. - tags (Optional[List[str]]): List of tags. - attribs (Optional[Dict[str, Any]]): Explicitly set attributes + tags (Optional[list[str]]): List of tags. + attribs (Optional[dict[str, Any]]): Explicitly set attributes of product. - data (Optional[Dict[str, Any]]): product entity data. Empty dictionary + data (Optional[dict[str, Any]]): product entity data. Empty dictionary is used if not passed. entity_id (Optional[str]): Predefined id of entity. New id is created if not passed. Returns: - Dict[str, Any]: Skeleton of product entity. + dict[str, Any]: Skeleton of product entity. """ if attribs is None: @@ -162,17 +173,17 @@ def new_product_entity( def new_version_entity( - version, - product_id, - task_id=None, - thumbnail_id=None, - author=None, - status=None, - tags=None, - attribs=None, - data=None, - entity_id=None -): + version: int, + product_id: str, + task_id: Optional[str] = None, + thumbnail_id: Optional[str] = None, + author: Optional[str] = None, + status: Optional[str] = None, + tags: Optional[list[str]] = None, + attribs: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + entity_id: Optional[str] = None, +) -> dict[str, Any]: """Create skeleton data of version entity. Args: @@ -183,15 +194,15 @@ def new_version_entity( thumbnail_id (Optional[str]): Thumbnail related to version. author (Optional[str]): Name of version author. status (Optional[str]): Version status. - tags (Optional[List[str]]): List of tags. - attribs (Optional[Dict[str, Any]]): Explicitly set attributes + tags (Optional[list[str]]): List of tags. + attribs (Optional[dict[str, Any]]): Explicitly set attributes of version. - data (Optional[Dict[str, Any]]): Version entity custom data. + data (Optional[dict[str, Any]]): Version entity custom data. entity_id (Optional[str]): Predefined id of entity. New id is created if not passed. Returns: - Dict[str, Any]: Skeleton of version entity. + dict[str, Any]: Skeleton of version entity. """ if attribs is None: @@ -221,17 +232,17 @@ def new_version_entity( def new_hero_version_entity( - version, - product_id, - task_id=None, - thumbnail_id=None, - author=None, - status=None, - tags=None, - attribs=None, - data=None, - entity_id=None -): + version: int, + product_id: str, + task_id: Optional[str] = None, + thumbnail_id: Optional[str] = None, + author: Optional[str] = None, + status: Optional[str] = None, + tags: Optional[list[str]] = None, + attribs: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + entity_id: Optional[str] = None, +) -> dict[str, Any]: """Create skeleton data of hero version entity. Args: @@ -242,18 +253,17 @@ def new_hero_version_entity( thumbnail_id (Optional[str]): Thumbnail related to version. author (Optional[str]): Name of version author. status (Optional[str]): Version status. - tags (Optional[List[str]]): List of tags. - attribs (Optional[Dict[str, Any]]): Explicitly set attributes + tags (Optional[list[str]]): List of tags. + attribs (Optional[dict[str, Any]]): Explicitly set attributes of version. - data (Optional[Dict[str, Any]]): Version entity data. + data (Optional[dict[str, Any]]): Version entity data. entity_id (Optional[str]): Predefined id of entity. New id is created if not passed. Returns: - Dict[str, Any]: Skeleton of version entity. + dict[str, Any]: Skeleton of version entity. """ - return new_version_entity( -abs(int(version)), product_id, @@ -269,16 +279,16 @@ def new_hero_version_entity( def new_representation_entity( - name, - version_id, + name: str, + version_id: str, files, - status=None, - tags=None, - attribs=None, - data=None, - traits=None, - entity_id=None, -): + status: Optional[str] = None, + tags: Optional[list[str]] = None, + attribs: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + traits: Optional[dict[str, Any]] = None, + entity_id: Optional[str] = None, +) -> dict[str, Any]: """Create skeleton data of representation entity. Args: @@ -287,17 +297,17 @@ def new_representation_entity( version_id (str): Parent version id. files (list[dict[str, str]]): List of files in representation. status (Optional[str]): Representation status. - tags (Optional[List[str]]): List of tags. - attribs (Optional[Dict[str, Any]]): Explicitly set attributes + tags (Optional[list[str]]): List of tags. + attribs (Optional[dict[str, Any]]): Explicitly set attributes of representation. - data (Optional[Dict[str, Any]]): Representation entity data. - traits (Optional[Dict[str, Any]]): Representation traits. Empty + data (Optional[dict[str, Any]]): Representation entity data. + traits (Optional[dict[str, Any]]): Representation traits. Empty if not passed. entity_id (Optional[str]): Predefined id of entity. New id is created if not passed. Returns: - Dict[str, Any]: Skeleton of representation entity. + dict[str, Any]: Skeleton of representation entity. """ if attribs is None: @@ -324,15 +334,15 @@ def new_representation_entity( def new_workfile_info( - filepath, - task_id, - status=None, - tags=None, - attribs=None, - description=None, - data=None, - entity_id=None -): + filepath: str, + task_id: str, + status: Optional[str] = None, + tags: Optional[list[str]] = None, + attribs: Optional[dict[str, Any]] = None, + description: Optional[str] = None, + data: Optional[dict[str, Any]] = None, + entity_id: Optional[str] = None, +) -> dict[str, Any]: """Create skeleton data of workfile info entity. Workfile entity is at this moment used primarily for artist notes. @@ -341,15 +351,15 @@ def new_workfile_info( filepath (str): Rootless workfile filepath. task_id (str): Task under which was workfile created. status (Optional[str]): Workfile status. - tags (Optional[List[str]]): Workfile tags. + tags (Optional[list[str]]): Workfile tags. attribs (Options[dic[str, Any]]): Explicitly set attributes. description (Optional[str]): Workfile description. - data (Optional[Dict[str, Any]]): Additional metadata. + data (Optional[dict[str, Any]]): Additional metadata. entity_id (Optional[str]): Predefined id of entity. New id is created if not passed. Returns: - Dict[str, Any]: Skeleton of workfile info entity. + dict[str, Any]: Skeleton of workfile info entity. """ if attribs is None: @@ -391,36 +401,49 @@ class AbstractOperation(ABC): e.g. 'folder', 'representation' etc. """ - def __init__(self, project_name, entity_type, session): + def __init__( + self, + project_name: str, + entity_type: str, + session: OperationsSession, + ) -> None: self._project_name = project_name self._entity_type = entity_type self._session = session self._id = str(uuid.uuid4()) @property - def project_name(self): + def project_name(self) -> str: return self._project_name @property - def id(self): + def id(self) -> str: """Identifier of operation.""" return self._id @property - def entity_type(self): + def entity_type(self) -> str: return self._entity_type @property @abstractmethod - def operation_name(self): + def operation_name(self) -> str: """Stringified type of operation.""" pass - def to_data(self): + @property + def session(self) -> OperationsSession: + return self._session + + @property + def con(self) -> "ServerAPI": + return self.session.con + + def to_data(self) -> dict[str, Any]: """Convert opration to data that can be converted to json or others. Returns: - Dict[str, Any]: Description of operation. + dict[str, Any]: Description of operation. """ return { @@ -438,12 +461,18 @@ class CreateOperation(AbstractOperation): project_name (str): On which project operation will happen. entity_type (str): Type of entity on which change happens. e.g. 'folder', 'representation' etc. - data (Dict[str, Any]): Data of entity that will be created. + data (dict[str, Any]): Data of entity that will be created. """ operation_name = "create" - def __init__(self, project_name, entity_type, data, session): + def __init__( + self, + project_name: str, + entity_type: str, + data: Optional[dict[str, Any]], + session: OperationsSession, + ) -> None: if not data: data = {} else: @@ -453,44 +482,34 @@ def __init__(self, project_name, entity_type, data, session): data["id"] = create_entity_id() self._data = data - super(CreateOperation, self).__init__( - project_name, entity_type, session - ) + super().__init__(project_name, entity_type, session) - def __setitem__(self, key, value): + def __setitem__(self, key: str, value: Any) -> None: self.set_value(key, value) - def __getitem__(self, key): + def __getitem__(self, key: str) -> Any: return self.data[key] - def set_value(self, key, value): + def set_value(self, key: str, value: Any) -> None: self.data[key] = value - def get(self, key, *args, **kwargs): + def get(self, key: str, *args, **kwargs) -> Any: return self.data.get(key, *args, **kwargs) @property - def con(self): - return self.session.con - - @property - def session(self): - return self._session - - @property - def entity_id(self): + def entity_id(self) -> str: return self._data["id"] @property - def data(self): + def data(self) -> dict[str, Any]: return self._data - def to_data(self): - output = super(CreateOperation, self).to_data() + def to_data(self) -> dict[str, Any]: + output = super().to_data() output["data"] = copy.deepcopy(self.data) return output - def to_server_operation(self): + def to_server_operation(self) -> dict[str, Any]: return { "id": self.id, "type": "create", @@ -508,7 +527,7 @@ class UpdateOperation(AbstractOperation): entity_type (str): Type of entity on which change happens. e.g. 'folder', 'representation' etc. entity_id (str): Identifier of an entity. - update_data (Dict[str, Any]): Key -> value changes that will be set in + update_data (dict[str, Any]): Key -> value changes that will be set in database. If value is set to 'REMOVED_VALUE' the key will be removed. Only first level of dictionary is checked (on purpose). @@ -516,46 +535,41 @@ class UpdateOperation(AbstractOperation): operation_name = "update" def __init__( - self, project_name, entity_type, entity_id, update_data, session + self, + project_name: str, + entity_type: str, + entity_id: str, + update_data: dict[str, Any], + session: OperationsSession, ): - super(UpdateOperation, self).__init__( - project_name, entity_type, session - ) + super().__init__(project_name, entity_type, session) self._entity_id = entity_id self._update_data = update_data @property - def entity_id(self): + def entity_id(self) -> str: return self._entity_id @property - def update_data(self): + def update_data(self) -> dict[str, Any]: return self._update_data - @property - def con(self): - return self.session.con - - @property - def session(self): - return self._session - - def to_data(self): + def to_data(self) -> dict[str, Any]: changes = {} for key, value in self._update_data.items(): if value is REMOVED_VALUE: value = None changes[key] = value - output = super(UpdateOperation, self).to_data() + output = super().to_data() output.update({ "entity_id": self.entity_id, "changes": changes }) return output - def to_server_operation(self): + def to_server_operation(self) -> Optional[dict[str, Any]]: if not self._update_data: return None @@ -586,31 +600,27 @@ class DeleteOperation(AbstractOperation): """ operation_name = "delete" - def __init__(self, project_name, entity_type, entity_id, session): + def __init__( + self, + project_name: str, + entity_type: str, + entity_id: str, + session: OperationsSession, + ) -> None: self._entity_id = entity_id - super(DeleteOperation, self).__init__( - project_name, entity_type, session - ) + super().__init__(project_name, entity_type, session) @property - def entity_id(self): + def entity_id(self) -> str: return self._entity_id - @property - def con(self): - return self.session.con - - @property - def session(self): - return self._session - - def to_data(self): - output = super(DeleteOperation, self).to_data() + def to_data(self) -> dict[str, Any]: + output = super().to_data() output["entity_id"] = self.entity_id return output - def to_server_operation(self): + def to_server_operation(self) -> dict[str, Any]: return { "id": self.id, "type": self.operation_name, @@ -634,7 +644,7 @@ class OperationsSession(object): is used if not passed. """ - def __init__(self, con=None): + def __init__(self, con: Optional["ServerApi"] = None) -> None: if con is None: con = get_server_api_connection() self._con = con @@ -643,19 +653,21 @@ def __init__(self, con=None): self._nested_operations = collections.defaultdict(list) @property - def con(self): + def con(self) -> "ServerAPI": return self._con - def get_project(self, project_name): + def get_project( + self, project_name: str + ) -> Optional[dict[str, Any]]: if project_name not in self._project_cache: self._project_cache[project_name] = self.con.get_project( project_name) return copy.deepcopy(self._project_cache[project_name]) - def __len__(self): + def __len__(self) -> int: return len(self._operations) - def add(self, operation): + def add(self, operation: AbstractOperation) -> None: """Add operation to be processed. Args: @@ -672,7 +684,7 @@ def add(self, operation): self._operations.append(operation) - def append(self, operation): + def append(self, operation: AbstractOperation) -> None: """Add operation to be processed. Args: @@ -681,32 +693,32 @@ def append(self, operation): """ self.add(operation) - def extend(self, operations): + def extend(self, operations: list[AbstractOperation]) -> None: """Add operations to be processed. Args: - operations (List[BaseOperation]): Operations that should be + operations (list[BaseOperation]): Operations that should be processed. """ for operation in operations: self.add(operation) - def remove(self, operation): + def remove(self, operation: AbstractOperation) -> None: """Remove operation.""" self._operations.remove(operation) - def clear(self): + def clear(self) -> None: """Clear all registered operations.""" self._operations = [] - def to_data(self): + def to_data(self) -> list[dict[str, Any]]: return [ operation.to_data() for operation in self._operations ] - def commit(self): + def commit(self) -> None: """Commit session operations.""" operations, self._operations = self._operations, [] if not operations: @@ -727,7 +739,13 @@ def commit(self): project_name, operations_body, can_fail=False ) - def create_entity(self, project_name, entity_type, data, nested_id=None): + def create_entity( + self, + project_name: str, + entity_type: str, + data: dict[str, Any], + nested_id: Optional[str] = None, + ) -> CreateOperation: """Fast access to 'CreateOperation'. Args: @@ -756,8 +774,13 @@ def create_entity(self, project_name, entity_type, data, nested_id=None): return operation def update_entity( - self, project_name, entity_type, entity_id, update_data, nested_id=None - ): + self, + project_name: str, + entity_type: str, + entity_id: str, + update_data: dict[str, Any], + nested_id: Optional[str] = None, + ) -> UpdateOperation: """Fast access to 'UpdateOperation'. Returns: @@ -776,8 +799,12 @@ def update_entity( return operation def delete_entity( - self, project_name, entity_type, entity_id, nested_id=None - ): + self, + project_name: str, + entity_type: str, + entity_id: str, + nested_id: Optional[str] = None, + ) -> DeleteOperation: """Fast access to 'DeleteOperation'. Returns: @@ -797,19 +824,19 @@ def delete_entity( def create_folder( self, - project_name, - name, - folder_type=None, - parent_id=None, - label=None, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - thumbnail_id=None, - folder_id=None, - ): + project_name: str, + name: str, + folder_type: Optional[str] = None, + parent_id: Optional[str] = None, + label: Optional[str] = None, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + thumbnail_id: Optional[str] = None, + folder_id: Optional[str] = None, + ) -> CreateOperation: """Create new folder. Args: @@ -858,18 +885,18 @@ def create_folder( def update_folder( self, - project_name, - folder_id, - name=None, - folder_type=None, - parent_id=NOT_SET, - label=NOT_SET, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - thumbnail_id=NOT_SET, + project_name: str, + folder_id: str, + name: Optional[str] = None, + folder_type: Optional[str] = None, + parent_id: Optional[str] = NOT_SET, + label: Optional[str] = NOT_SET, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + thumbnail_id: Optional[str] = NOT_SET, ): """Update folder entity on server. @@ -925,7 +952,11 @@ def update_folder( project_name, "folder", folder_id, update_data ) - def delete_folder(self, project_name, folder_id): + def delete_folder( + self, + project_name: str, + folder_id: str, + ) -> DeleteOperation: """Delete folder. Args: @@ -942,20 +973,20 @@ def delete_folder(self, project_name, folder_id): def create_task( self, - project_name, - name, - task_type, - folder_id, - label=None, - assignees=None, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - thumbnail_id=None, - task_id=None, - ): + project_name: str, + name: str, + task_type: str, + folder_id: str, + label: Optional[str] = None, + assignees: Optional[Iterable[str]] = None, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + thumbnail_id: Optional[str] = None, + task_id: Optional[str] = None, + ) -> CreateOperation: """Create new task. Args: @@ -1005,20 +1036,20 @@ def create_task( def update_task( self, - project_name, - task_id, - name=None, - task_type=None, - folder_id=None, - label=NOT_SET, - assignees=None, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - thumbnail_id=NOT_SET, - ): + project_name: str, + task_id: str, + name: Optional[str] = None, + task_type: Optional[str] = None, + folder_id: Optional[str] = None, + label: Optional[str] = NOT_SET, + assignees: Optional[list[str]] = None, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + thumbnail_id: Optional[str] = NOT_SET, + ) -> UpdateOperation: """Update task entity on server. Do not pass ``label`` amd ``thumbnail_id`` if you don't @@ -1075,7 +1106,11 @@ def update_task( project_name, "task", task_id, update_data ) - def delete_task(self, project_name, task_id): + def delete_task( + self, + project_name: str, + task_id: str, + ) -> DeleteOperation: """Delete task. Args: @@ -1090,17 +1125,17 @@ def delete_task(self, project_name, task_id): def create_product( self, - project_name, - name, - product_type, - folder_id, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - product_id=None, - ): + project_name: str, + name: str, + product_type: str, + folder_id: str, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + product_id: Optional[str] = None, + ) -> CreateOperation: """Create new product. Args: @@ -1144,17 +1179,17 @@ def create_product( def update_product( self, - project_name, - product_id, - name=None, - folder_id=None, - product_type=None, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - ): + project_name: str, + product_id: str, + name: Optional[str] = None, + folder_id: Optional[str] = None, + product_type: Optional[str] = None, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + ) -> UpdateOperation: """Update product entity on server. Update of ``data`` will override existing value on folder entity. @@ -1199,7 +1234,11 @@ def update_product( update_data ) - def delete_product(self, project_name, product_id): + def delete_product( + self, + project_name: str, + product_id: str, + ) -> DeleteOperation: """Delete product. Args: @@ -1216,19 +1255,19 @@ def delete_product(self, project_name, product_id): def create_version( self, - project_name, - version, - product_id, - task_id=None, - author=None, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - thumbnail_id=None, - version_id=None, - ): + project_name: str, + version: int, + product_id: str, + task_id: Optional[str] = None, + author: Optional[str] = None, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + thumbnail_id: Optional[str] = None, + version_id: Optional[str] = None, + ) -> CreateOperation: """Create new version. Args: @@ -1276,18 +1315,18 @@ def create_version( def update_version( self, - project_name, - version_id, - version=None, - product_id=None, - task_id=NOT_SET, - attrib=None, - data=None, - tags=None, - status=None, - active=None, - thumbnail_id=NOT_SET, - ): + project_name: str, + version_id: str, + version: Optional[int] = None, + product_id: Optional[str] = None, + task_id: Optional[str] = NOT_SET, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + thumbnail_id: Optional[str] = NOT_SET, + ) -> UpdateOperation: """Update version entity on server. Do not pass ``task_id`` amd ``thumbnail_id`` if you don't @@ -1341,7 +1380,11 @@ def update_version( project_name, "version", version_id, update_data ) - def delete_version(self, project_name, version_id): + def delete_version( + self, + project_name: str, + version_id: str, + ) -> DeleteOperation: """Delete version. Args: @@ -1358,18 +1401,18 @@ def delete_version(self, project_name, version_id): def create_representation( self, - project_name, - name, - version_id, - files=None, - attrib=None, - data=None, - traits=None, - tags=None, - status=None, - active=None, - representation_id=None, - ): + project_name: str, + name: str, + version_id: str, + files: Optional[list[dict[str, Any]]] = None, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + traits: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + representation_id: Optional[str] = None, + ) -> CreateOperation: """Create new representation. Args: @@ -1379,7 +1422,7 @@ def create_representation( files (Optional[list[dict]]): Representation files information. attrib (Optional[dict[str, Any]]): Representation attributes. data (Optional[dict[str, Any]]): Representation data. - traits (Optional[Dict[str, Any]]): Representation traits. Empty + traits (Optional[dict[str, Any]]): Representation traits. Empty if not passed. tags (Optional[Iterable[str]]): Representation tags. status (Optional[str]): Representation status. @@ -1418,18 +1461,18 @@ def create_representation( def update_representation( self, - project_name, - representation_id, - name=None, - version_id=None, - files=None, - attrib=None, - data=None, - traits=None, - tags=None, - status=None, - active=None, - ): + project_name: str, + representation_id: str, + name: Optional[str] = None, + version_id: Optional[str] = None, + files: Optional[list[dict[str, Any]]] = None, + attrib: Optional[dict[str, Any]] = None, + data: Optional[dict[str, Any]] = None, + traits: Optional[dict[str, Any]] = None, + tags: Optional[list[str]] = None, + status: Optional[str] = None, + active: Optional[bool] = None, + ) -> UpdateOperation: """Update representation entity on server. Update of ``data`` will override existing value on folder entity. @@ -1446,7 +1489,7 @@ def update_representation( information. attrib (Optional[dict[str, Any]]): New attributes. data (Optional[dict[str, Any]]): New data. - traits (Optional[Dict[str, Any]]): New representation traits. + traits (Optional[dict[str, Any]]): New representation traits. tags (Optional[Iterable[str]]): New tags. status (Optional[str]): New status. active (Optional[bool]): New active state. @@ -1477,7 +1520,11 @@ def update_representation( update_data ) - def delete_representation(self, project_name, representation_id): + def delete_representation( + self, + project_name: str, + representation_id: str, + ) -> DeleteOperation: """Delete representation. Args: From 5eae3125befd9946553f7336843480fe79f88f1d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:29:46 +0200 Subject: [PATCH 2/6] fix missing variables --- ayon_api/operations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ayon_api/operations.py b/ayon_api/operations.py index 24c1f507d..e923143a0 100644 --- a/ayon_api/operations.py +++ b/ayon_api/operations.py @@ -6,7 +6,7 @@ import uuid from abc import ABC, abstractmethod import typing -from typing import Optional, Any +from typing import Optional, Any, Iterable from ._api import get_server_api_connection from .utils import create_entity_id, REMOVED_VALUE, NOT_SET @@ -644,7 +644,7 @@ class OperationsSession(object): is used if not passed. """ - def __init__(self, con: Optional["ServerApi"] = None) -> None: + def __init__(self, con: Optional["ServerAPI"] = None) -> None: if con is None: con = get_server_api_connection() self._con = con From c5a6a94ca4e5ae08aed76ad1bd14602560c6130e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 29 Aug 2025 13:48:50 +0200 Subject: [PATCH 3/6] don't wrap ServerAPI --- ayon_api/operations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ayon_api/operations.py b/ayon_api/operations.py index 96a7d6335..1192c290a 100644 --- a/ayon_api/operations.py +++ b/ayon_api/operations.py @@ -436,7 +436,7 @@ def session(self) -> OperationsSession: return self._session @property - def con(self) -> "ServerAPI": + def con(self) -> ServerAPI: return self.session.con def to_data(self) -> dict[str, Any]: @@ -644,7 +644,7 @@ class OperationsSession(object): is used if not passed. """ - def __init__(self, con: Optional["ServerAPI"] = None) -> None: + def __init__(self, con: Optional[ServerAPI] = None) -> None: if con is None: con = get_server_api_connection() self._con = con @@ -653,7 +653,7 @@ def __init__(self, con: Optional["ServerAPI"] = None) -> None: self._nested_operations = collections.defaultdict(list) @property - def con(self) -> "ServerAPI": + def con(self) -> ServerAPI: return self._con def get_project( From 070095e4335ed61ad4e8560ea3bb95c48c49668d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:46:35 +0200 Subject: [PATCH 4/6] added typed dictionaries for new entities --- ayon_api/operations.py | 27 ++++++++++++------- ayon_api/typing.py | 59 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/ayon_api/operations.py b/ayon_api/operations.py index 1192c290a..a9a4074d7 100644 --- a/ayon_api/operations.py +++ b/ayon_api/operations.py @@ -13,6 +13,13 @@ if typing.TYPE_CHECKING: from .server_api import ServerAPI + from .typing import ( + NewFolderDict, + NewProductDict, + NewVersionDict, + NewRepresentationDict, + NewWorkfileDict, + ) def _create_or_convert_to_id(entity_id: Optional[str] = None) -> str: @@ -74,7 +81,7 @@ def new_folder_entity( data: Optional[dict[str, Any]] = None, thumbnail_id: Optional[str] = None, entity_id: Optional[str] = None -) -> dict[str, Any]: +) -> NewFolderDict: """Create skeleton data of folder entity. Args: @@ -130,7 +137,7 @@ def new_product_entity( attribs: Optional[dict[str, Any]] = None, data: Optional[dict[str, Any]] = None, entity_id: Optional[str] = None, -) -> dict[str, Any]: +) -> NewProductDict: """Create skeleton data of product entity. Args: @@ -148,7 +155,7 @@ def new_product_entity( created if not passed. Returns: - dict[str, Any]: Skeleton of product entity. + NewProductDict: Skeleton of product entity. """ if attribs is None: @@ -183,7 +190,7 @@ def new_version_entity( attribs: Optional[dict[str, Any]] = None, data: Optional[dict[str, Any]] = None, entity_id: Optional[str] = None, -) -> dict[str, Any]: +) -> NewVersionDict: """Create skeleton data of version entity. Args: @@ -202,7 +209,7 @@ def new_version_entity( created if not passed. Returns: - dict[str, Any]: Skeleton of version entity. + NewVersionDict: Skeleton of version entity. """ if attribs is None: @@ -242,7 +249,7 @@ def new_hero_version_entity( attribs: Optional[dict[str, Any]] = None, data: Optional[dict[str, Any]] = None, entity_id: Optional[str] = None, -) -> dict[str, Any]: +) -> NewVersionDict: """Create skeleton data of hero version entity. Args: @@ -261,7 +268,7 @@ def new_hero_version_entity( created if not passed. Returns: - dict[str, Any]: Skeleton of version entity. + NewVersionDict: Skeleton of version entity. """ return new_version_entity( @@ -288,7 +295,7 @@ def new_representation_entity( data: Optional[dict[str, Any]] = None, traits: Optional[dict[str, Any]] = None, entity_id: Optional[str] = None, -) -> dict[str, Any]: +) -> NewRepresentationDict: """Create skeleton data of representation entity. Args: @@ -307,7 +314,7 @@ def new_representation_entity( if not passed. Returns: - dict[str, Any]: Skeleton of representation entity. + NewRepresentationDict: Skeleton of representation entity. """ if attribs is None: @@ -342,7 +349,7 @@ def new_workfile_info( description: Optional[str] = None, data: Optional[dict[str, Any]] = None, entity_id: Optional[str] = None, -) -> dict[str, Any]: +) -> NewWorkfileDict: """Create skeleton data of workfile info entity. Workfile entity is at this moment used primarily for artist notes. diff --git a/ayon_api/typing.py b/ayon_api/typing.py index aa31065ea..8957cf92b 100644 --- a/ayon_api/typing.py +++ b/ayon_api/typing.py @@ -342,6 +342,65 @@ class SecretDict(TypedDict): ActivityDict, ] + +class NewFolderDict(TypedDict): + id: str + name: str + folderType: str + parentId: Optional[str] + data: dict[str, Any] + attrib: dict[str, Any] + thumbnailId: Optional[str] + status: Optional[str] + tags: Optional[list[str]] + + +class NewProductDict(TypedDict): + id: str + name: str + productType: str + folderId: str + data: dict[str, Any] + attrib: dict[str, Any] + status: Optional[str] + tags: Optional[list[str]] + + +class NewVersionDict(TypedDict): + id: str + version: int + productId: str + attrib: dict[str, Any] + data: dict[str, Any] + taskId: Optional[str] + thumbnailId: Optional[str] + author: Optional[str] + status: Optional[str] + tags: Optional[list[str]] + + +class NewRepresentationDict(TypedDict): + id: str + versionId: str + name: str + data: dict[str, Any] + attrib: dict[str, Any] + files: list[dict[str, str]] + traits: Optional[dict[str, Any]] + status: Optional[str] + tags: Optional[list[str]] + + +class NewWorkfileDict(TypedDict): + id: str + taskId: str + path: str + data: dict[str, Any] + attrib: dict[str, Any] + status: Optional[str] + tags: Optional[list[str]] + + EventStatus = Literal[ "pending", "in_progress", From e73c90698adad716b031b4353a77005bb0140080 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:57:26 +0200 Subject: [PATCH 5/6] fix type hints in docstring --- ayon_api/operations.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ayon_api/operations.py b/ayon_api/operations.py index a9a4074d7..6780ccfd9 100644 --- a/ayon_api/operations.py +++ b/ayon_api/operations.py @@ -921,14 +921,14 @@ def update_folder( folder_id (str): Folder id. name (Optional[str]): New name. folder_type (Optional[str]): New folder type. - parent_id (Optional[Union[str, None]]): New parent folder id. - label (Optional[Union[str, None]]): New label. + parent_id (Optional[str]): New parent folder id. + label (Optional[str]): New label. attrib (Optional[dict[str, Any]]): New attributes. data (Optional[dict[str, Any]]): New data. tags (Optional[Iterable[str]]): New tags. status (Optional[str]): New status. active (Optional[bool]): New active state. - thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + thumbnail_id (Optional[str]): New thumbnail id. Returns: UpdateOperation: Object of update operation. @@ -1074,14 +1074,14 @@ def update_task( name (Optional[str]): New name. task_type (Optional[str]): New task type. folder_id (Optional[str]): New folder id. - label (Optional[Union[str, None]]): New label. + label (Optional[str]): New label. assignees (Optional[str]): New assignees. attrib (Optional[dict[str, Any]]): New attributes. data (Optional[dict[str, Any]]): New data. tags (Optional[Iterable[str]]): New tags. status (Optional[str]): New status. active (Optional[bool]): New active state. - thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + thumbnail_id (Optional[str]): New thumbnail id. Returns: UpdateOperation: Object of update operation. @@ -1350,13 +1350,13 @@ def update_version( version_id (str): Version id. version (Optional[int]): New version. product_id (Optional[str]): New product id. - task_id (Optional[Union[str, None]]): New task id. + task_id (Optional[str]): New task id. attrib (Optional[dict[str, Any]]): New attributes. data (Optional[dict[str, Any]]): New data. tags (Optional[Iterable[str]]): New tags. status (Optional[str]): New status. active (Optional[bool]): New active state. - thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + thumbnail_id (Optional[str]): New thumbnail id. Returns: UpdateOperation: Object of update operation. From ad79690a03f82999b65a30664cc8352fdd736d0f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 8 Sep 2025 17:22:55 +0200 Subject: [PATCH 6/6] fix return type hints --- ayon_api/operations.py | 6 +++--- ayon_api/typing.py | 29 +++++++++++++++-------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/ayon_api/operations.py b/ayon_api/operations.py index 6780ccfd9..dbdc99985 100644 --- a/ayon_api/operations.py +++ b/ayon_api/operations.py @@ -99,7 +99,7 @@ def new_folder_entity( created if not passed. Returns: - dict[str, Any]: Skeleton of folder entity. + NewFolderDict: Skeleton of folder entity. """ if attribs is None: @@ -119,7 +119,7 @@ def new_folder_entity( "parentId": parent_id, "data": data, "attrib": attribs, - "thumbnailId": thumbnail_id + "thumbnailId": thumbnail_id, } if status: output["status"] = status @@ -366,7 +366,7 @@ def new_workfile_info( if not passed. Returns: - dict[str, Any]: Skeleton of workfile info entity. + NewWorkfileDict: Skeleton of workfile info entity. """ if attribs is None: diff --git a/ayon_api/typing.py b/ayon_api/typing.py index 8957cf92b..cce3f196a 100644 --- a/ayon_api/typing.py +++ b/ayon_api/typing.py @@ -8,6 +8,7 @@ Union, Optional, BinaryIO, + NotRequired, ) @@ -351,8 +352,8 @@ class NewFolderDict(TypedDict): data: dict[str, Any] attrib: dict[str, Any] thumbnailId: Optional[str] - status: Optional[str] - tags: Optional[list[str]] + status: NotRequired[str] + tags: NotRequired[list[str]] class NewProductDict(TypedDict): @@ -362,8 +363,8 @@ class NewProductDict(TypedDict): folderId: str data: dict[str, Any] attrib: dict[str, Any] - status: Optional[str] - tags: Optional[list[str]] + status: NotRequired[str] + tags: NotRequired[list[str]] class NewVersionDict(TypedDict): @@ -372,11 +373,11 @@ class NewVersionDict(TypedDict): productId: str attrib: dict[str, Any] data: dict[str, Any] - taskId: Optional[str] - thumbnailId: Optional[str] - author: Optional[str] - status: Optional[str] - tags: Optional[list[str]] + taskId: NotRequired[str] + thumbnailId: NotRequired[str] + author: NotRequired[str] + status: NotRequired[str] + tags: NotRequired[list[str]] class NewRepresentationDict(TypedDict): @@ -386,9 +387,9 @@ class NewRepresentationDict(TypedDict): data: dict[str, Any] attrib: dict[str, Any] files: list[dict[str, str]] - traits: Optional[dict[str, Any]] - status: Optional[str] - tags: Optional[list[str]] + traits: NotRequired[dict[str, Any]] + status: NotRequired[str] + tags: NotRequired[list[str]] class NewWorkfileDict(TypedDict): @@ -397,8 +398,8 @@ class NewWorkfileDict(TypedDict): path: str data: dict[str, Any] attrib: dict[str, Any] - status: Optional[str] - tags: Optional[list[str]] + status: NotRequired[str] + tags: NotRequired[list[str]] EventStatus = Literal[