ayon-python-api 1.2.23__py3-none-any.whl → 1.2.24__py3-none-any.whl

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.
ayon_api/_api.py CHANGED
@@ -1943,6 +1943,7 @@ def create_dependency_package(
1943
1943
  checksum: str,
1944
1944
  checksum_algorithm: str,
1945
1945
  file_size: int,
1946
+ python_version: str | None = None,
1946
1947
  sources: Optional[list[dict[str, Any]]] = None,
1947
1948
  platform_name: Optional[str] = None,
1948
1949
  ) -> None:
@@ -1968,6 +1969,8 @@ def create_dependency_package(
1968
1969
  checksum (str): Checksum of archive file where dependencies are.
1969
1970
  checksum_algorithm (str): Algorithm used to calculate checksum.
1970
1971
  file_size (Optional[int]): Size of file.
1972
+ python_version (str | None): Python version for which
1973
+ is dependency package created.
1971
1974
  sources (Optional[list[dict[str, Any]]]): Information about
1972
1975
  sources from where it is possible to get file.
1973
1976
  platform_name (Optional[str]): Name of platform for which is
@@ -1984,6 +1987,7 @@ def create_dependency_package(
1984
1987
  checksum=checksum,
1985
1988
  checksum_algorithm=checksum_algorithm,
1986
1989
  file_size=file_size,
1990
+ python_version=python_version,
1987
1991
  sources=sources,
1988
1992
  platform_name=platform_name,
1989
1993
  )
@@ -57,6 +57,7 @@ class DependencyPackagesAPI(BaseServerAPI):
57
57
  checksum: str,
58
58
  checksum_algorithm: str,
59
59
  file_size: int,
60
+ python_version: str | None = None,
60
61
  sources: Optional[list[dict[str, Any]]] = None,
61
62
  platform_name: Optional[str] = None,
62
63
  ) -> None:
@@ -82,6 +83,8 @@ class DependencyPackagesAPI(BaseServerAPI):
82
83
  checksum (str): Checksum of archive file where dependencies are.
83
84
  checksum_algorithm (str): Algorithm used to calculate checksum.
84
85
  file_size (Optional[int]): Size of file.
86
+ python_version (str | None): Python version for which
87
+ is dependency package created.
85
88
  sources (Optional[list[dict[str, Any]]]): Information about
86
89
  sources from where it is possible to get file.
87
90
  platform_name (Optional[str]): Name of platform for which is
@@ -99,6 +102,9 @@ class DependencyPackagesAPI(BaseServerAPI):
99
102
  "size": file_size,
100
103
  "platform": platform_name or platform.system().lower(),
101
104
  }
105
+ if python_version:
106
+ post_body["pythonVersion"] = python_version
107
+
102
108
  if sources:
103
109
  post_body["sources"] = sources
104
110
 
ayon_api/graphql.py CHANGED
@@ -353,6 +353,25 @@ class GraphQlQuery:
353
353
  for child in self._children:
354
354
  child.parse_result(data, output, progress_data)
355
355
 
356
+ def _query_data(self, con: ServerAPI) -> dict[str, Any]:
357
+ """Send single query to server and return 'data' of the response."""
358
+ query_str = self.calculate_query()
359
+ variables = self.get_variables_values()
360
+ response = con.query_graphql(query_str, variables)
361
+ if response.errors:
362
+ raise GraphQlQueryFailed(response.errors, query_str, variables)
363
+
364
+ data = response.data.get("data")
365
+ if data is None:
366
+ # Parsing 'None' would not change pagination state and the same
367
+ # query would be sent again in an infinite loop.
368
+ raise GraphQlQueryError(
369
+ f"GraphQl query '{self._name}' response does not contain"
370
+ f" 'data'. Response: {str(response.data)[:1000]}"
371
+ f"\nQuery:\n{query_str}\nVariables: {variables}"
372
+ )
373
+ return data
374
+
356
375
  def query(self, con: ServerAPI) -> dict[str, Any]:
