Ayon-Python-Api 0.5.1__tar.gz → 0.5.3__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.1 → ayon-python-api-0.5.3}/PKG-INFO +1 -1
  2. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/_api.py +1 -1
  3. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/constants.py +11 -0
  4. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/entity_hub.py +27 -3
  5. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/server_api.py +90 -16
  6. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/utils.py +25 -1
  7. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/version.py +1 -1
  8. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_python_api.egg-info/PKG-INFO +1 -1
  9. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/pyproject.toml +1 -1
  10. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/LICENSE +0 -0
  11. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/README.md +0 -0
  12. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/__init__.py +0 -0
  13. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/events.py +0 -0
  14. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/exceptions.py +0 -0
  15. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/graphql.py +0 -0
  16. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/graphql_queries.py +0 -0
  17. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_api/operations.py +0 -0
  18. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  19. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  20. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_python_api.egg-info/requires.txt +0 -0
  21. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/ayon_python_api.egg-info/top_level.txt +0 -0
  22. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/setup.cfg +0 -0
  23. {ayon-python-api-0.5.1 → ayon-python-api-0.5.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.5.1
3
+ Version: 0.5.3
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -374,7 +374,7 @@ def get_default_settings_variant():
374
374
  """
375
375
 
376
376
  con = get_server_api_connection()
377
- return con.get_client_version()
377
+ return con.get_default_settings_variant()
378
378
 
379
379
 
380
380
  def set_default_settings_variant(variant):
@@ -3,6 +3,10 @@ SERVER_URL_ENV_KEY = "AYON_SERVER_URL"
3
3
  SERVER_API_ENV_KEY = "AYON_API_KEY"
4
4
  SERVER_TIMEOUT_ENV_KEY = "AYON_SERVER_TIMEOUT"
5
5
  SERVER_RETRIES_ENV_KEY = "AYON_SERVER_RETRIES"
6
+ # Default variant used for settings
7
+ DEFAULT_VARIANT_ENV_KEY = "AYON_DEFAULT_SETTINGS_VARIANT"
8
+ # Default site id used for connection
9
+ SITE_ID_ENV_KEY = "AYON_SITE_ID"
6
10
 
7
11
  # Backwards compatibility
8
12
  SERVER_TOKEN_ENV_KEY = SERVER_API_ENV_KEY
@@ -40,6 +44,7 @@ DEFAULT_PROJECT_FIELDS = {
40
44
  "code",
41
45
  "config",
42
46
  "createdAt",
47
+ "data",
43
48
  }
44
49
 
45
50
  # --- Folders ---
@@ -52,6 +57,7 @@ DEFAULT_FOLDER_FIELDS = {
52
57
  "parentId",
53
58
  "active",
54
59
  "thumbnailId",
60
+ "data",
55
61
  }
56
62
 
57
63
  # --- Tasks ---
@@ -63,6 +69,7 @@ DEFAULT_TASK_FIELDS = {
63
69
  "folderId",
64
70
  "active",
65
71
  "assignees",
72
+ "data",
66
73
  }
67
74
 
68
75
  # --- Products ---
@@ -72,6 +79,7 @@ DEFAULT_PRODUCT_FIELDS = {
72
79
  "folderId",
73
80
  "active",
74
81
  "productType",
82
+ "data",
75
83
  }
76
84
 
77
85
  # --- Versions ---
@@ -86,6 +94,7 @@ DEFAULT_VERSION_FIELDS = {
86
94
  "thumbnailId",
87
95
  "createdAt",
88
96
  "updatedAt",
97
+ "data",
89
98
  }
90
99
 
91
100
  # --- Representations ---
@@ -96,6 +105,7 @@ DEFAULT_REPRESENTATION_FIELDS = {
96
105
  "createdAt",
97
106
  "active",
98
107
  "versionId",
108
+ "data",
99
109
  }
100
110
 
101
111
  REPRESENTATION_FILES_FIELDS = {
@@ -119,6 +129,7 @@ DEFAULT_WORKFILE_INFO_FIELDS = {
119
129
  "thumbnailId",
120
130
  "updatedAt",
121
131
  "updatedBy",
132
+ "data",
122
133
  }
123
134
 
124
135
  DEFAULT_EVENT_FIELDS = {
@@ -36,11 +36,20 @@ class EntityHub(object):
36
36
  """
37
37
 
38
38
  def __init__(
39
- self, project_name, connection=None, allow_data_changes=False
39
+ self, project_name, connection=None, allow_data_changes=None
40
40
  ):
41
41
  if not connection:
42
42
  connection = get_server_api_connection()
43
+ major, minor, patch, _, _ = connection.server_version_tuple
44
+ path_start_with_slash = True
45
+ if (major, minor) < (0, 6):
46
+ path_start_with_slash = False
47
+
48
+ if allow_data_changes is None:
49
+ allow_data_changes = connection.graphql_allows_data_in_query
50
+
43
51
  self._connection = connection
52
+ self._path_start_with_slash = path_start_with_slash
44
53
 
45
54
  self._project_name = project_name
46
55
  self._entities_by_id = {}
@@ -65,6 +74,18 @@ class EntityHub(object):
65
74
 
66
75
  return self._allow_data_changes
67
76
 
77
+ @property
78
+ def path_start_with_slash(self):
79
+ """Folder path should start with slash.
80
+
81
+ This changed in 0.6.x server version.
82
+
83
+ Returns:
84
+ bool: Path starts with slash.
85
+ """
86
+
87
+ return self._path_start_with_slash
88
+
68
89
  @property
69
90
  def project_name(self):
70
91
  """Project name which is maintained by hub.
@@ -2419,10 +2440,13 @@ class FolderEntity(BaseEntity):
2419
2440
 
2420
2441
  if self._path is None:
2421
2442
  parent = self.parent
2422
- path = self.name
2423
2443
  if parent.entity_type == "folder":
2424
2444
  parent_path = parent.path
2425
- path = "/".join([parent_path, path])
2445
+ path = "/".join([parent_path, self.name])
2446
+ elif self._entity_hub.path_start_with_slash:
2447
+ path = "/{}".format(self.name)
2448
+ else:
2449
+ path = self.name
2426
2450
  self._path = path
2427
2451
  return self._path
2428
2452
 
@@ -9,6 +9,9 @@ import platform
9
9
  import copy
10
10
  import uuid
11
11
  from contextlib import contextmanager
12
+
13
+ import six
14
+
12
15
  try:
13
16
  from http import HTTPStatus
14
17
  except ImportError:
@@ -27,7 +30,6 @@ except ImportError:
27
30
  from json import JSONDecodeError as RequestsJSONDecodeError
28
31
 
29
32
  from .constants import (
30
- SERVER_TIMEOUT_ENV_KEY,
31
33
  SERVER_RETRIES_ENV_KEY,
32
34
  DEFAULT_PRODUCT_TYPE_FIELDS,
33
35
  DEFAULT_PROJECT_FIELDS,
@@ -76,8 +78,11 @@ from .utils import (
76
78
  create_dependency_package_basename,
77
79
  ThumbnailContent,
78
80
  get_default_timeout,
81
+ get_default_settings_variant,
82
+ get_default_site_id,
79
83
  )
80
84
 
85
+ _PLACEHOLDER = object()
81
86
  PatternType = type(re.compile(""))
82
87
  JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
83
88
  # This should be collected from server schema
@@ -176,6 +181,11 @@ class RestApiResponse(object):
176
181
  return self.status
177
182
 
178
183
  def raise_for_status(self, message=None):
184
+ if self._response is None:
185
+ if self._data and self._data.get("detail"):
186
+ raise ServerError(self._data["detail"])
187
+ raise ValueError("Response is not available.")
188
+
179
189
  try:
180
190
  self._response.raise_for_status()
181
191
  except requests.exceptions.HTTPError as exc:
@@ -358,7 +368,7 @@ class ServerAPI(object):
358
368
  self,
359
369
  base_url,
360
370
  token=None,
361
- site_id=None,
371
+ site_id=_PLACEHOLDER,
362
372
  client_version=None,
363
373
  default_settings_variant=None,
364
374
  sender=None,
@@ -377,11 +387,14 @@ class ServerAPI(object):
377
387
  self._graphql_url = "{}/graphql".format(base_url)
378
388
  self._log = None
379
389
  self._access_token = token
390
+ # Allow to have 'site_id' to 'None'
391
+ if site_id is _PLACEHOLDER:
392
+ site_id = get_default_site_id()
380
393
  self._site_id = site_id
381
394
  self._client_version = client_version
382
395
  self._default_settings_variant = (
383
396
  default_settings_variant
384
- or "production"
397
+ or get_default_settings_variant()
385
398
  )
386
399
  self._sender = sender
387
400
 
@@ -411,6 +424,8 @@ class ServerAPI(object):
411
424
  self._server_version = None
412
425
  self._server_version_tuple = None
413
426
 
427
+ self._graphql_allows_data_in_query = None
428
+
414
429
  self._session = None
415
430
 
416
431
  self._base_functions_mapping = {
@@ -936,6 +951,26 @@ class ServerAPI(object):
936
951
  server_version = property(get_server_version)
937
952
  server_version_tuple = property(get_server_version_tuple)
938
953
 
954
+ @property
955
+ def graphql_allows_data_in_query(self):
956
+ """GraphlQl query can support 'data' field.
957
+
958
+ This applies only to project hierarchy entities 'project', 'folder',
959
+ 'task', 'product', 'version' and 'representation'. Others like 'user'
960
+ still require to use rest api to access 'data'.
961
+
962
+ Returns:
963
+ bool: True if server supports 'data' field in GraphQl query.
964
+ """
965
+
966
+ if self._graphql_allows_data_in_query is None:
967
+ major, minor, patch, _, _ = self.server_version_tuple
968
+ graphql_allows_data_in_query = True
969
+ if (major, minor, patch) < (0, 5, 5):
970
+ graphql_allows_data_in_query = False
971
+ self._graphql_allows_data_in_query = graphql_allows_data_in_query
972
+ return self._graphql_allows_data_in_query
973
+
939
974
  def _get_user_info(self):
940
975
  if self._access_token is None:
941
976
  return None
@@ -1512,8 +1547,9 @@ class ServerAPI(object):
1512
1547
  "sourceTopic": source_topic,
1513
1548
  "targetTopic": target_topic,
1514
1549
  "sender": sender,
1515
- "maxRetries": max_retries,
1516
1550
  }
1551
+ if max_retries is not None:
1552
+ kwargs["maxRetries"] = max_retries
1517
1553
  if sequential is not None:
1518
1554
  kwargs["sequential"] = sequential
1519
1555
  if description is not None:
@@ -1877,31 +1913,45 @@ class ServerAPI(object):
1877
1913
  return set(DEFAULT_EVENT_FIELDS)
1878
1914
 
1879
1915
  if entity_type == "project":
1880
- entity_type_defaults = DEFAULT_PROJECT_FIELDS
1916
+ entity_type_defaults = set(DEFAULT_PROJECT_FIELDS)
1917
+ if not self.graphql_allows_data_in_query:
1918
+ entity_type_defaults.discard("data")
1881
1919
 
1882
1920
  elif entity_type == "folder":
1883
- entity_type_defaults = DEFAULT_FOLDER_FIELDS
1921
+ entity_type_defaults = set(DEFAULT_FOLDER_FIELDS)
1922
+ if not self.graphql_allows_data_in_query:
1923
+ entity_type_defaults.discard("data")
1884
1924
 
1885
1925
  elif entity_type == "task":
1886
- entity_type_defaults = DEFAULT_TASK_FIELDS
1926
+ entity_type_defaults = set(DEFAULT_TASK_FIELDS)
1927
+ if not self.graphql_allows_data_in_query:
1928
+ entity_type_defaults.discard("data")
1887
1929
 
1888
1930
  elif entity_type == "product":
1889
- entity_type_defaults = DEFAULT_PRODUCT_FIELDS
1931
+ entity_type_defaults = set(DEFAULT_PRODUCT_FIELDS)
1932
+ if not self.graphql_allows_data_in_query:
1933
+ entity_type_defaults.discard("data")
1890
1934
 
1891
1935
  elif entity_type == "version":
1892
- entity_type_defaults = DEFAULT_VERSION_FIELDS
1936
+ entity_type_defaults = set(DEFAULT_VERSION_FIELDS)
1937
+ if not self.graphql_allows_data_in_query:
1938
+ entity_type_defaults.discard("data")
1893
1939
 
1894
1940
  elif entity_type == "representation":
1895
1941
  entity_type_defaults = (
1896
1942
  DEFAULT_REPRESENTATION_FIELDS
1897
1943
  | REPRESENTATION_FILES_FIELDS
1898
1944
  )
1945
+ if not self.graphql_allows_data_in_query:
1946
+ entity_type_defaults.discard("data")
1899
1947
 
1900
1948
  elif entity_type == "productType":
1901
- entity_type_defaults = DEFAULT_PRODUCT_TYPE_FIELDS
1949
+ entity_type_defaults = set(DEFAULT_PRODUCT_TYPE_FIELDS)
1902
1950
 
1903
1951
  elif entity_type == "workfile":
1904
- entity_type_defaults = DEFAULT_WORKFILE_INFO_FIELDS
1952
+ entity_type_defaults = set(DEFAULT_WORKFILE_INFO_FIELDS)
1953
+ if not self.graphql_allows_data_in_query:
1954
+ entity_type_defaults.discard("data")
1905
1955
 
1906
1956
  elif entity_type == "user":
1907
1957
  entity_type_defaults = set(DEFAULT_USER_FIELDS)
@@ -3653,7 +3703,7 @@ class ServerAPI(object):
3653
3703
  fields |= self.get_attributes_fields_for_type("folder")
3654
3704
 
3655
3705
  use_rest = False
3656
- if "data" in fields:
3706
+ if "data" in fields and not self.graphql_allows_data_in_query:
3657
3707
  use_rest = True
3658
3708
  fields = {"id"}
3659
3709
 
@@ -3674,6 +3724,8 @@ class ServerAPI(object):
3674
3724
 
3675
3725
  if use_rest:
3676
3726
  folder = self.get_rest_folder(project_name, folder["id"])
3727
+ else:
3728
+ self._convert_entity_data(folder)
3677
3729
 
3678
3730
  if own_attributes:
3679
3731
  fill_own_attribs(folder)
@@ -3891,7 +3943,7 @@ class ServerAPI(object):
3891
3943
  fields |= self.get_attributes_fields_for_type("task")
3892
3944
 
3893
3945
  use_rest = False
3894
- if "data" in fields:
3946
+ if "data" in fields and not self.graphql_allows_data_in_query:
3895
3947
  use_rest = True
3896
3948
  fields = {"id"}
3897
3949
 
@@ -3912,6 +3964,8 @@ class ServerAPI(object):
3912
3964
 
3913
3965
  if use_rest:
3914
3966
  task = self.get_rest_task(project_name, task["id"])
3967
+ else:
3968
+ self._convert_entity_data(task)
3915
3969
 
3916
3970
  if own_attributes:
3917
3971
  fill_own_attribs(task)
@@ -3992,6 +4046,8 @@ class ServerAPI(object):
3992
4046
 
3993
4047
  if use_rest:
3994
4048
  product = self.get_rest_product(project_name, product["id"])
4049
+ else:
4050
+ self._convert_entity_data(product)
3995
4051
 
3996
4052
  if own_attributes:
3997
4053
  fill_own_attribs(product)
@@ -4098,7 +4154,7 @@ class ServerAPI(object):
4098
4154
  fields = self.get_default_fields_for_type("product")
4099
4155
 
4100
4156
  use_rest = False
4101
- if "data" in fields:
4157
+ if "data" in fields and not self.graphql_allows_data_in_query:
4102
4158
  use_rest = True
4103
4159
  fields = {"id"}
4104
4160
 
@@ -4366,7 +4422,7 @@ class ServerAPI(object):
4366
4422
  fields |= {"id", "version"}
4367
4423
 
4368
4424
  use_rest = False
4369
- if "data" in fields:
4425
+ if "data" in fields and not self.graphql_allows_data_in_query:
4370
4426
  use_rest = True
4371
4427
  fields = {"id"}
4372
4428
 
@@ -4446,6 +4502,8 @@ class ServerAPI(object):
4446
4502
  version = self.get_rest_version(
4447
4503
  project_name, version["id"]
4448
4504
  )
4505
+ else:
4506
+ self._convert_entity_data(version)
4449
4507
 
4450
4508
  if own_attributes:
4451
4509
  fill_own_attribs(version)
@@ -4825,7 +4883,7 @@ class ServerAPI(object):
4825
4883
  fields |= self.get_attributes_fields_for_type("representation")
4826
4884
 
4827
4885
  use_rest = False
4828
- if "data" in fields:
4886
+ if "data" in fields and not self.graphql_allows_data_in_query:
4829
4887
  use_rest = True
4830
4888
  fields = {"id"}
4831
4889
 
@@ -4888,6 +4946,8 @@ class ServerAPI(object):
4888
4946
  repre = self.get_rest_representation(
4889
4947
  project_name, repre["id"]
4890
4948
  )
4949
+ else:
4950
+ self._convert_entity_data(repre)
4891
4951
 
4892
4952
  if "context" in repre:
4893
4953
  orig_context = repre["context"]
@@ -5007,6 +5067,10 @@ class ServerAPI(object):
5007
5067
  version = repre.pop("version")
5008
5068
  product = version.pop("product")
5009
5069
  folder = product.pop("folder")
5070
+ self._convert_entity_data(repre)
5071
+ self._convert_entity_data(version)
5072
+ self._convert_entity_data(product)
5073
+ self._convert_entity_data(folder)
5010
5074
  output[repre_id] = RepresentationParents(
5011
5075
  version, product, folder, project
5012
5076
  )
@@ -6317,3 +6381,13 @@ class ServerAPI(object):
6317
6381
  op_result["detail"],
6318
6382
  ))
6319
6383
  return op_results
6384
+
6385
+ def _convert_entity_data(self, entity):
6386
+ if not entity:
6387
+ return
6388
+ entity_data = entity.get("data")
6389
+ if (
6390
+ entity_data is not None
6391
+ and isinstance(entity_data, six.string_types)
6392
+ ):
6393
+ entity["data"] = json.loads(entity_data)
@@ -16,7 +16,11 @@ except ImportError:
16
16
  import requests
17
17
  import unidecode
18
18
 
19
- from .constants import SERVER_TIMEOUT_ENV_KEY
19
+ from .constants import (
20
+ SERVER_TIMEOUT_ENV_KEY,
21
+ DEFAULT_VARIANT_ENV_KEY,
22
+ SITE_ID_ENV_KEY,
23
+ )
20
24
  from .exceptions import UrlError
21
25
 
22
26
  REMOVED_VALUE = object()
@@ -46,6 +50,26 @@ def get_default_timeout():
46
50
  return 10.0
47
51
 
48
52
 
53
+ def get_default_settings_variant():
54
+ """Default settings variant.
55
+
56
+ Returns:
57
+ str: Settings variant from environment variable or 'production'.
58
+ """
59
+
60
+ return os.environ.get(DEFAULT_VARIANT_ENV_KEY) or "production"
61
+
62
+
63
+ def get_default_site_id():
64
+ """Site id used for server connection.
65
+
66
+ Returns:
67
+ Union[str, None]: Site id from environment variable or None.
68
+ """
69
+
70
+ return os.environ.get(SITE_ID_ENV_KEY)
71
+
72
+
49
73
  class ThumbnailContent:
50
74
  """Wrapper for thumbnail content.
51
75
 
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for Ayon server."""
2
- __version__ = "0.5.1"
2
+ __version__ = "0.5.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.5.1
3
+ Version: 0.5.3
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.1"
3
+ version = "0.5.3"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
File without changes