Ayon-Python-Api 0.3.5__tar.gz → 0.3.6__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.
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/PKG-INFO +1 -1
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/__init__.py +10 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/_api.py +20 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/constants.py +3 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/graphql_queries.py +5 -1
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/server_api.py +172 -29
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/version.py +1 -1
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/PKG-INFO +1 -1
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/pyproject.toml +1 -1
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/LICENSE +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/README.md +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/entity_hub.py +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/events.py +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/exceptions.py +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/graphql.py +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/operations.py +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_api/utils.py +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/SOURCES.txt +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/dependency_links.txt +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/requires.txt +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/top_level.txt +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/setup.cfg +0 -0
- {ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/setup.py +0 -0
|
@@ -48,6 +48,11 @@ from ._api import (
|
|
|
48
48
|
patch,
|
|
49
49
|
delete,
|
|
50
50
|
|
|
51
|
+
get_timeout,
|
|
52
|
+
set_timeout,
|
|
53
|
+
get_max_retries,
|
|
54
|
+
set_max_retries,
|
|
55
|
+
|
|
51
56
|
get_event,
|
|
52
57
|
get_events,
|
|
53
58
|
dispatch_event,
|
|
@@ -245,6 +250,11 @@ __all__ = (
|
|
|
245
250
|
"patch",
|
|
246
251
|
"delete",
|
|
247
252
|
|
|
253
|
+
"get_timeout",
|
|
254
|
+
"set_timeout",
|
|
255
|
+
"get_max_retries",
|
|
256
|
+
"set_max_retries",
|
|
257
|
+
|
|
248
258
|
"get_event",
|
|
249
259
|
"get_events",
|
|
250
260
|
"dispatch_event",
|
|
@@ -474,6 +474,26 @@ def delete(*args, **kwargs):
|
|
|
474
474
|
return con.delete(*args, **kwargs)
|
|
475
475
|
|
|
476
476
|
|
|
477
|
+
def get_timeout(*args, **kwargs):
|
|
478
|
+
con = get_server_api_connection()
|
|
479
|
+
return con.get_timeout(*args, **kwargs)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def set_timeout(*args, **kwargs):
|
|
483
|
+
con = get_server_api_connection()
|
|
484
|
+
return con.set_timeout(*args, **kwargs)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def get_max_retries(*args, **kwargs):
|
|
488
|
+
con = get_server_api_connection()
|
|
489
|
+
return con.get_max_retries(*args, **kwargs)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def set_max_retries(*args, **kwargs):
|
|
493
|
+
con = get_server_api_connection()
|
|
494
|
+
return con.set_max_retries(*args, **kwargs)
|
|
495
|
+
|
|
496
|
+
|
|
477
497
|
def get_event(*args, **kwargs):
|
|
478
498
|
con = get_server_api_connection()
|
|
479
499
|
return con.get_event(*args, **kwargs)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Environments where server url and api key are stored for global connection
|
|
2
2
|
SERVER_URL_ENV_KEY = "AYON_SERVER_URL"
|
|
3
3
|
SERVER_API_ENV_KEY = "AYON_API_KEY"
|
|
4
|
+
SERVER_TIMEOUT_ENV_KEY = "AYON_SERVER_TIMEOUT"
|
|
5
|
+
SERVER_RETRIES_ENV_KEY = "AYON_SERVER_RETRIES"
|
|
6
|
+
|
|
4
7
|
# Backwards compatibility
|
|
5
8
|
SERVER_TOKEN_ENV_KEY = SERVER_API_ENV_KEY
|
|
6
9
|
|
|
@@ -247,9 +247,11 @@ def products_graphql_query(fields):
|
|
|
247
247
|
query = GraphQlQuery("ProductsQuery")
|
|
248
248
|
|
|
249
249
|
project_name_var = query.add_variable("projectName", "String!")
|
|
250
|
-
folder_ids_var = query.add_variable("folderIds", "[String!]")
|
|
251
250
|
product_ids_var = query.add_variable("productIds", "[String!]")
|
|
252
251
|
product_names_var = query.add_variable("productNames", "[String!]")
|
|
252
|
+
folder_ids_var = query.add_variable("folderIds", "[String!]")
|
|
253
|
+
product_types_var = query.add_variable("productTypes", "[String!]")
|
|
254
|
+
statuses_var = query.add_variable("statuses", "[String!]")
|
|
253
255
|
|
|
254
256
|
project_field = query.add_field("project")
|
|
255
257
|
project_field.set_filter("name", project_name_var)
|
|
@@ -258,6 +260,8 @@ def products_graphql_query(fields):
|
|
|
258
260
|
products_field.set_filter("ids", product_ids_var)
|
|
259
261
|
products_field.set_filter("names", product_names_var)
|
|
260
262
|
products_field.set_filter("folderIds", folder_ids_var)
|
|
263
|
+
products_field.set_filter("productTypes", product_types_var)
|
|
264
|
+
products_field.set_filter("statuses", statuses_var)
|
|
261
265
|
|
|
262
266
|
nested_fields = fields_to_dict(set(fields))
|
|
263
267
|
add_links_fields(products_field, nested_fields)
|
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
import re
|
|
3
3
|
import io
|
|
4
4
|
import json
|
|
5
|
+
import time
|
|
5
6
|
import logging
|
|
6
7
|
import collections
|
|
7
8
|
import platform
|
|
@@ -26,6 +27,8 @@ except ImportError:
|
|
|
26
27
|
from json import JSONDecodeError as RequestsJSONDecodeError
|
|
27
28
|
|
|
28
29
|
from .constants import (
|
|
30
|
+
SERVER_TIMEOUT_ENV_KEY,
|
|
31
|
+
SERVER_RETRIES_ENV_KEY,
|
|
29
32
|
DEFAULT_PRODUCT_TYPE_FIELDS,
|
|
30
33
|
DEFAULT_PROJECT_FIELDS,
|
|
31
34
|
DEFAULT_FOLDER_FIELDS,
|
|
@@ -127,6 +130,8 @@ class RestApiResponse(object):
|
|
|
127
130
|
|
|
128
131
|
@property
|
|
129
132
|
def text(self):
|
|
133
|
+
if self._response is None:
|
|
134
|
+
return self.detail
|
|
130
135
|
return self._response.text
|
|
131
136
|
|
|
132
137
|
@property
|
|
@@ -135,6 +140,8 @@ class RestApiResponse(object):
|
|
|
135
140
|
|
|
136
141
|
@property
|
|
137
142
|
def headers(self):
|
|
143
|
+
if self._response is None:
|
|
144
|
+
return {}
|
|
138
145
|
return self._response.headers
|
|
139
146
|
|
|
140
147
|
@property
|
|
@@ -148,6 +155,8 @@ class RestApiResponse(object):
|
|
|
148
155
|
|
|
149
156
|
@property
|
|
150
157
|
def content(self):
|
|
158
|
+
if self._response is None:
|
|
159
|
+
return b""
|
|
151
160
|
return self._response.content
|
|
152
161
|
|
|
153
162
|
@property
|
|
@@ -339,7 +348,11 @@ class ServerAPI(object):
|
|
|
339
348
|
variable value 'AYON_CERT_FILE' by default.
|
|
340
349
|
create_session (Optional[bool]): Create session for connection if
|
|
341
350
|
token is available. Default is True.
|
|
351
|
+
timeout (Optional[float]): Timeout for requests.
|
|
352
|
+
max_retries (Optional[int]): Number of retries for requests.
|
|
342
353
|
"""
|
|
354
|
+
_default_timeout = 10.0
|
|
355
|
+
_default_max_retries = 3
|
|
343
356
|
|
|
344
357
|
def __init__(
|
|
345
358
|
self,
|
|
@@ -352,6 +365,8 @@ class ServerAPI(object):
|
|
|
352
365
|
ssl_verify=None,
|
|
353
366
|
cert=None,
|
|
354
367
|
create_session=True,
|
|
368
|
+
timeout=None,
|
|
369
|
+
max_retries=None,
|
|
355
370
|
):
|
|
356
371
|
if not base_url:
|
|
357
372
|
raise ValueError("Invalid server URL {}".format(str(base_url)))
|
|
@@ -370,6 +385,9 @@ class ServerAPI(object):
|
|
|
370
385
|
)
|
|
371
386
|
self._sender = sender
|
|
372
387
|
|
|
388
|
+
self._timeout = timeout
|
|
389
|
+
self._max_retries = max_retries
|
|
390
|
+
|
|
373
391
|
if ssl_verify is None:
|
|
374
392
|
# Custom AYON env variable for CA file or 'True'
|
|
375
393
|
# - that should cover most default behaviors in 'requests'
|
|
@@ -474,6 +492,87 @@ class ServerAPI(object):
|
|
|
474
492
|
ssl_verify = property(get_ssl_verify, set_ssl_verify)
|
|
475
493
|
cert = property(get_cert, set_cert)
|
|
476
494
|
|
|
495
|
+
@classmethod
|
|
496
|
+
def get_default_timeout(cls):
|
|
497
|
+
"""Default value for requests timeout.
|
|
498
|
+
|
|
499
|
+
First looks for environment variable SERVER_TIMEOUT_ENV_KEY which
|
|
500
|
+
can affect timeout value. If not available then use class
|
|
501
|
+
attribute '_default_timeout'.
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
float: Timeout value in seconds.
|
|
505
|
+
"""
|
|
506
|
+
|
|
507
|
+
try:
|
|
508
|
+
return float(os.environ.get(SERVER_TIMEOUT_ENV_KEY))
|
|
509
|
+
except (ValueError, TypeError):
|
|
510
|
+
pass
|
|
511
|
+
|
|
512
|
+
return cls._default_timeout
|
|
513
|
+
|
|
514
|
+
@classmethod
|
|
515
|
+
def get_default_max_retries(cls):
|
|
516
|
+
"""Default value for requests max retries.
|
|
517
|
+
|
|
518
|
+
First looks for environment variable SERVER_RETRIES_ENV_KEY, which
|
|
519
|
+
can affect max retries value. If not available then use class
|
|
520
|
+
attribute '_default_max_retries'.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
int: Max retries value.
|
|
524
|
+
"""
|
|
525
|
+
|
|
526
|
+
try:
|
|
527
|
+
return int(os.environ.get(SERVER_RETRIES_ENV_KEY))
|
|
528
|
+
except (ValueError, TypeError):
|
|
529
|
+
pass
|
|
530
|
+
|
|
531
|
+
return cls._default_max_retries
|
|
532
|
+
|
|
533
|
+
def get_timeout(self):
|
|
534
|
+
"""Current value for requests timeout.
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
float: Timeout value in seconds.
|
|
538
|
+
"""
|
|
539
|
+
|
|
540
|
+
return self._timeout
|
|
541
|
+
|
|
542
|
+
def set_timeout(self, timeout):
|
|
543
|
+
"""Change timeout value for requests.
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
timeout (Union[float, None]): Timeout value in seconds.
|
|
547
|
+
"""
|
|
548
|
+
|
|
549
|
+
if timeout is None:
|
|
550
|
+
timeout = self.get_default_timeout()
|
|
551
|
+
self._timeout = float(timeout)
|
|
552
|
+
|
|
553
|
+
def get_max_retries(self):
|
|
554
|
+
"""Current value for requests max retries.
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
int: Max retries value.
|
|
558
|
+
"""
|
|
559
|
+
|
|
560
|
+
return self._max_retries
|
|
561
|
+
|
|
562
|
+
def set_max_retries(self, max_retries):
|
|
563
|
+
"""Change max retries value for requests.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
max_retries (Union[int, None]): Max retries value.
|
|
567
|
+
"""
|
|
568
|
+
|
|
569
|
+
if max_retries is None:
|
|
570
|
+
max_retries = self.get_default_max_retries()
|
|
571
|
+
self._max_retries = int(max_retries)
|
|
572
|
+
|
|
573
|
+
timeout = property(get_timeout, set_timeout)
|
|
574
|
+
max_retries = property(get_max_retries, set_max_retries)
|
|
575
|
+
|
|
477
576
|
@property
|
|
478
577
|
def access_token(self):
|
|
479
578
|
"""Access token used for authorization to server.
|
|
@@ -1004,6 +1103,10 @@ class ServerAPI(object):
|
|
|
1004
1103
|
logout_from_server(self._base_url, self._access_token)
|
|
1005
1104
|
|
|
1006
1105
|
def _do_rest_request(self, function, url, **kwargs):
|
|
1106
|
+
kwargs.setdefault("timeout", self.timeout)
|
|
1107
|
+
max_retries = kwargs.get("max_retries", self.max_retries)
|
|
1108
|
+
if max_retries < 1:
|
|
1109
|
+
max_retries = 1
|
|
1007
1110
|
if self._session is None:
|
|
1008
1111
|
# Validate token if was not yet validated
|
|
1009
1112
|
# - ignore validation if we're in middle of
|
|
@@ -1023,38 +1126,54 @@ class ServerAPI(object):
|
|
|
1023
1126
|
elif isinstance(function, RequestType):
|
|
1024
1127
|
function = self._session_functions_mapping[function]
|
|
1025
1128
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1129
|
+
response = None
|
|
1130
|
+
new_response = None
|
|
1131
|
+
for _ in range(max_retries):
|
|
1132
|
+
try:
|
|
1133
|
+
response = function(url, **kwargs)
|
|
1134
|
+
break
|
|
1135
|
+
|
|
1136
|
+
except ConnectionRefusedError:
|
|
1137
|
+
# Server may be restarting
|
|
1138
|
+
new_response = RestApiResponse(
|
|
1139
|
+
None,
|
|
1140
|
+
{"detail": "Unable to connect the server. Connection refused"}
|
|
1141
|
+
)
|
|
1142
|
+
except requests.exceptions.Timeout:
|
|
1143
|
+
# Connection timed out
|
|
1144
|
+
new_response = RestApiResponse(
|
|
1145
|
+
None,
|
|
1146
|
+
{"detail": "Connection timed out."}
|
|
1147
|
+
)
|
|
1148
|
+
except requests.exceptions.ConnectionError:
|
|
1149
|
+
# Other connection error (ssl, etc) - does not make sense to
|
|
1150
|
+
# try call server again
|
|
1151
|
+
new_response = RestApiResponse(
|
|
1152
|
+
None,
|
|
1153
|
+
{"detail": "Unable to connect the server. Connection error"}
|
|
1154
|
+
)
|
|
1155
|
+
break
|
|
1028
1156
|
|
|
1029
|
-
|
|
1030
|
-
new_response = RestApiResponse(
|
|
1031
|
-
None,
|
|
1032
|
-
{"detail": "Unable to connect the server. Connection refused"}
|
|
1033
|
-
)
|
|
1034
|
-
except requests.exceptions.ConnectionError:
|
|
1035
|
-
new_response = RestApiResponse(
|
|
1036
|
-
None,
|
|
1037
|
-
{"detail": "Unable to connect the server. Connection error"}
|
|
1038
|
-
)
|
|
1039
|
-
else:
|
|
1040
|
-
content_type = response.headers.get("Content-Type")
|
|
1041
|
-
if content_type == "application/json":
|
|
1042
|
-
try:
|
|
1043
|
-
new_response = RestApiResponse(response)
|
|
1044
|
-
except JSONDecodeError:
|
|
1045
|
-
new_response = RestApiResponse(
|
|
1046
|
-
None,
|
|
1047
|
-
{
|
|
1048
|
-
"detail": "The response is not a JSON: {}".format(
|
|
1049
|
-
response.text)
|
|
1050
|
-
}
|
|
1051
|
-
)
|
|
1157
|
+
time.sleep(0.1)
|
|
1052
1158
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1159
|
+
if new_response is not None:
|
|
1160
|
+
return new_response
|
|
1055
1161
|
|
|
1056
|
-
|
|
1162
|
+
content_type = response.headers.get("Content-Type")
|
|
1163
|
+
if content_type == "application/json":
|
|
1164
|
+
try:
|
|
1057
1165
|
new_response = RestApiResponse(response)
|
|
1166
|
+
except JSONDecodeError:
|
|
1167
|
+
new_response = RestApiResponse(
|
|
1168
|
+
None,
|
|
1169
|
+
{
|
|
1170
|
+
"detail": "The response is not a JSON: {}".format(
|
|
1171
|
+
response.text)
|
|
1172
|
+
}
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
else:
|
|
1176
|
+
new_response = RestApiResponse(response)
|
|
1058
1177
|
|
|
1059
1178
|
self.log.debug("Response {}".format(str(new_response)))
|
|
1060
1179
|
return new_response
|
|
@@ -3810,6 +3929,8 @@ class ServerAPI(object):
|
|
|
3810
3929
|
product_ids=None,
|
|
3811
3930
|
product_names=None,
|
|
3812
3931
|
folder_ids=None,
|
|
3932
|
+
product_types=None,
|
|
3933
|
+
statuses=None,
|
|
3813
3934
|
names_by_folder_ids=None,
|
|
3814
3935
|
active=True,
|
|
3815
3936
|
fields=None,
|
|
@@ -3828,6 +3949,10 @@ class ServerAPI(object):
|
|
|
3828
3949
|
filtering.
|
|
3829
3950
|
folder_ids (Optional[Iterable[str]]): Ids of task parents.
|
|
3830
3951
|
Use 'None' if folder is direct child of project.
|
|
3952
|
+
product_types (Optional[Iterable[str]]): Product types used for
|
|
3953
|
+
filtering.
|
|
3954
|
+
statuses (Optional[Iterable[str]]): Product statuses used for
|
|
3955
|
+
filtering.
|
|
3831
3956
|
names_by_folder_ids (Optional[dict[str, Iterable[str]]]): Product
|
|
3832
3957
|
name filtering by folder id.
|
|
3833
3958
|
active (Optional[bool]): Filter active/inactive products.
|
|
@@ -3862,6 +3987,18 @@ class ServerAPI(object):
|
|
|
3862
3987
|
if not filter_folder_ids:
|
|
3863
3988
|
return
|
|
3864
3989
|
|
|
3990
|
+
filter_product_types = None
|
|
3991
|
+
if product_types is not None:
|
|
3992
|
+
filter_product_types = set(product_types)
|
|
3993
|
+
if not filter_product_types:
|
|
3994
|
+
return
|
|
3995
|
+
|
|
3996
|
+
filter_statuses = None
|
|
3997
|
+
if statuses is not None:
|
|
3998
|
+
filter_statuses = set(statuses)
|
|
3999
|
+
if not filter_statuses:
|
|
4000
|
+
return
|
|
4001
|
+
|
|
3865
4002
|
# This will disable 'folder_ids' and 'product_names' filters
|
|
3866
4003
|
# - maybe could be enhanced in future?
|
|
3867
4004
|
if names_by_folder_ids is not None:
|
|
@@ -3881,7 +4018,7 @@ class ServerAPI(object):
|
|
|
3881
4018
|
fields = set(fields) | {"id"}
|
|
3882
4019
|
if "attrib" in fields:
|
|
3883
4020
|
fields.remove("attrib")
|
|
3884
|
-
fields |= self.get_attributes_fields_for_type("
|
|
4021
|
+
fields |= self.get_attributes_fields_for_type("product")
|
|
3885
4022
|
else:
|
|
3886
4023
|
fields = self.get_default_fields_for_type("product")
|
|
3887
4024
|
|
|
@@ -3908,6 +4045,12 @@ class ServerAPI(object):
|
|
|
3908
4045
|
if filter_folder_ids:
|
|
3909
4046
|
filters["folderIds"] = list(filter_folder_ids)
|
|
3910
4047
|
|
|
4048
|
+
if filter_product_types:
|
|
4049
|
+
filters["productTypes"] = list(filter_product_types)
|
|
4050
|
+
|
|
4051
|
+
if filter_statuses:
|
|
4052
|
+
filters["statuses"] = list(filter_statuses)
|
|
4053
|
+
|
|
3911
4054
|
if product_ids:
|
|
3912
4055
|
filters["productIds"] = list(product_ids)
|
|
3913
4056
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Package declaring Python API for Ayon server."""
|
|
2
|
-
__version__ = "0.3.
|
|
2
|
+
__version__ = "0.3.6"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ayon-python-api-0.3.5 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|