357
376
  """Do a query from server.
358
377
 
@@ -366,15 +385,8 @@ class GraphQlQuery:
366
385
  progress_data = {}
367
386
  output = {}
368
387
  while self.need_query:
369
- query_str = self.calculate_query()
370
- variables = self.get_variables_values()
371
- response = con.query_graphql(
372
- query_str,
373
- variables
374
- )
375
- if response.errors:
376
- raise GraphQlQueryFailed(response.errors, query_str, variables)
377
- self.parse_result(response.data["data"], output, progress_data)
388
+ data = self._query_data(con)
389
+ self.parse_result(data, output, progress_data)
378
390
 
379
391
  return output
380
392
 
@@ -394,30 +406,16 @@ class GraphQlQuery:
394
406
  if self.has_multiple_edge_fields:
395
407
  output = {}
396
408
  while self.need_query:
397
- query_str = self.calculate_query()
398
- variables = self.get_variables_values()
399
-
400
- response = con.query_graphql(query_str, variables)
401
- if response.errors:
402
- raise GraphQlQueryFailed(
403
- response.errors, query_str, variables
404
- )
405
- self.parse_result(response.data["data"], output, progress_data)
409
+ data = self._query_data(con)
410
+ self.parse_result(data, output, progress_data)
406
411
 
407
412
  yield output
408
413
 
409
414
  else:
410
415
  while self.need_query:
411
416
  output = {}
412
- query_str = self.calculate_query()
413
- variables = self.get_variables_values()
414
- response = con.query_graphql(query_str, variables)
415
- if response.errors:
416
- raise GraphQlQueryFailed(
417
- response.errors, query_str, variables
418
- )
419
-
420
- self.parse_result(response.data["data"], output, progress_data)
417
+ data = self._query_data(con)
418
+ self.parse_result(data, output, progress_data)
421
419
 
422
420
  yield output
423
421
 
@@ -903,11 +901,13 @@ class GraphQlQueryEdgeField(BaseGraphQlQueryField):
903
901
  progress_data[cursor_key] = nodes_by_cursor
904
902
 
905
903
  page_info = value["pageInfo"]
904
+ # Continue from the last received edge for both orders. Server
905
+ # returns edges of 'last' page from the newest item, so
906
+ # 'startCursor' would move the cursor back only by one item.
907
+ new_cursor = page_info["endCursor"]
906
908
  if self._order == SortOrder.ascending:
907
- new_cursor = page_info["endCursor"]
908
909
  self._need_query = page_info["hasNextPage"]
909
910
  else:
910
- new_cursor = page_info["startCursor"]
911
911
  self._need_query = page_info["hasPreviousPage"]
912
912
 
913
913
  edges = value["edges"]
@@ -943,6 +943,14 @@ class GraphQlQueryEdgeField(BaseGraphQlQueryField):
943
943
  change_cursor = False
944
944
 
945
945
  if change_cursor and self._need_query:
946
+ if new_cursor is None:
947
+ # Without cursor the pagination would start from beginning
948
+ raise GraphQlQueryError(
949
+ f"Field '{self.path}' reported another page without"
950
+ " a cursor. Stopped pagination after"
951
+ f" {self._fetched_counter} items."
952
+ )
953
+
946
954
  if new_cursor == self._cursor:
947
955
  raise GraphQlQueryError(
948
956
  "Cursor didn't change during pagination."
@@ -1016,11 +1024,7 @@ class GraphQlQueryEdgeField(BaseGraphQlQueryField):
1016
1024
  # Add page information
1017
1025
  output.append(edges_offset + "pageInfo {")
1018
1026
  for page_key in (
1019
- (
1020
- "endCursor"
1021
- if self._order == SortOrder.ascending
1022
- else "startCursor"
1023
- ),
1027
+ "endCursor",
1024
1028
  (
1025
1029
  "hasNextPage"
1026
1030
  if self._order == SortOrder.ascending
ayon_api/server_api.py CHANGED
@@ -1489,6 +1489,8 @@ class ServerAPI(
1489
1489
  response = self.raw_get(
1490
1490
  "info",
1491
1491
  handle_invalid_token=False,
1492
+ verify=self.ssl_verify,
1493
+ cert=self.cert
1492
1494
  )
1493
1495
  response.raise_for_status()
1494
1496
  return response.data
@@ -1519,7 +1521,7 @@ class ServerAPI(
1519
1521
  **kwargs
1520
1522
  ) -> RestApiResponse:
1521
1523
  kwargs.setdefault("timeout", self.timeout)
1522
- max_retries = kwargs.get("max_retries", self.max_retries)
1524
+ max_retries = kwargs.pop("max_retries", self.max_retries)
1523
1525
  if max_retries < 1:
1524
1526
  max_retries = 1
1525
1527
 
@@ -1558,6 +1560,7 @@ class ServerAPI(
1558
1560
  response = None
1559
1561
  new_response = None
1560
1562
  for retry_idx in reversed(range(max_retries)):
1563
+ new_response = None
1561
1564
  try:
1562
1565
  response = function(url, **kwargs)
1563
1566
 
@@ -1658,7 +1661,7 @@ class ServerAPI(
1658
1661
  url = self._endpoint_to_url(endpoint, use_rest=False)
1659
1662
  progress.set_source_url(url)
1660
1663
 
1661
- retries = self.get_default_max_retries()
1664
+ retries = max(self.max_retries, 1)
1662
1665
  api_prepended = False
1663
1666
  for attempt in range(retries):
1664
1667
  # Continue in download
@@ -1684,6 +1687,14 @@ class ServerAPI(
1684
1687
  progress.set_destination_url(url)
1685
1688
  continue
1686
1689
  response.raise_for_status()
1690
+ if offset > 0 and response.status_code != 206:
1691
+ # Server ignored 'Range' and sends whole file again,
1692
+ # already downloaded content must be discarded
1693
+ stream.seek(0)
1694
+ stream.truncate()
1695
+ progress.reset_transferred()
1696
+ headers.pop("Range", None)
1697
+
1687
1698
  if progress.get_content_size() is None:
1688
1699
  progress.set_content_size(
1689
1700
  response.headers["Content-length"]
@@ -1692,11 +1703,21 @@ class ServerAPI(
1692
1703
  for chunk in response.iter_content(chunk_size=chunk_size):
1693
1704
  stream.write(chunk)
1694
1705
  progress.add_transferred_chunk(len(chunk))
1706
+
1707
+ content_size = progress.get_content_size()
1708
+ transferred = progress.get_transferred_size()
1709
+ if content_size is not None and transferred != content_size:
1710
+ # Connection was closed before all content was received
1711
+ raise requests.exceptions.ConnectionError(
1712
+ f"Downloaded {transferred} out of {content_size}"
1713
+ f" bytes from '{url}'."
1714
+ )
1695
1715
  break
1696
1716
 
1697
1717
  except (
1698
1718
  requests.exceptions.Timeout,
1699
1719
  requests.exceptions.ConnectionError,
1720
+ requests.exceptions.ChunkedEncodingError,
1700
1721
  ):
1701
1722
  if attempt == retries - 1:
1702
1723
  raise
@@ -2093,7 +2114,7 @@ class ServerAPI(
2093
2114
  headers.pop(orig_key)
2094
2115
  headers[key] = value
2095
2116
 
2096
- retries = self.get_default_max_retries()
2117
+ retries = max(self.max_retries, 1)
2097
2118
  response = None
2098
2119
 
2099
2120
  # Get size of file
@@ -2676,24 +2697,32 @@ class ServerAPI(
2676
2697
  if result.get("success"):
2677
2698
  return None
2678
2699
 
2679
- print(result)
2680
- for op_result in result["operations"]:
2700
+ self.log.warning(
2701
+ "Operations failed. Server response:\n%s",
2702
+ json.dumps(result, indent=4, default=str),
2703
+ )
2704
+ for op_result in result.get("operations") or []:
2681
2705
  if op_result["success"]:
2682
2706
  continue
2683
2707
 
2684
2708
  operation_id = op_result["id"]
2685
2709
  operation = next(
2686
- op
2687
- for op in operations_body
2688
- if op["id"] == operation_id
2710
+ (op for op in operations_body if op["id"] == operation_id),
2711
+ op_result,
2689
2712
  )
2690
2713
  detail = op_result["detail"]
2691
2714
  raise FailedOperations(
2692
2715
  f"Operation \"{operation_id}\" failed with data:"
2693
- f"\n{json.dumps(operation, indent=4)}"
2716
+ f"\n{json.dumps(operation, indent=4, default=str)}"
2694
2717
  f"\nDetail: {detail}."
2695
2718
  )
2696
2719
 
2720
+ # Server did not report which operation failed
2721
+ raise FailedOperations(
2722
+ "Operations failed. Server response:"
2723
+ f"\n{json.dumps(result, indent=4, default=str)}"
2724
+ )
2725
+
2697
2726
  def _prepare_fields(
2698
2727
  self,
2699
2728
  entity_type: str,
ayon_api/utils.py CHANGED
@@ -955,7 +955,7 @@ class TransferProgress:
955
955
  """
