ayon-python-api 0.5.3__tar.gz → 0.5.4__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 (23) hide show
  1. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/PKG-INFO +1 -1
  2. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/entity_hub.py +4 -1
  3. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/server_api.py +114 -34
  4. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/version.py +1 -1
  5. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/PKG-INFO +1 -1
  6. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/pyproject.toml +1 -1
  7. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/LICENSE +0 -0
  8. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/README.md +0 -0
  9. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/__init__.py +0 -0
  10. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/_api.py +0 -0
  11. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/constants.py +0 -0
  12. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/events.py +0 -0
  13. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/exceptions.py +0 -0
  14. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/graphql.py +0 -0
  15. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/graphql_queries.py +0 -0
  16. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/operations.py +0 -0
  17. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_api/utils.py +0 -0
  18. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  19. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  20. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/requires.txt +0 -0
  21. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/top_level.txt +0 -0
  22. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/setup.cfg +0 -0
  23. {ayon-python-api-0.5.3 → ayon-python-api-0.5.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.5.3
3
+ Version: 0.5.4
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -2549,7 +2549,10 @@ class FolderEntity(BaseEntity):
2549
2549
  if self.thumbnail_id is not UNKNOWN_VALUE:
2550
2550
  output["thumbnailId"] = self.thumbnail_id
2551
2551
 
2552
- if self._entity_hub.allow_data_changes:
2552
+ if (
2553
+ self._entity_hub.allow_data_changes
2554
+ and self._data is not UNKNOWN_VALUE
2555
+ ):
2553
2556
  output["data"] = self._data
2554
2557
  return output
2555
2558
 
@@ -363,6 +363,10 @@ class ServerAPI(object):
363
363
  max_retries (Optional[int]): Number of retries for requests.
364
364
  """
365
365
  _default_max_retries = 3
366
+ # 1 MB chunk by default
367
+ # TODO find out if these are reasonable default value
368
+ default_download_chunk_size = 1024 * 1024
369
+ default_upload_chunk_size = 1024 * 1024
366
370
 
367
371
  def __init__(
368
372
  self,
@@ -1596,6 +1600,10 @@ class ServerAPI(object):
1596
1600
  download happens in thread and other thread want to catch changes over
1597
1601
  time.
1598
1602
 
1603
+ Todos:
1604
+ Use retries and timeout.
1605
+ Return RestApiResponse.
1606
+
1599
1607
  Args:
1600
1608
  endpoint (str): Endpoint or URL to file that should be downloaded.
1601
1609
  filepath (str): Path where file will be downloaded.
@@ -1606,8 +1614,7 @@ class ServerAPI(object):
1606
1614
  """
1607
1615
 
1608
1616
  if not chunk_size:
1609
- # 1 MB chunk by default
1610
- chunk_size = 1024 * 1024
1617
+ chunk_size = self.default_download_chunk_size
1611
1618
 
1612
1619
  if endpoint.startswith(self._base_url):
1613
1620
  url = endpoint
@@ -1634,33 +1641,93 @@ class ServerAPI(object):
1634
1641
  progress.set_transfer_done()
1635
1642
  return progress
1636
1643
 
1637
- def _upload_file(self, url, filepath, progress, request_type=None):
1644
+ @staticmethod
1645
+ def _upload_chunks_iter(file_stream, progress, chunk_size):
1646
+ """Generator that yields chunks of file.
1647
+
1648
+ Args:
1649
+ file_stream (io.BinaryIO): Byte stream.
1650
+ progress (TransferProgress): Object to track upload progress.
1651
+ chunk_size (int): Size of chunks that are uploaded at once.
1652
+
1653
+ Yields:
1654
+ bytes: Chunk of file.
1655
+ """
1656
+
1657
+ # Get size of file
1658
+ file_stream.seek(0, io.SEEK_END)
1659
+ size = file_stream.tell()
1660
+ file_stream.seek(0)
1661
+ # Set content size to progress object
1662
+ progress.set_content_size(size)
1663
+
1664
+ while True:
1665
+ chunk = file_stream.read(chunk_size)
1666
+ if not chunk:
1667
+ break
1668
+ progress.add_transferred_chunk(len(chunk))
1669
+ yield chunk
1670
+
1671
+ def _upload_file(
1672
+ self,
1673
+ url,
1674
+ filepath,
1675
+ progress,
1676
+ request_type=None,
1677
+ chunk_size=None,
1678
+ **kwargs
1679
+ ):
1680
+ """
1681
+
1682
+ Args:
1683
+ url (str): Url where file will be uploaded.
1684
+ filepath (str): Source filepath.
1685
+ progress (TransferProgress): Object that gives ability to track
1686
+ progress.
1687
+ request_type (Optional[RequestType]): Type of request that will
1688
+ be used. Default is PUT.
1689
+ chunk_size (Optional[int]): Size of chunks that are uploaded
1690
+ at once.
1691
+ **kwargs (Any): Additional arguments that will be passed
1692
+ to request function.
1693
+
1694
+ Returns:
1695
+ RestApiResponse: Server response.
1696
+ """
1697
+
1638
1698
  if request_type is None:
1639
1699
  request_type = RequestTypes.put
1640
- kwargs = {}
1700
+
1641
1701
  if self._session is None:
1642
- kwargs["headers"] = self.get_headers()
1702
+ headers = kwargs.setdefault("headers", {})
1703
+ for key, value in self.get_headers().items():
1704
+ if key not in headers:
1705
+ headers[key] = value
1643
1706
  post_func = self._base_functions_mapping[request_type]
1644
1707
  else:
1645
1708
  post_func = self._session_functions_mapping[request_type]
1646
1709
 
1710
+ if not chunk_size:
1711
+ chunk_size = self.default_upload_chunk_size
1712
+
1647
1713
  with open(filepath, "rb") as stream:
1648
- stream.seek(0, io.SEEK_END)
1649
- size = stream.tell()
1650
- stream.seek(0)
1651
- progress.set_content_size(size)
1652
- response = post_func(url, data=stream, **kwargs)
1714
+ response = post_func(
1715
+ url,
1716
+ data=self._upload_chunks_iter(stream, progress, chunk_size),
1717
+ **kwargs
1718
+ )
1719
+
1653
1720
  response.raise_for_status()
1654
- progress.set_transferred_size(size)
1655
1721
  return response
1656
1722
 
1657
1723
  def upload_file(
1658
- self, endpoint, filepath, progress=None, request_type=None
1724
+ self, endpoint, filepath, progress=None, request_type=None, **kwargs
1659
1725
  ):
1660
1726
  """Upload file to server.
1661
1727
 
1662
1728
  Todos:
1663
- Uploading with more detailed progress.
1729
+ Use retries and timeout.
1730
+ Return RestApiResponse.
1664
1731
 
1665
1732
  Args:
1666
1733
  endpoint (str): Endpoint or url where file will be uploaded.
@@ -1669,6 +1736,8 @@ class ServerAPI(object):
1669
1736
  to track upload progress.
1670
1737
  request_type (Optional[RequestType]): Type of request that will
1671
1738
  be used to upload file.
1739
+ **kwargs (Any): Additional arguments that will be passed
1740
+ to request function.
1672
1741
 
1673
1742
  Returns:
1674
1743
  requests.Response: Response object.
@@ -1690,7 +1759,9 @@ class ServerAPI(object):
1690
1759
  progress.set_started()
1691
1760
 
1692
1761
  try:
1693
- return self._upload_file(url, filepath, progress, request_type)
1762
+ return self._upload_file(
1763
+ url, filepath, progress, request_type, **kwargs
1764
+ )
1694
1765
 
1695
1766
  except Exception as exc:
1696
1767
  progress.set_failed(str(exc))
@@ -4719,7 +4790,7 @@ class ServerAPI(object):
4719
4790
  own_attributes=own_attributes
4720
4791
  )
4721
4792
  return {
4722
- version["parent"]: version
4793
+ version["productId"]: version
4723
4794
  for version in versions
4724
4795
  }
4725
4796
 
@@ -4833,6 +4904,23 @@ class ServerAPI(object):
4833
4904
  )
4834
4905
  return latest_version["id"] == version_id
4835
4906
 
4907
+ def _representation_conversion(self, representation):
4908
+ if "context" in representation:
4909
+ orig_context = representation["context"]
4910
+ context = {}
4911
+ if orig_context and orig_context != "null":
4912
+ context = json.loads(orig_context)
4913
+ representation["context"] = context
4914
+
4915
+ repre_files = representation.get("files")
4916
+ if not repre_files:
4917
+ return
4918
+
4919
+ for repre_file in repre_files:
4920
+ repre_file_size = repre_file.get("size")
4921
+ if repre_file_size is not None:
4922
+ repre_file["size"] = int(repre_file["size"])
4923
+
4836
4924
  def get_representations(
4837
4925
  self,
4838
4926
  project_name,
@@ -4880,7 +4968,9 @@ class ServerAPI(object):
4880
4968
  fields = set(fields)
4881
4969
  if "attrib" in fields:
4882
4970
  fields.remove("attrib")
4883
- fields |= self.get_attributes_fields_for_type("representation")
4971
+ fields |= self.get_attributes_fields_for_type(
4972
+ "representation"
4973
+ )
4884
4974
 
4885
4975
  use_rest = False
4886
4976
  if "data" in fields and not self.graphql_allows_data_in_query:
@@ -4949,12 +5039,7 @@ class ServerAPI(object):
4949
5039
  else:
4950
5040
  self._convert_entity_data(repre)
4951
5041
 
4952
- if "context" in repre:
4953
- orig_context = repre["context"]
4954
- context = {}
4955
- if orig_context and orig_context != "null":
4956
- context = json.loads(orig_context)
4957
- repre["context"] = context
5042
+ self._representation_conversion(repre)
4958
5043
 
4959
5044
  if own_attributes:
4960
5045
  fill_own_attribs(repre)
@@ -5067,7 +5152,6 @@ class ServerAPI(object):
5067
5152
  version = repre.pop("version")
5068
5153
  product = version.pop("product")
5069
5154
  folder = product.pop("folder")
5070
- self._convert_entity_data(repre)
5071
5155
  self._convert_entity_data(version)
5072
5156
  self._convert_entity_data(product)
5073
5157
  self._convert_entity_data(folder)
@@ -5523,16 +5607,14 @@ class ServerAPI(object):
5523
5607
  return thumbnail_id
5524
5608
 
5525
5609
  mime_type = self._get_thumbnail_mime_type(src_filepath)
5526
- with open(src_filepath, "rb") as stream:
5527
- content = stream.read()
5528
-
5529
- response = self.raw_post(
5610
+ response = self.upload_file(
5530
5611
  "projects/{}/thumbnails".format(project_name),
5612
+ src_filepath,
5613
+ request_type=RequestTypes.post,
5531
5614
  headers={"Content-Type": mime_type},
5532
- data=content
5533
5615
  )
5534
5616
  response.raise_for_status()
5535
- return response.data["id"]
5617
+ return response.json()["id"]
5536
5618
 
5537
5619
  def update_thumbnail(self, project_name, thumbnail_id, src_filepath):
5538
5620
  """Change thumbnail content by id.
@@ -5553,13 +5635,11 @@ class ServerAPI(object):
5553
5635
  raise ValueError("Entered filepath does not exist.")
5554
5636
 
5555
5637
  mime_type = self._get_thumbnail_mime_type(src_filepath)
5556
- with open(src_filepath, "rb") as stream:
5557
- content = stream.read()
5558
-
5559
- response = self.raw_put(
5638
+ response = self.upload_file(
5560
5639
  "projects/{}/thumbnails/{}".format(project_name, thumbnail_id),
5640
+ src_filepath,
5641
+ request_type=RequestTypes.put,
5561
5642
  headers={"Content-Type": mime_type},
5562
- data=content
5563
5643
  )
5564
5644
  response.raise_for_status()
5565
5645
 
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for Ayon server."""
2
- __version__ = "0.5.3"
2
+ __version__ = "0.5.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.5.3
3
+ Version: 0.5.4
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 = "0.5.3"
3
+ version = "0.5.4"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
File without changes