ayon-python-api 1.2.5__tar.gz → 1.2.7__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 (49) hide show
  1. {ayon_python_api-1.2.5/ayon_python_api.egg-info → ayon_python_api-1.2.7}/PKG-INFO +1 -1
  2. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/projects.py +2 -2
  3. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/entity_hub.py +24 -4
  4. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/server_api.py +59 -17
  5. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/utils.py +16 -0
  6. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/version.py +1 -1
  7. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7/ayon_python_api.egg-info}/PKG-INFO +1 -1
  8. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/pyproject.toml +1 -1
  9. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/LICENSE +0 -0
  10. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/README.md +0 -0
  11. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/__init__.py +0 -0
  12. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api.py +0 -0
  13. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/__init__.py +0 -0
  14. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/actions.py +0 -0
  15. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/activities.py +0 -0
  16. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/attributes.py +0 -0
  17. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/base.py +0 -0
  18. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/bundles_addons.py +0 -0
  19. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/dependency_packages.py +0 -0
  20. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/events.py +0 -0
  21. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/folders.py +0 -0
  22. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/installers.py +0 -0
  23. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/links.py +0 -0
  24. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/lists.py +0 -0
  25. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/products.py +0 -0
  26. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/representations.py +0 -0
  27. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/secrets.py +0 -0
  28. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/tasks.py +0 -0
  29. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/thumbnails.py +0 -0
  30. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/versions.py +0 -0
  31. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/_api_helpers/workfiles.py +0 -0
  32. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/constants.py +0 -0
  33. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/events.py +0 -0
  34. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/exceptions.py +0 -0
  35. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/graphql.py +0 -0
  36. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/graphql_queries.py +0 -0
  37. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/operations.py +0 -0
  38. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_api/typing.py +0 -0
  39. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  40. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  41. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_python_api.egg-info/requires.txt +0 -0
  42. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/ayon_python_api.egg-info/top_level.txt +0 -0
  43. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/setup.cfg +0 -0
  44. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/setup.py +0 -0
  45. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/tests/test_entity_hub.py +0 -0
  46. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/tests/test_folder_hierarchy.py +0 -0
  47. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/tests/test_get_events.py +0 -0
  48. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/tests/test_graphql_queries.py +0 -0
  49. {ayon_python_api-1.2.5 → ayon_python_api-1.2.7}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.5
3
+ Version: 1.2.7
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -684,7 +684,7 @@ class ProjectsAPI(BaseServerAPI):
684
684
  must_use_graphql = True
685
685
  fields.discard(field)
686
686
  for f_name in DEFAULT_PRODUCT_TYPE_FIELDS:
687
- fields.add(f"{field}.{f_name}")
687
+ graphql_fields.add(f"{field}.{f_name}")
688
688
 
689
689
  elif field.startswith("productTypes"):
690
690
  must_use_graphql = True
@@ -694,7 +694,7 @@ class ProjectsAPI(BaseServerAPI):
694
694
  must_use_graphql = True
695
695
  fields.discard(field)
696
696
  for f_name in DEFAULT_PRODUCT_BASE_TYPE_FIELDS:
697
- fields.add(f"{field}.{f_name}")
697
+ graphql_fields.add(f"{field}.{f_name}")
698
698
 
699
699
  elif field.startswith("productBaseTypes"):
700
700
  must_use_graphql = True
@@ -1418,7 +1418,9 @@ class EntityData(dict):
1418
1418
  """
1419
1419
  def __init__(self, *args, **kwargs) -> None:
1420
1420
  super().__init__(*args, **kwargs)
1421
- self._orig_data = copy.deepcopy(self)
1421
+ self._orig_data = {}
1422
+ # Fill orig data
1423
+ self.lock()
1422
1424
 
1423
1425
  def get_changes(self) -> dict[str, Any]:
1424
1426
  """Changes in entity data.
@@ -1437,10 +1439,10 @@ class EntityData(dict):
1437
1439
  output[key] = None
1438
1440
  elif key not in self._orig_data:
1439
1441
  # New value was set
1440
- output[key] = self[key]
1442
+ output[key] = copy.deepcopy(self[key])
1441
1443
  elif self[key] != self._orig_data[key]:
1442
1444
  # Value was changed
1443
- output[key] = self[key]
1445
+ output[key] = copy.deepcopy(self[key])
1444
1446
  return output
1445
1447
 
1446
1448
  def get_new_entity_value(self) -> dict[str, AttributeValueType]:
@@ -1460,7 +1462,25 @@ class EntityData(dict):
1460
1462
  def lock(self) -> None:
1461
1463
  """Lock changes of entity data."""
1462
1464
 
1463
- self._orig_data = copy.deepcopy(self)
1465
+ orig_data = {}
1466
+ for key, value in self.items():
1467
+ try:
1468
+ key = copy.deepcopy(key)
1469
+ except RecursionError:
1470
+ raise RuntimeError(
1471
+ f"Failed to create copy of key '{key}'"
1472
+ " because of recursion."
1473
+ )
1474
+
1475
+ try:
1476
+ orig_data[key] = copy.deepcopy(value)
1477
+ except RecursionError:
1478
+ raise RuntimeError(
1479
+ f"Failed to create copy of value '{key}'"
1480
+ " because of recursion."
1481
+ )
1482
+
1483
+ self._orig_data = orig_data
1464
1484
 
1465
1485
 
1466
1486
  class BaseEntity(ABC):