956
956
  return self._content_size
957
957
 
958
- def set_content_size(self, content_size: int) -> None:
958
+ def set_content_size(self, content_size: int | str) -> None:
959
959
  """Set content size in bytes.
960
960
 
961
961
  Args:
@@ -967,7 +967,7 @@ class TransferProgress:
967
967
  """
968
968
  if self._content_size is not None:
969
969
  raise ValueError("Content size was set more then once")
970
- self._content_size = content_size
970
+ self._content_size = int(content_size)
971
971
 
972
972
  def get_started(self) -> bool:
973
973
  """Transfer was started.
ayon_api/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for AYON server."""
2
- __version__ = "1.2.23"
2
+ __version__ = "1.2.24"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.23
3
+ Version: 1.2.24
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -1,23 +1,23 @@
1
1
  ayon_api/__init__.py,sha256=_54OHR1kxUb07F9ck43MsOX_aTqN97G2itxSgqlENjo,16313
2
- ayon_api/_api.py,sha256=zTVzizvV6-kvPvs3uElygMT5c4KsByrsU0P9ETbssZc,262088
2
+ ayon_api/_api.py,sha256=EGHH53Kzt9JWGRIMzcLN48_QYaaIQoz7ozQNndSqJyo,262271
3
3
  ayon_api/constants.py,sha256=wnBse4dsk9zoeJOkayya3cFiGu00EXfebMNTioUclh0,4113
