ayon-python-api 1.2.1__tar.gz → 1.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {ayon-python-api-1.2.1/ayon_python_api.egg-info → ayon_python_api-1.2.2}/PKG-INFO +2 -2
  2. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/__init__.py +4 -0
  3. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api.py +75 -0
  4. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/entity_hub.py +5 -2
  5. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/server_api.py +135 -27
  6. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/typing.py +6 -0
  7. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/version.py +1 -1
  8. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2/ayon_python_api.egg-info}/PKG-INFO +1 -1
  9. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/pyproject.toml +4 -4
  10. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/setup.py +1 -1
  11. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/LICENSE +0 -0
  12. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/README.md +0 -0
  13. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/__init__.py +0 -0
  14. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/actions.py +0 -0
  15. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/activities.py +0 -0
  16. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/attributes.py +0 -0
  17. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/base.py +0 -0
  18. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/bundles_addons.py +0 -0
  19. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/dependency_packages.py +0 -0
  20. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/events.py +0 -0
  21. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/folders.py +0 -0
  22. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/installers.py +0 -0
  23. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/links.py +0 -0
  24. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/lists.py +0 -0
  25. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/products.py +0 -0
  26. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/projects.py +0 -0
  27. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/representations.py +0 -0
  28. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/secrets.py +0 -0
  29. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/tasks.py +0 -0
  30. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/thumbnails.py +0 -0
  31. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/versions.py +0 -0
  32. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/_api_helpers/workfiles.py +0 -0
  33. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/constants.py +0 -0
  34. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/events.py +0 -0
  35. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/exceptions.py +0 -0
  36. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/graphql.py +0 -0
  37. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/graphql_queries.py +0 -0
  38. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/operations.py +0 -0
  39. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_api/utils.py +0 -0
  40. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  41. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  42. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_python_api.egg-info/requires.txt +0 -0
  43. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/ayon_python_api.egg-info/top_level.txt +0 -0
  44. {ayon-python-api-1.2.1 → ayon_python_api-1.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
- Name: ayon-python-api
3
- Version: 1.2.1
2
+ Name: ayon_python_api
3
+ Version: 1.2.2
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -80,6 +80,8 @@ from ._api import (
80
80
  get_default_fields_for_type,
81
81
  get_rest_entity_by_id,
82
82
  send_batch_operations,
83
+ send_background_batch_operations,
84
+ get_background_operations_status,
83
85
  get_installers,
84
86
  create_installer,
85
87
  update_installer,
@@ -347,6 +349,8 @@ __all__ = (
347
349
  "get_default_fields_for_type",
348
350
  "get_rest_entity_by_id",
349
351
  "send_batch_operations",
352
+ "send_background_batch_operations",
353
+ "get_background_operations_status",
350
354
  "get_installers",
351
355
  "create_installer",
352
356
  "update_installer",
@@ -48,6 +48,7 @@ if typing.TYPE_CHECKING:
48
48
  ActivityReferenceType,
49
49
  EntityListEntityType,
50
50
  EntityListItemMode,
51
+ BackgroundOperationTask,
51
52
  LinkDirection,
52
53
  EventFilter,
53
54
  EventStatus,
@@ -1253,6 +1254,80 @@ def send_batch_operations(
1253
1254
  )
1254
1255
 
1255
1256
 
1257
+ def send_background_batch_operations(
1258
+ project_name: str,
1259
+ operations: list[dict[str, Any]],
1260
+ *,
1261
+ can_fail: bool = False,
1262
+ wait: bool = False,
1263
+ raise_on_fail: bool = True,
1264
+ ) -> BackgroundOperationTask:
1265
+ """Post multiple CRUD operations to server.
1266
+
1267
+ When multiple changes should be made on server side this is the best
1268
+ way to go. It is possible to pass multiple operations to process on a
1269
+ server side and do the changes in a transaction.
1270
+
1271
+ Compared to 'send_batch_operations' this function creates a task on
1272
+ server which then can be periodically checked for a status and
1273
+ receive it's result.
1274
+
1275
+ When used with 'wait' set to 'True' this method blocks until task is
1276
+ finished. Which makes it work as 'send_batch_operations'
1277
+ but safer for large operations batch as is not bound to
1278
+ response timeout.
1279
+
1280
+ Args:
1281
+ project_name (str): On which project should be operations
1282
+ processed.
1283
+ operations (list[dict[str, Any]]): Operations to be processed.
1284
+ can_fail (Optional[bool]): Server will try to process all
1285
+ operations even if one of them fails.
1286
+ wait (bool): Wait for operations to end.
1287
+ raise_on_fail (Optional[bool]): Raise exception if an operation
1288
+ fails. You can handle failed operations on your own
1289
+ when set to 'False'. Used when 'wait' is enabled.
1290
+
1291
+ Raises:
1292
+ ValueError: Operations can't be converted to json string.
1293
+ FailedOperations: When output does not contain server operations
1294
+ or 'raise_on_fail' is enabled and any operation fails.
1295
+
1296
+ Returns:
1297
+ BackgroundOperationTask: Background operation.
1298
+
1299
+ """
1300
+ con = get_server_api_connection()
1301
+ return con.send_background_batch_operations(
1302
+ project_name=project_name,
1303
+ operations=operations,
1304
+ can_fail=can_fail,
1305
+ wait=wait,
1306
+ raise_on_fail=raise_on_fail,
1307
+ )
1308
+
1309
+
1310
+ def get_background_operations_status(
1311
+ project_name: str,
1312
+ task_id: str,
1313
+ ) -> BackgroundOperationTask:
1314
+ """Get status of background operations task.
1315
+
1316
+ Args:
1317
+ project_name (str): Project name.
1318
+ task_id (str): Backgorund operation task id.
1319
+
1320
+ Returns:
1321
+ BackgroundOperationTask: Background operation.
1322
+
1323
+ """
1324
+ con = get_server_api_connection()
1325
+ return con.get_background_operations_status(
1326
+ project_name=project_name,
1327
+ task_id=task_id,
1328
+ )
1329
+
1330
+
1256
1331
  def get_installers(
1257
1332
  version: Optional[str] = None,
1258
1333
  platform_name: Optional[str] = None,
@@ -1225,8 +1225,11 @@ class EntityHub:
1225
1225
  if not entity.created:
1226
1226
  operations_body.append(self._get_delete_body(entity))
1227
1227
 
1228
- self._connection.send_batch_operations(
1229
- self.project_name, operations_body
1228
+ self._connection.send_background_batch_operations(
1229
+ self.project_name,
1230
+ operations_body,
1231
+ can_fail=False,
1232
+ wait=True,
1230
1233
  )
1231
1234
  if post_project_changes:
1232
1235
  self._connection.update_project(
@@ -91,6 +91,7 @@ if typing.TYPE_CHECKING:
91
91
  ServerVersion,
92
92
  AnyEntityDict,
93
93
  StreamType,
94
+ BackgroundOperationTask,
94
95
  )
95
96
 
96
97
  VERSION_REGEX = re.compile(
@@ -1870,7 +1871,7 @@ class ServerAPI(
1870
1871
  project_name: str,
1871
1872
  operations: list[dict[str, Any]],
1872
1873
  can_fail: bool = False,
1873
- raise_on_fail: bool = True
1874
+ raise_on_fail: bool = True,
1874
1875
  ) -> list[dict[str, Any]]:
1875
1876
  """Post multiple CRUD operations to server.
1876
1877
 
@@ -1904,17 +1905,98 @@ class ServerAPI(
1904
1905
  raise_on_fail,
1905
1906
  )
1906
1907
 
1907
- def _send_batch_operations(
1908
+ def send_background_batch_operations(
1908
1909
  self,
1909
- uri: str,
1910
+ project_name: str,
1910
1911
  operations: list[dict[str, Any]],
1911
- can_fail: bool,
1912
- raise_on_fail: bool
1913
- ) -> list[dict[str, Any]]:
1914
- if not operations:
1915
- return []
1912
+ *,
1913
+ can_fail: bool = False,
1914
+ wait: bool = False,
1915
+ raise_on_fail: bool = True,
1916
+ ) -> BackgroundOperationTask:
1917
+ """Post multiple CRUD operations to server.
1918
+
1919
+ When multiple changes should be made on server side this is the best
1920
+ way to go. It is possible to pass multiple operations to process on a
1921
+ server side and do the changes in a transaction.
1922
+
1923
+ Compared to 'send_batch_operations' this function creates a task on
1924
+ server which then can be periodically checked for a status and
1925
+ receive it's result.
1926
+
1927
+ When used with 'wait' set to 'True' this method blocks until task is
1928
+ finished. Which makes it work as 'send_batch_operations'
1929
+ but safer for large operations batch as is not bound to
1930
+ response timeout.
1931
+
1932
+ Args:
1933
+ project_name (str): On which project should be operations
1934
+ processed.
1935
+ operations (list[dict[str, Any]]): Operations to be processed.
1936
+ can_fail (Optional[bool]): Server will try to process all
1937
+ operations even if one of them fails.
1938
+ wait (bool): Wait for operations to end.
1939
+ raise_on_fail (Optional[bool]): Raise exception if an operation
1940
+ fails. You can handle failed operations on your own
1941
+ when set to 'False'. Used when 'wait' is enabled.
1942
+
1943
+ Raises:
1944
+ ValueError: Operations can't be converted to json string.
1945
+ FailedOperations: When output does not contain server operations
1946
+ or 'raise_on_fail' is enabled and any operation fails.
1947
+
1948
+ Returns:
1949
+ BackgroundOperationTask: Background operation.
1950
+
1951
+ """
1952
+ operations_body = self._prepare_operations_body(operations)
1953
+ response = self.post(
1954
+ f"projects/{project_name}/operations/background",
1955
+ operations=operations_body,
1956
+ canFail=can_fail
1957
+ )
1958
+ response.raise_for_status()
1959
+ if not wait:
1960
+ return response.data
1916
1961
 
1917
- body_by_id = {}
1962
+ task_id = response["id"]
1963
+ time.sleep(0.1)
1964
+ while True:
1965
+ op_status = self.get_background_operations_status(
1966
+ project_name, task_id
1967
+ )
1968
+ if op_status["status"] == "completed":
1969
+ break
1970
+ time.sleep(1)
1971
+
1972
+ if raise_on_fail:
1973
+ self._validate_operations_result(
1974
+ op_status["result"], operations_body
1975
+ )
1976
+ return op_status
1977
+
1978
+ def get_background_operations_status(
1979
+ self, project_name: str, task_id: str
1980
+ ) -> BackgroundOperationTask:
1981
+ """Get status of background operations task.
1982
+
1983
+ Args:
1984
+ project_name (str): Project name.
1985
+ task_id (str): Backgorund operation task id.
1986
+
1987
+ Returns:
1988
+ BackgroundOperationTask: Background operation.
1989
+
1990
+ """
1991
+ response = self.get(
1992
+ f"projects/{project_name}/operations/background/{task_id}"
1993
+ )
1994
+ response.raise_for_status()
1995
+ return response.data
1996
+
1997
+ def _prepare_operations_body(
1998
+ self, operations: list[dict[str, Any]]
1999
+ ) -> list[dict[str, Any]]:
1918
2000
  operations_body = []
1919
2001
  for operation in operations:
1920
2002
  if not operation:
@@ -1936,42 +2018,68 @@ class ServerAPI(
1936
2018
  )
1937
2019
  ))
1938
2020
 
1939
- body_by_id[op_id] = body
1940
2021
  operations_body.append(body)
2022
+ return operations_body
1941
2023
 
2024
+ def _send_batch_operations(
2025
+ self,
2026
+ uri: str,
2027
+ operations: list[dict[str, Any]],
2028
+ can_fail: bool,
2029
+ raise_on_fail: bool
2030
+ ) -> list[dict[str, Any]]:
2031
+ if not operations:
2032
+ return []
2033
+
2034
+ operations_body = self._prepare_operations_body(operations)
1942
2035
  if not operations_body:
1943
2036
  return []
1944
2037
 
1945
- result = self.post(
2038
+ response = self.post(
1946
2039
  uri,
1947
2040
  operations=operations_body,
1948
2041
  canFail=can_fail
1949
2042
  )
1950
2043
 
1951
- op_results = result.get("operations")
2044
+ op_results = response.get("operations")
1952
2045
  if op_results is None:
1953
- detail = result.get("detail")
2046
+ detail = response.get("detail")
1954
2047
  if detail:
1955
2048
  raise FailedOperations(f"Operation failed. Detail: {detail}")
1956
2049
  raise FailedOperations(
1957
- f"Operation failed. Content: {result.text}"
2050
+ f"Operation failed. Content: {response.text}"
1958
2051
  )
1959
2052
 
1960
- if result.get("success") or not raise_on_fail:
1961
- return op_results
1962
-
1963
- for op_result in op_results:
1964
- if not op_result["success"]:
1965
- operation_id = op_result["id"]
1966
- raise FailedOperations((
1967
- "Operation \"{}\" failed with data:\n{}\nDetail: {}."
1968
- ).format(
1969
- operation_id,
1970
- json.dumps(body_by_id[operation_id], indent=4),
1971
- op_result["detail"],
1972
- ))
2053
+ if raise_on_fail:
2054
+ self._validate_operations_result(response.data, operations_body)
1973
2055
  return op_results
1974
2056
 
2057
+ def _validate_operations_result(
2058
+ self,
2059
+ result: dict[str, Any],
2060
+ operations_body: list[dict[str, Any]],
2061
+ ) -> None:
2062
+ if result.get("success"):
2063
+ return None
2064
+
2065
+ print(result)
2066
+ for op_result in result["operations"]:
2067
+ if op_result["success"]:
2068
+ continue
2069
+
2070
+ operation_id = op_result["id"]
2071
+ operation = next(
2072
+ op
2073
+ for op in operations_body
2074
+ if op["id"] == operation_id
2075
+ )
2076
+ detail = op_result["detail"]
2077
+ raise FailedOperations(
2078
+ f"Operation \"{operation_id}\" failed with data:"
2079
+ f"\n{json.dumps(operation, indent=4)}"
2080
+ f"\nDetail: {detail}."
2081
+ )
2082
+
1975
2083
  def _prepare_fields(
1976
2084
  self, entity_type: str, fields: set[str], own_attributes: bool = False
1977
2085
  ):
@@ -89,6 +89,12 @@ class EventFilter(TypedDict):
89
89
  operator: Literal["and", "or"]
90
90
 
91
91
 
92
+ class BackgroundOperationTask(TypedDict):
93
+ id: str
94
+ status: Literal["pending", "in_progress", "completed"]
95
+ result: Optional[dict[str, Any]]
96
+
97
+
92
98
  AttributeScope = Literal[
93
99
  "project",
94
100
  "folder",
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for AYON server."""
2
- __version__ = "1.2.1"
2
+ __version__ = "1.2.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -1,6 +1,6 @@
1
1
  [project]
2
- name = "ayon-python-api"
3
- version = "1.2.1"
2
+ name = "ayon_python_api"
3
+ version = "1.2.2"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
@@ -27,8 +27,8 @@ requires = ["poetry-core>=1.0.0"]
27
27
  build-backend = "poetry.core.masonry.api"
28
28
 
29
29
  [tool.poetry]
30
- name = "ayon-python-api"
31
- version = "1.2.1"
30
+ name = "ayon_python_api"
31
+ version = "1.2.2"
32
32
  description = "AYON Python API"
33
33
  authors = [
34
34
  "ynput.io <info@ynput.io>"
@@ -10,7 +10,7 @@ _version_content = {}
10
10
  exec(open(VERSION_PATH).read(), _version_content)
11
11
 
12
12
  setup(
13
- name="ayon_api",
13
+ name="ayon_python_api",
14
14
  version=_version_content["__version__"],
15
15
  py_modules=["ayon_api"],
16
16
  packages=["ayon_api", "ayon_api._api_helpers"],
File without changes