@@ -1329,7 +1329,7 @@ class ServerAPI(
1329
1329
  def _endpoint_to_url(
1330
1330
  self,
1331
1331
  endpoint: str,
1332
- use_rest: Optional[bool] = True
1332
+ use_rest: bool = True,
1333
1333
  ) -> str:
1334
1334
  """Cleanup endpoint and return full url to AYON server.
1335
1335
 
@@ -1347,25 +1347,51 @@ class ServerAPI(
1347
1347
  endpoint = endpoint.lstrip("/").rstrip("/")
1348
1348
  if endpoint.startswith(self._base_url):
1349
1349
  return endpoint
1350
- base_url = self._rest_url if use_rest else self._graphql_url
1350
+ base_url = self._rest_url if use_rest else self._base_url
1351
1351
  return f"{base_url}/{endpoint}"
1352
1352
 
1353
1353
  def _download_file_to_stream(
1354
- self, url: str, stream, chunk_size, progress
1354
+ self,
1355
+ url: str,
1356
+ stream: StreamType,
1357
+ chunk_size: int,
1358
+ progress: TransferProgress,
1355
1359
  ):
1356
- kwargs = {"stream": True}
1360
+ headers = self.get_headers()
1361
+ kwargs = {
1362
+ "stream": True,
1363
+ "headers": headers,
1364
+ }
1357
1365
  if self._session is None:
1358
- kwargs["headers"] = self.get_headers()
1359
1366
  get_func = self._base_functions_mapping[RequestTypes.get]
1360
1367
  else:
1361
1368
  get_func = self._session_functions_mapping[RequestTypes.get]
1362
1369
 
1363
- with get_func(url, **kwargs) as response:
1364
- response.raise_for_status()
1365
- progress.set_content_size(response.headers["Content-length"])
1366
- for chunk in response.iter_content(chunk_size=chunk_size):
1367
- stream.write(chunk)
1368
- progress.add_transferred_chunk(len(chunk))
1370
+ retries = self.get_default_max_retries()
1371
+ for attempt in range(retries):
1372
+ # Continue in download
1373
+ offset = progress.get_transferred_size()
1374
+ if offset > 0:
1375
+ headers["Range"] = f"bytes={offset}-"
1376
+
1377
+ try:
1378
+ with get_func(url, **kwargs) as response:
1379
+ response.raise_for_status()
1380
+ progress.set_content_size(
1381
+ response.headers["Content-length"]
1382
+ )
1383
+ for chunk in response.iter_content(chunk_size=chunk_size):
1384
+ stream.write(chunk)
1385
+ progress.add_transferred_chunk(len(chunk))
1386
+ break
1387
+
1388
+ except (
1389
+ requests.exceptions.Timeout,
1390
+ requests.exceptions.ConnectionError,
1391
+ ):
1392
+ if attempt == retries:
1393
+ raise
1394
+ progress.next_attempt()
1369
1395
 
1370
1396
  def download_file_to_stream(
1371
1397
  self,
@@ -1399,7 +1425,7 @@ class ServerAPI(
1399
1425
  if not chunk_size:
1400
1426
  chunk_size = self.default_download_chunk_size
1401
1427
 
1402
- url = self._endpoint_to_url(endpoint)
1428
+ url = self._endpoint_to_url(endpoint, use_rest=False)
1403
1429
 
1404
1430
  if progress is None:
1405
1431
  progress = TransferProgress()
@@ -1543,11 +1569,27 @@ class ServerAPI(
1543
1569
  if not chunk_size:
1544
1570
  chunk_size = self.default_upload_chunk_size
1545
1571
 
1546
- response = post_func(
1547
- url,
1548
- data=self._upload_chunks_iter(stream, progress, chunk_size),
1549
- **kwargs
1550
- )
1572
+ retries = self.get_default_max_retries()
1573
+ response = None
1574
+ for attempt in range(retries):
1575
+ try:
1576
+ response = post_func(
1577
+ url,
1578
+ data=self._upload_chunks_iter(
1579
+ stream, progress, chunk_size
1580
+ ),
1581
+ **kwargs
1582
+ )
1583
+ break
1584
+
1585
+ except (
1586
+ requests.exceptions.Timeout,
1587
+ requests.exceptions.ConnectionError,
1588
+ ):
1589
+ if attempt == retries:
1590
+ raise
1591
+ progress.next_attempt()
1592
+ progress.reset_transferred()
1551
1593
 
1552
1594
  response.raise_for_status()
1553
1595
  return response
@@ -796,6 +796,7 @@ class TransferProgress:
796
796
  """Object to store progress of download/upload from/to server."""
797
797
 
798
798
  def __init__(self):
799
+ self._attempt: int = 0
799
800
  self._started: bool = False
800
801
  self._transfer_done: bool = False
801
802
  self._transferred: int = 0
@@ -850,6 +851,17 @@ class TransferProgress:
850
851
  if self._started:
851
852
  raise ValueError("Progress already started")
852
853
  self._started = True
854
+ self._attempt = 1
855
+
856
+ def get_attempt(self) -> int:
857
+ """Find out which attempt of progress it is."""
858
+ return self._attempt
859
+
860
+ def next_attempt(self) -> None:
861
+ """Start new attempt of progress."""
862
+ if not self._started:
863
+ raise ValueError("Progress did not start yet")
864
+ self._attempt += 1
853
865
 
854
866
  def get_transfer_done(self) -> bool:
855
867
  """Transfer finished.
@@ -921,6 +933,10 @@ class TransferProgress:
921
933
  """
922
934
  self._transferred = transferred
923
935
 
936
+ def reset_transferred(self) -> None:
937
+ """Reset transferred size to initial value."""
938
+ self._transferred = 0
939
+
924
940
  def add_transferred_chunk(self, chunk_size: int):
925
941
  """Add transferred chunk size in bytes.
926
942
 
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for AYON server."""
2
- __version__ = "1.2.5"
2
+ __version__ = "1.2.7"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.5
3
+ Version: 1.2.7
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
2
  name = "ayon_python_api"
3
- version = "1.2.5"
3
+ version = "1.2.7"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
File without changes