4
4
  ayon_api/entity_hub.py,sha256=MaMl2zbiPDbQNwaSlDV65NOCscPQqgLzIrfAqAq5LxU,119854
5
5
  ayon_api/events.py,sha256=RQ_ct_GTb3KbZ6QWBkYn5m-ZUc6NJDAFFsoy2P_w3dQ,1370
6
6
  ayon_api/exceptions.py,sha256=9inI-TkAGMKuhF84O3FwikW2esgmCdPIeRhbj2ithQU,3692
7
- ayon_api/graphql.py,sha256=9ve_6wtbm_kpItttAF4Shaaj5ARAJfssnFbM5NEZXcI,30695
7
+ ayon_api/graphql.py,sha256=I6_PQTxGkgPxLl7e0BKy4V7htV5Z8yw9OvXG9FDj1dU,31037
8
8
  ayon_api/graphql_queries.py,sha256=atupD5oe9t6WCyHy3YSY_0_uR8_c4ysb7d8SxbfZAvk,26455
9
9
  ayon_api/operations.py,sha256=Cqm9GjqO_9S55dLZ07GIZVlCuCKQ7_fX2MfhPJvKt2A,56235
10
- ayon_api/server_api.py,sha256=JorRDuYG5gGButUeJdamtu0NejiOIj6ZjTwwhIJBNng,89400
10
+ ayon_api/server_api.py,sha256=Hu1z1OZItqOxglycGDh44nVHsZwePPzv1Vq_uTU7hM8,90780
11
11
  ayon_api/typing.py,sha256=p2jNqGoh1m9_IUS0Dfr8sMoqjt60UycAkLuSbYtLY14,12286
12
- ayon_api/utils.py,sha256=0K1uY5eqZgDY5tG_vVwf9kiIqohC4x0pw-rkYpOn9mQ,36377
13
- ayon_api/version.py,sha256=X3eIgeewLKdOxtTfia6W-cZ4A_nA9E1DFQ1qkdcn9As,75
12
+ ayon_api/utils.py,sha256=DHQXzByX4m2F_9MDaL1WaI__u0f22bonDPOchIq7lno,36388
13
+ ayon_api/version.py,sha256=iyZ9soTd1NAp6X8Qui-VR3zSWsWs7VJi2viiasWmmzg,75
14
14
  ayon_api/_api_helpers/__init__.py,sha256=15wxNshWe1htzZhFnwTTflBfQ3tYJzTe6-0hrEUCQbw,1076
15
15
  ayon_api/_api_helpers/actions.py,sha256=OBRAEJSVnhGP0xf8ktQL3M7Ubz64nDmn957qZehaRQ0,10578
16
16
  ayon_api/_api_helpers/activities.py,sha256=3U-0JA_F9-FlR9Tv6JPZZ2cpo6YegXQG4yNDB0wuhF8,14029
17
17
  ayon_api/_api_helpers/attributes.py,sha256=ur9FsMWWY1K1Paw966srRj-z4EDx3nCVDLdLm9Yxn44,6949
18
18
  ayon_api/_api_helpers/base.py,sha256=I5ejQN-WNxjmsWLBcGVTnMB834T8ZzEWcezyQ6Q4Hl8,4582
19
19
  ayon_api/_api_helpers/bundles_addons.py,sha256=_AvquS0GZuUiGbCapRYu3vb_5Pp835HlbNfg9ipD3VU,31087
20
- ayon_api/_api_helpers/dependency_packages.py,sha256=nghndRoxwLCo1j9EvBtYRQYBHGoGljuUOGFpYGI7PwE,8211
20
+ ayon_api/_api_helpers/dependency_packages.py,sha256=_ADSpLDcWlsuqUppQNfMVHC_DDnZeDFvUFunP2PWx3A,8451
21
21
  ayon_api/_api_helpers/events.py,sha256=j89YfsYXox_wIvMkiyxr3l2dzkA8x-JOgGy6p-7-ndU,17229
22
22
  ayon_api/_api_helpers/folders.py,sha256=AvRx5r2V0luERFZ2Z6EOSIRh3Pe_OmLwR-HrrVrrVms,22279
23
23
  ayon_api/_api_helpers/installers.py,sha256=Q45sg6rfKUht6g4bSPHHYFj8mDo_EAm9ahg9Zk3pSUs,5552
@@ -31,8 +31,8 @@ ayon_api/_api_helpers/tasks.py,sha256=gW8QbC2V7fNQeirViKyQVugnxfnYkQGGxiv3btYRn3
31
31
  ayon_api/_api_helpers/thumbnails.py,sha256=2mQWU4cSY98J18ImYYt_5CRpm7DyOCO2wCmcLXwtdhk,12281
32
32
  ayon_api/_api_helpers/versions.py,sha256=c9ebZdM9i3J24B4bs2BRRoH937JWvRTWlJ3cFRscdug,22413
33
33
  ayon_api/_api_helpers/workfiles.py,sha256=8ksjCZ5xn7nEAKX792PeIwFNF5flslQwzbcHnJZwcu8,17679
34
- ayon_python_api-1.2.23.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
35
- ayon_python_api-1.2.23.dist-info/METADATA,sha256=lNYh638eXxiuuZXueldiKgxY_ZfepFx0RgB_Sd07bcI,16266
36
- ayon_python_api-1.2.23.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
37
- ayon_python_api-1.2.23.dist-info/top_level.txt,sha256=PKrQbX5Cz53_UmSTR_nIySnBkGyHcBTS88SCw9tuVlc,9
38
- ayon_python_api-1.2.23.dist-info/RECORD,,
34
+ ayon_python_api-1.2.24.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
35
+ ayon_python_api-1.2.24.dist-info/METADATA,sha256=rt7sHv_R3L4FSUGsm7bL3oSGO2GtYL4P4Fm_M9IJp6E,16266
36
+ ayon_python_api-1.2.24.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
37
+ ayon_python_api-1.2.24.dist-info/top_level.txt,sha256=PKrQbX5Cz53_UmSTR_nIySnBkGyHcBTS88SCw9tuVlc,9
38
+ ayon_python_api-1.2.24.dist-info/RECORD,,