accsyn-python-api 3.0.3__py3-none-any.whl → 3.1.0__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.
accsyn_api/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # :coding: utf-8
2
2
  # :copyright: Copyright (c) 2015-2023 accsyn/HDR AB
3
3
 
4
- __version__ = "2.2.1-0"
4
+ __version__ = "3.1.0-1"
accsyn_api/session.py CHANGED
@@ -14,7 +14,7 @@ import uuid
14
14
  import hashlib
15
15
  import copy
16
16
 
17
- import urllib
17
+ import urllib.parse
18
18
  import base64
19
19
  import io
20
20
  import gzip
@@ -25,13 +25,6 @@ import requests
25
25
 
26
26
  from ._version import __version__
27
27
 
28
- if sys.version_info.major >= 3:
29
- import io
30
-
31
- else:
32
- # Python 2 backward compability
33
- import binascii
34
-
35
28
  try:
36
29
  requests.packages.urllib3.disable_warnings()
37
30
  except BaseException:
@@ -44,7 +37,7 @@ logging.basicConfig(
44
37
  )
45
38
 
46
39
  ACCSYN_BACKEND_DOMAIN = "accsyn.com"
47
- ACCSYN_BACKEND_MASTER_HOSTNAME = "master.{}".format(ACCSYN_BACKEND_DOMAIN)
40
+ ACCSYN_BACKEND_MASTER_HOSTNAME = f"master.{ACCSYN_BACKEND_DOMAIN}"
48
41
  ACCSYN_PORT = 443
49
42
  DEFAULT_EVENT_PAYLOAD_COMPRESS_SIZE_TRESHOLD = 100 * 1024 # Compress event data payloads above 100k
50
43
 
@@ -52,7 +45,7 @@ CLEARANCE_SUPPORT = "support"
52
45
  CLEARANCE_ADMIN = "admin"
53
46
  CLEARANCE_EMPLOYEE = "employee"
54
47
  CLEARANCE_STANDARD = "standard"
55
- CLEARANCE_CLIENT = CLEARANCE_STANDARD # BWCOMP
48
+ CLEARANCE_CLIENT = CLEARANCE_STANDARD # BWCOMP
56
49
  CLEARANCE_NONE = "none"
57
50
 
58
51
  CLIENT_TYPE_APP = 0
@@ -68,13 +61,14 @@ CLIENT_STATE_OFFLINE = "offline"
68
61
  CLIENT_STATE_DISABLED = "disabled"
69
62
  CLIENT_STATE_DISABLED_OFFLINE = "disabled-offline"
70
63
 
64
+
71
65
  class JSONEncoder(json.JSONEncoder):
72
66
  """JSON serialiser."""
73
67
 
74
68
  def default(self, obj):
75
69
  if isinstance(obj, datetime.date) or isinstance(obj, datetime.datetime):
76
70
  return obj.strftime("%Y-%m-%dT%H:%M:%S")
77
- return super(JSONEncoder, self).default(obj)
71
+ return super().default(obj)
78
72
 
79
73
 
80
74
  class JSONDecoder(json.JSONDecoder):
@@ -145,7 +139,7 @@ class Session(object):
145
139
 
146
140
  def __init__(
147
141
  self,
148
- domain=None,
142
+ workspace=None,
149
143
  username=None,
150
144
  api_key=None,
151
145
  hostname=None,
@@ -153,26 +147,29 @@ class Session(object):
153
147
  proxy=None,
154
148
  verbose=False,
155
149
  pretty_json=False,
156
- dev=False,
157
150
  path_logfile=None,
158
151
  timeout=None,
159
152
  connect_timeout=None,
153
+ domain=None,
160
154
  ):
161
155
  """
162
156
  Initiate a new API session object. Throws exception upon authentication failure.
163
157
 
164
- :param domain: The accsyn domain (or read from ACCSYN_DOMAIN environment variable)
158
+ :param workspace: The accsyn workspace code (or read from ACCSYN_WORKSPACE environment variable)
165
159
  :param username: The accsyn username (or read from ACCSYN_API_USER environment variable)
166
160
  :param api_key: The secret API key for authentication (or read from ACCSYN_API_KEY environment variable)
167
- :param hostname: Override hostname/IP to connect to.
168
- :param port: Override default port 443.
161
+ :param hostname: Override the hostname/IP of the workspace to connect to.
162
+ :param port: Override default port (443/TCP)
169
163
  :param proxy: The proxy settings (or read from ACCSYN_PROXY environment variable).
170
- :param verbose: Create a verbose session, printing debug output to stdout.
164
+ :param verbose: Printing verbose debugging output to stdout.
171
165
  :param pretty_json: (verbose) Print pretty formatted JSON.
172
- :param dev: Dev mode.
173
166
  :param path_logfile: Output all log messages to this logfile instead of stdout.
174
167
  :param timeout: Timeout in seconds for API calls - waiting for response.
175
168
  :param connect_timeout: Timeout in seconds for API calls - waiting for connection.
169
+ :param domain: (Backward compatibility) The accsyn domain (or read from ACCSYN_DOMAIN environment variable)
170
+
171
+ .. deprecated:: 3.1.0
172
+ Use the :param workspace: parameter instead
176
173
  """
177
174
  # Generate a session ID
178
175
  self.__version__ = __version__
@@ -182,25 +179,22 @@ class Session(object):
182
179
  self._be_verbose = verbose
183
180
  self._pretty_json = pretty_json
184
181
  self._proxy = proxy
185
- self._dev = dev is True or os.environ.get('ACCSYN_DEV', 'false') in ['true', '1']
182
+ self._dev = os.environ.get('AS_DEV', 'false') in ['true', '1']
186
183
  Session._p_logfile = path_logfile
187
184
  self._role = CLEARANCE_NONE
188
- self._verbose("Creating accsyn Python API session (v{})".format(__version__))
185
+ self._verbose(f"Creating accsyn Python API session (v{__version__})")
189
186
  for key in os.environ:
190
187
  if key.startswith("FILMHUB_"):
191
- Session._warning('Found old FilmHUB product environment variable "{}", ' "please migrate!".format(key))
192
- if not domain:
193
- domain = (
194
- os.environ["ACCSYN_DOMAIN"]
195
- if "ACCSYN_DOMAIN" in os.environ
196
- else os.environ.get(
197
- "ACCSYN_ORG",
198
- )
199
- )
200
- if not domain:
201
- raise AccsynException(
202
- "Please supply your accsyn domain/workspace or set " "ACCSYN_DOMAIN environment!"
203
- )
188
+ Session._warning(f'Detected deprecated FilmHUB environment variable "{key}", please migrate!')
189
+ if not workspace:
190
+ workspace = domain
191
+ if not workspace:
192
+ for key in ["ACCSYN_WORKSPACE", "ACCSYN_DOMAIN", "ACCSYN_ORG"]:
193
+ if key in os.environ:
194
+ workspace = os.environ[key]
195
+ break
196
+ if not workspace:
197
+ raise AccsynException("Please supply your accsyn workspace domain or set ACCSYN_WORKSPACE environment!")
204
198
  if not username:
205
199
  username = os.environ.get("ACCSYN_API_USER")
206
200
  if not username:
@@ -220,24 +214,26 @@ class Session(object):
220
214
  if self._dev:
221
215
  self._hostname = "127.0.0.1"
222
216
  else:
223
- # Get domain
217
+ # Resolve workspace hostname
224
218
  response = self._rest(
225
219
  "GET",
226
220
  ACCSYN_BACKEND_MASTER_HOSTNAME,
227
221
  "J3PKTtDvolDMBtTy6AFGA",
228
- {"ident": domain},
222
+ dict(ident=workspace),
229
223
  )
230
224
  # Store hostname
231
225
  if "message" in response:
232
226
  raise AccsynException(response["message"])
233
- result = response.get('result', {})
234
- assert "hostname" in result, "No API endpoint hostname were provided for us!"
227
+ result = response.get('result', dict())
228
+ assert "hostname" in result, "No API endpoint hostname were provided for workspace {}!".format(
229
+ workspace
230
+ )
235
231
  self._hostname = result["hostname"]
236
232
  if self._port is None:
237
233
  self._port = result["port"]
238
234
  if self._port is None:
239
235
  self._port = ACCSYN_PORT if not self._dev else 8181
240
- self._domain = domain
236
+ self._workspace = workspace
241
237
  self._username = username
242
238
  self._api_key = api_key
243
239
  self._last_message = None
@@ -294,11 +290,8 @@ class Session(object):
294
290
  session_id=self._session_id,
295
291
  )
296
292
  headers = {
297
- "Authorization": "basic {}:{}".format(
298
- Session._base64_encode(self._username),
299
- Session._base64_encode(self._api_key),
300
- ),
301
- "X-Accsyn-Workspace": self._domain,
293
+ "Authorization": f"basic {Session._base64_encode(self._username)}:{Session._base64_encode(self._api_key)}",
294
+ "X-Accsyn-Workspace": self._workspace,
302
295
  }
303
296
  response = self._rest(
304
297
  "PUT",
@@ -344,17 +337,17 @@ class Session(object):
344
337
  )
345
338
  else:
346
339
  if isinstance(data, list):
347
- data = {"tasks": data}
340
+ data = dict(tasks=data)
348
341
  assert data is not None and 0 < len(data), "Empty create data submitted!"
349
342
  if entitytype == "queue":
350
343
  data["type"] = 2
351
344
  elif entitytype == "task" and "tasks" not in data:
352
- data = {"tasks": data}
345
+ data = dict(tasks=data)
353
346
  if entitytype in ["job", "task"]:
354
347
  data["allow_duplicates"] = allow_duplicates
355
348
  d = self._event(
356
349
  "POST",
357
- "%s/create" % Session._get_base_uri(entitytype),
350
+ f"{Session._get_base_uri(entitytype)}/create",
358
351
  data,
359
352
  query=entitytype_id,
360
353
  )
@@ -399,11 +392,11 @@ class Session(object):
399
392
 
400
393
  retval = None
401
394
  d = self.decode_query(query)
402
- data = {}
395
+ data = dict()
403
396
  if d["entitytype"] == "entitytypes":
404
397
  # Ask cloud server, the Python API is rarely updated and should not
405
398
  # need to know
406
- d = self._event("GET", "entitytypes", {})
399
+ d = self._event("GET", "entitytypes", dict())
407
400
  if d:
408
401
  retval = d["result"]
409
402
  elif d["entitytype"] == "attributes":
@@ -419,7 +412,7 @@ class Session(object):
419
412
  d = self._event(
420
413
  "GET",
421
414
  "attributes",
422
- {"entitytype": entitytype, "create": create, "update": update},
415
+ dict(entitytype=entitytype, create=create, update=update),
423
416
  )
424
417
  if d:
425
418
  retval = d["result"]
@@ -427,9 +420,9 @@ class Session(object):
427
420
  # Send query to server, first determine uri
428
421
  uri_base = Session._get_base_uri(d["entitytype"])
429
422
  if d["entitytype"] == "queue":
430
- data = {"type": 2}
423
+ data = dict(type=2)
431
424
  elif d["entitytype"] == "job":
432
- data = {"type": 1}
425
+ data = dict(type=1)
433
426
  if finished is not None:
434
427
  data["finished"] = finished
435
428
  if offline is not None:
@@ -442,7 +435,7 @@ class Session(object):
442
435
  data["skip"] = skip
443
436
  if attributes:
444
437
  data["attributes"] = attributes
445
- d = self._event("GET", "{}/find".format(uri_base), data, query=d.get("expression"))
438
+ d = self._event("GET", f"{uri_base}/find", data, query=d.get("expression"))
446
439
  if d:
447
440
  retval = d["result"]
448
441
  return retval
@@ -491,8 +484,8 @@ class Session(object):
491
484
  d = self.decode_query(query)
492
485
  # Send query to server, first determine uri
493
486
  uri_base = Session._get_base_uri(d["entitytype"])
494
- data = {}
495
- d = self._event("GET", "%s/report" % uri_base, data, query=d.get("expression"))
487
+ data = dict()
488
+ d = self._event("GET", f"{uri_base}/report", data, query=d.get("expression"))
496
489
  return d["report"]
497
490
 
498
491
  def metrics(self, query, attributes=["speed"], time=None):
@@ -514,7 +507,7 @@ class Session(object):
514
507
  }
515
508
  if not time is None:
516
509
  data["time"] = time
517
- d = self._event("GET", "%s/metrics" % uri_base, data, query=d.get("expression"))
510
+ d = self._event("GET", f"{uri_base}/metrics", data, query=d.get("expression"))
518
511
  return d["result"]
519
512
 
520
513
  # Update an entity
@@ -538,7 +531,7 @@ class Session(object):
538
531
  assert 0 < len(data or {}) and isinstance(data, dict), "Invalid data supplied, must be dict and have content!"
539
532
  response = self._event(
540
533
  "PUT",
541
- "%s/edit" % Session._get_base_uri(entitytype),
534
+ f"{Session._get_base_uri(entitytype)}/edit",
542
535
  data,
543
536
  entityid=entityid,
544
537
  )
@@ -555,7 +548,7 @@ class Session(object):
555
548
  :return: The updated entity data, as dictionary
556
549
 
557
550
  .. deprecated:: 2.0.2
558
- Since Python 2.0.2 you should use the :func:`update` function instead
551
+ Since version 2.0.2 you should use the :func:`update` function instead
559
552
 
560
553
  '''
561
554
  return self.update(entitytype, entityid, data)
@@ -570,7 +563,7 @@ class Session(object):
570
563
  :return: The updated sub entities (tasks), as dictionaries.
571
564
 
572
565
  .. deprecated:: 2.0.2
573
- Since Python 2.0.2 you should use the :func:`update` function instead
566
+ Since version 2.0.2 you should use the :func:`update` function instead
574
567
  """
575
568
  assert 0 < len(entitytype or "") and Session._is_str(
576
569
  entitytype
@@ -584,7 +577,7 @@ class Session(object):
584
577
  assert 0 < len(data or []) and isinstance(data, list), "Invalid data supplied, must be a list!"
585
578
  response = self._event(
586
579
  "PUT",
587
- "%s/edit" % Session._get_base_uri(entitytype),
580
+ f"{Session._get_base_uri(entitytype)}/edit",
588
581
  data,
589
582
  entityid=entityid,
590
583
  )
@@ -631,8 +624,8 @@ class Session(object):
631
624
  raise Exception("Please supply type of server assignment (main " "or site) in assignment data!")
632
625
  response = self._event(
633
626
  "PUT",
634
- "%s/edit" % Session._get_base_uri(entitytype_parent),
635
- {what: client_id},
627
+ f"{Session._get_base_uri(entitytype_parent)}/edit",
628
+ dict([(what, client_id)]),
636
629
  entityid=share_id,
637
630
  )
638
631
  if response:
@@ -655,7 +648,7 @@ class Session(object):
655
648
  if entitytype.lower() == "share":
656
649
  response = self._event(
657
650
  "GET",
658
- "%s/servers" % Session._get_base_uri(entitytype),
651
+ f"{Session._get_base_uri(entitytype)}/servers",
659
652
  {},
660
653
  entityid=entityid,
661
654
  )
@@ -701,8 +694,8 @@ class Session(object):
701
694
  raise Exception("Please supply type of server assignment (main " "or site) in assignment data!")
702
695
  response = self._event(
703
696
  "PUT",
704
- "%s/edit" % Session._get_base_uri(entitytype_parent),
705
- {"{}_clear".format(what): client_id},
697
+ f"{Session._get_base_uri(entitytype_parent)}/edit",
698
+ dict([(f"{what}_clear", client_id)]),
706
699
  entityid=share_id,
707
700
  )
708
701
  if response:
@@ -731,7 +724,7 @@ class Session(object):
731
724
  ), "Invalid entity ID supplied, must be of string type!"
732
725
  response = self._event(
733
726
  "DELETE",
734
- "%s/offline" % Session._get_base_uri(entitytype),
727
+ f"{Session._get_base_uri(entitytype)}/offline",
735
728
  {},
736
729
  entityid=entityid,
737
730
  )
@@ -755,7 +748,7 @@ class Session(object):
755
748
  ), "Invalid entity ID supplied, must be of string type!"
756
749
  response = self._event(
757
750
  "DELETE",
758
- "%s/delete" % Session._get_base_uri(entitytype),
751
+ f"{Session._get_base_uri(entitytype)}/delete",
759
752
  {},
760
753
  entityid=entityid,
761
754
  )
@@ -977,7 +970,7 @@ class Session(object):
977
970
  result += d["size"]
978
971
  return result
979
972
 
980
- event_data = {"files": data, "size": recursive_get_size(data)}
973
+ event_data = dict(files=data, size=recursive_get_size(data))
981
974
  response = self._event("PUT", "workspace/publish/preprocess", event_data)
982
975
  return response["result"]
983
976
 
@@ -985,7 +978,7 @@ class Session(object):
985
978
 
986
979
  def get_setting(self, name=None, scope='workspace', entity_id=None, integration=None, data=None):
987
980
  '''Retrive *name* setting for the given *scope* (workspace, job, share..), for optional *entity_id* or *integration* (ftrack,..)'''
988
- evt_data = {'scope': scope, 'name': name}
981
+ evt_data = dict(scope=scope, name=name)
989
982
  if entity_id:
990
983
  evt_data['ident'] = entity_id
991
984
  if integration:
@@ -997,7 +990,7 @@ class Session(object):
997
990
 
998
991
  def set_setting(self, name, value, scope='workspace', entity_id=None, integration=None, data=None):
999
992
  '''Set the setting identified by *name* to *value* for *entity_id* within *scope*.'''
1000
- evt_data = {'scope': scope, 'name': name, 'value': value}
993
+ evt_data = dict(scope=scope, name=name, value=value)
1001
994
  if entity_id:
1002
995
  evt_data['ident'] = entity_id
1003
996
  if integration:
@@ -1010,10 +1003,10 @@ class Session(object):
1010
1003
  # Misc
1011
1004
  def get_api_key(self):
1012
1005
  """Fetch API key, by default disabled in backend."""
1013
- return self._event("GET", "user/api_key", {})["api_key"]
1006
+ return self._event("GET", "user/api_key", dict())["api_key"]
1014
1007
 
1015
1008
  def gui_is_running(self):
1016
- """ Backward compability """
1009
+ """Backward compability"""
1017
1010
  return self.app_is_running()
1018
1011
 
1019
1012
  def app_is_running(self):
@@ -1028,7 +1021,7 @@ class Session(object):
1028
1021
  "GET",
1029
1022
  "client/find",
1030
1023
  {},
1031
- query="user={0} AND code={1} AND type={2}".format(self._uid, Session.get_hostname(), CLIENT_TYPE_APP),
1024
+ query=f"user={self._uid} AND code={Session.get_hostname()} AND type={CLIENT_TYPE_APP}",
1032
1025
  )["result"]
1033
1026
  retval = None
1034
1027
  if 0 < len(result):
@@ -1039,7 +1032,7 @@ class Session(object):
1039
1032
  return retval
1040
1033
 
1041
1034
  def server_is_running(self):
1042
- """ Backward compatibility """
1035
+ """Backward compatibility"""
1043
1036
  return self.daemon_is_running()
1044
1037
 
1045
1038
  def daemon_is_running(self):
@@ -1054,8 +1047,7 @@ class Session(object):
1054
1047
  "GET",
1055
1048
  "client/find",
1056
1049
  {},
1057
- query="user={0} AND code={1} AND (type={2} OR type={3})".format(self._uid, Session.get_hostname(),
1058
- CLIENT_TYPE_SERVER, CLIENT_TYPE_USERSERVER),
1050
+ query=f"user={self._uid} AND code={Session.get_hostname()} AND (type={CLIENT_TYPE_SERVER} OR type={CLIENT_TYPE_USERSERVER})",
1059
1051
  )["result"]
1060
1052
  retval = None
1061
1053
  if 0 < len(result):
@@ -1071,7 +1063,7 @@ class Session(object):
1071
1063
  assert len(operation) > 0, 'No operation provided'
1072
1064
  return self._event(
1073
1065
  "PUT",
1074
- "workspace/integration/{}/utility".format(name),
1066
+ f"workspace/integration/{name}/utility",
1075
1067
  {
1076
1068
  'operation': operation,
1077
1069
  'data': data,
@@ -1134,7 +1126,7 @@ class Session(object):
1134
1126
  if port is None:
1135
1127
  port = self._port or ACCSYN_PORT
1136
1128
  if hostname is None:
1137
- hostname = "{}.{}".format(self._domain, ACCSYN_BACKEND_DOMAIN)
1129
+ hostname = f"{self._workspace}.{ACCSYN_BACKEND_DOMAIN}"
1138
1130
  # Proxy set?
1139
1131
  proxy_type = None
1140
1132
  proxy_hostname = None
@@ -1162,12 +1154,12 @@ class Session(object):
1162
1154
  if proxy_type == "accsyn":
1163
1155
  if proxy_port == -1:
1164
1156
  proxy_port = 80
1165
- self._verbose("Using accsyn proxy @ {}:{}".format(proxy_hostname, proxy_port))
1157
+ self._verbose(f"Using accsyn proxy @ {proxy_hostname}:{proxy_port}")
1166
1158
  hostname = proxy_hostname
1167
1159
  port = proxy_port
1168
1160
  elif proxy_type in ["socks", "socks5"]:
1169
1161
  try:
1170
- self._verbose("Using SOCKS5 proxy @ {}:{}".format(proxy_hostname, proxy_port))
1162
+ self._verbose(f"Using SOCKS5 proxy @ {proxy_hostname}:{proxy_port}")
1171
1163
  import socks
1172
1164
 
1173
1165
  socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, proxy_hostname, proxy_port)
@@ -1176,17 +1168,12 @@ class Session(object):
1176
1168
  Session._warning('Python lacks SOCKS support, please install "pysocks" and' " try again...")
1177
1169
  raise ie
1178
1170
  elif proxy_type is not None:
1179
- raise AccsynException('Unknown proxy type "{}"!'.format(proxy_type))
1180
- url = "http{}://{}:{}/api/v3{}".format(
1181
- "s" if ssl else "",
1182
- hostname,
1183
- port,
1184
- ("/" if not uri.startswith("/") else "") + uri,
1185
- )
1171
+ raise AccsynException(f'Unknown proxy type "{proxy_type}"!')
1172
+ url = f"http{'s' if ssl else ''}://{hostname}:{port}/api/v3{('/' if not uri.startswith('/') else '') + uri}"
1186
1173
  if timeout is None:
1187
1174
  timeout = self.timeout
1188
1175
  if data is None:
1189
- data = {}
1176
+ data = dict()
1190
1177
  # Wait 10s to reach machine, 2min for it to send back data
1191
1178
  CONNECT_TO, READ_TO = (self.connect_timeout, timeout)
1192
1179
  r = None
@@ -1197,31 +1184,15 @@ class Session(object):
1197
1184
  headers_effective = copy.deepcopy(headers)
1198
1185
  elif self._api_key:
1199
1186
  headers_effective = {
1200
- "Authorization": "basic {}:{}".format(
1201
- Session._base64_encode(self._username),
1202
- Session._base64_encode(self._api_key)
1203
- ),
1204
- "X-Accsyn-Workspace": self._domain,
1187
+ "Authorization": f"basic {Session._base64_encode(self._username)}:{Session._base64_encode(self._api_key)}",
1188
+ "X-Accsyn-Workspace": self._workspace,
1205
1189
  }
1206
- headers_effective["X-Accsyn-Device"] = "PythonAPI v%s @ %s %s(%s)" % (
1207
- __version__,
1208
- sys.platform,
1209
- Session.get_hostname(),
1210
- os.name,
1190
+ headers_effective["X-Accsyn-Device"] = (
1191
+ f"PythonAPI v{__version__} @ {sys.platform} {Session.get_hostname()}({os.name})"
1211
1192
  )
1212
- if 3 <= sys.version_info.major:
1213
- t_start = int(round(time.time() * 1000))
1214
- else:
1215
- t_start = long(round(time.time() * 1000))
1193
+ t_start = int(round(time.time() * 1000))
1216
1194
  try:
1217
- self._verbose(
1218
- "REST %s %s, data: %s"
1219
- % (
1220
- method,
1221
- url,
1222
- data if not self._pretty_json else Session.str(data),
1223
- )
1224
- )
1195
+ self._verbose(f"REST {method} {url}, data: {data if not self._pretty_json else Session.str(data)}")
1225
1196
  if method.lower() == "get":
1226
1197
  r = requests.get(
1227
1198
  url,
@@ -1259,48 +1230,28 @@ class Session(object):
1259
1230
  except BaseException:
1260
1231
  # if timeout <= 0:
1261
1232
  raise AccsynException(
1262
- "Could not reach {}:{}! Make sure backend({}) can"
1263
- " be reached from you location and no firewall is "
1264
- "blocking outgoing TCP traffic at port {}. "
1265
- "Details: {}".format(
1266
- hostname,
1267
- port,
1268
- hostname,
1269
- port,
1270
- traceback.format_exc() if not quiet else "(quiet)",
1271
- )
1233
+ f"Could not reach {hostname}:{port}! Make sure backend({hostname}) can be reached from you location and no firewall is blocking outgoing TCP traffic at port {port}. Details: {traceback.format_exc() if not quiet else '(quiet)'}"
1272
1234
  )
1273
1235
  try:
1274
1236
  retval = json.loads(r.text, cls=JSONDecoder)
1275
1237
  if not quiet:
1276
- self._verbose(
1277
- "{}/{} REST {} result: {} (~{}ms)".format(
1278
- hostname,
1279
- uri,
1280
- method,
1281
- Session._obscure_dict_string(
1282
- Session._safely_printable(
1283
- str(retval) if not self._pretty_json else Session.str(retval)
1284
- ).replace("'", '"')
1285
- ),
1286
- t_start - t_end + 1,
1238
+ str_result = Session._obscure_dict_string(
1239
+ Session._safely_printable(str(retval) if not self._pretty_json else Session.str(retval)).replace(
1240
+ "'", '"'
1287
1241
  )
1288
1242
  )
1243
+ self._verbose(f"{hostname}/{uri} REST {method} result: {str_result} (~{t_start - t_end + 1}ms)")
1289
1244
  except BaseException:
1290
1245
  sys.stderr.write(traceback.format_exc())
1291
- message = 'The {} REST {} {} operation failed! Details: {} {}'.format(
1292
- url,
1293
- method,
1294
- Session._obscure_dict_string(Session._safely_printable(str(data)).replace("'", '"')),
1295
- r.text,
1296
- traceback.format_exc(),
1246
+ str_data = Session._obscure_dict_string(Session._safely_printable(str(data)).replace("'", '"'))
1247
+ message = (
1248
+ f'The {url} REST {method} {str_data} operation failed! Details: {r.text} {traceback.format_exc()}'
1297
1249
  )
1298
1250
  Session._warning(message)
1299
1251
  raise AccsynException(message)
1300
1252
 
1301
1253
  if "exception" in retval:
1302
- message = "{} caused an exception! Please contact {} admin for more"
1303
- " further support.".format(uri, self._domain)
1254
+ message = f"{uri} caused an exception! Please contact {self._workspace} admin for more further support."
1304
1255
  Session._warning(message)
1305
1256
  if self._role in [CLEARANCE_ADMIN, CLEARANCE_SUPPORT]:
1306
1257
  Session._warning(retval["exception"])
@@ -1328,7 +1279,7 @@ class Session(object):
1328
1279
  assert self._uid, "Login before posting event!"
1329
1280
  event = {
1330
1281
  "audience": "api",
1331
- "domain": self._domain,
1282
+ "workspace": self._workspace,
1332
1283
  "eid": str(uuid.uuid4()),
1333
1284
  "session": self._session_id,
1334
1285
  "uri": uri,
@@ -1364,12 +1315,7 @@ class Session(object):
1364
1315
  b = out.getvalue()
1365
1316
  event["gz_data"] = base64.b64encode(b).decode('utf-8')
1366
1317
  self._verbose(
1367
- "Compressed event payload %d>%d(%s%%)"
1368
- % (
1369
- size,
1370
- len(event["gz_data"]),
1371
- (100 * len(event["gz_data"]) / size),
1372
- )
1318
+ f"Compressed event payload {size}>{len(event['gz_data'])}({100 * len(event['gz_data']) / size}%)"
1373
1319
  )
1374
1320
  did_compress_payload = True
1375
1321
  if not did_compress_payload:
@@ -1433,18 +1379,18 @@ class Session(object):
1433
1379
  s += query[idx]
1434
1380
  if idx_part_start < len(query):
1435
1381
  parts.append(query[idx_part_start:])
1436
- self._verbose('Query: "{}", parts: "{}"'.format(query, parts))
1382
+ self._verbose(f'Query: "{query}", parts: "{parts}"')
1437
1383
  assert len(parts) == 1 or 3 <= len(parts), (
1438
1384
  "Query has invalid syntax; statements can either be "
1439
1385
  'single ("<entity>"") or with a WHERE statement '
1440
1386
  '("<(sub)entity> WHERE {<entity>.}id=..{ AND ..}"")'
1441
1387
  )
1442
1388
  if len(parts) == 1:
1443
- return {"entitytype": parts[0].lower()}
1389
+ return dict(entitytype=parts[0].lower())
1444
1390
  else:
1445
1391
  assert (
1446
1392
  parts[1].strip().lower() == "where"
1447
- ), 'Invalid query "{}", should be on the form ' '"<entitytype> where <expression>".'.format(query)
1393
+ ), f'Invalid query "{query}", should be on the form "<entitytype> where <expression>".'
1448
1394
  # Decode expression
1449
1395
  return {
1450
1396
  "entitytype": parts[0].lower(),
@@ -1488,14 +1434,11 @@ class Session(object):
1488
1434
 
1489
1435
  def _verbose(self, s):
1490
1436
  if self._be_verbose:
1491
- Session._info("[ACCSYN_API] %s" % (s))
1437
+ Session._info(f"[ACCSYN_API] {s}")
1492
1438
 
1493
1439
  @staticmethod
1494
1440
  def _safe_dumps(d, indent=None):
1495
- if 3 <= sys.version_info.major:
1496
- return json.dumps(d if not isinstance(d, list) else list(d.values()), cls=JSONEncoder, indent=indent)
1497
- else:
1498
- return json.dumps(d, cls=JSONEncoder, indent=indent)
1441
+ return json.dumps(d if not isinstance(d, list) else list(d.values()), cls=JSONEncoder, indent=indent)
1499
1442
 
1500
1443
  @staticmethod
1501
1444
  def _safely_printable(s):
@@ -1503,37 +1446,25 @@ class Session(object):
1503
1446
 
1504
1447
  @staticmethod
1505
1448
  def _is_str(s):
1506
- if 3 <= sys.version_info.major:
1507
- return isinstance(s, str)
1508
- else:
1509
- return isinstance(s, str) or isinstance(s, unicode)
1449
+ return isinstance(s, str)
1510
1450
 
1511
1451
  @staticmethod
1512
1452
  def _url_quote(url):
1513
- if 3 <= sys.version_info.major:
1514
- return urllib.parse.quote(Session._safe_dumps(url))
1515
- else:
1516
- return urllib.quote(Session._safe_dumps(url))
1453
+ return urllib.parse.quote(Session._safe_dumps(url))
1517
1454
 
1518
1455
  @staticmethod
1519
1456
  def _json_serial(obj):
1520
1457
  """JSON serializer for *obj not serializable by default json code."""
1521
1458
  if isinstance(obj, datetime.datetime) or isinstance(obj, datetime.date):
1522
1459
  return obj.isoformat()
1523
- raise TypeError("Type %s not serializable" % type(obj))
1460
+ raise TypeError(f"Type {type(obj)} not serializable")
1524
1461
 
1525
1462
  @staticmethod
1526
1463
  def _base64_encode(s):
1527
1464
  """Produce a BASE64 encoded string."""
1528
- if 3 <= sys.version_info.major:
1529
- return (base64.b64encode(s.encode("utf-8"))).decode("ascii")
1530
- else:
1531
- if isinstance(s, str) or isinstance(s, unicode):
1532
- return base64.b64encode(s)
1533
- else:
1534
- return binascii.b2a_base64(s)
1465
+ return (base64.b64encode(s.encode("utf-8"))).decode("ascii")
1535
1466
 
1536
1467
 
1537
1468
  class AccsynException(Exception):
1538
1469
  def __init__(self, message):
1539
- super(AccsynException, self).__init__(message)
1470
+ super().__init__(message)
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.1
2
+ Name: accsyn-python-api
3
+ Version: 3.1.0
4
+ Summary: A Python API for accsyn programmable fast and secure data delivery software
5
+ Home-page: https://accsyn.com
6
+ License: Apache-2.0
7
+ Keywords: accsyn,api,file-transfer,delivery
8
+ Author: Henrik Norin
9
+ Author-email: support@accsyn.com
10
+ Requires-Python: >=3.8,<4.0
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Internet :: File Transfer Protocol (FTP)
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Dist: requests (>=2.25.0,<3.0.0)
25
+ Project-URL: Bug Tracker, https://github.com/accsyn/accsyn-python-api/issues
26
+ Project-URL: Documentation, https://accsyn-python-api.readthedocs.io
27
+ Project-URL: Repository, https://github.com/accsyn/accsyn-python-api.git
28
+ Project-URL: Support, https://support.accsyn.com
29
+ Description-Content-Type: text/markdown
30
+
31
+ # accsyn-python-api
32
+ Official accsyn fast and secure file delivery Python API
33
+
34
+ Python API support can be found [here](https://support.accsyn.com/workflows/python-api).
35
+
36
+
37
+ Changelog:
38
+ ----------
39
+
40
+ See doc/release_notes.rst
41
+
42
+
43
+ Documentation:
44
+ --------------
45
+
46
+ [https://accsyn-python-api.readthedocs.io/en/latest](https://accsyn-python-api.readthedocs.io/en/latest)
47
+
48
+
49
+ Development Setup:
50
+ ------------------
51
+
52
+ This project uses Poetry for dependency management. To get started:
53
+
54
+ ```bash
55
+ # Install Poetry (if not already installed)
56
+ curl -sSL https://install.python-poetry.org | python3 -
57
+
58
+ # Install dependencies
59
+ poetry install
60
+
61
+ # Install with documentation dependencies
62
+ poetry install --with docs
63
+
64
+ # Activate the virtual environment
65
+ poetry shell
66
+ ```
67
+
68
+ Building Documentation:
69
+ ----------------------
70
+
71
+ To build the documentation locally:
72
+
73
+ ```bash
74
+ # Install with docs dependencies
75
+ poetry install --with docs
76
+
77
+ # Build docs
78
+ cd doc
79
+ poetry run sphinx-build -T -E -b html -d _build/doctrees -D language=en . ../dist/doc
80
+ ```
81
+
82
+ Or use the shorter command:
83
+ ```bash
84
+ poetry run sphinx-build -b html doc dist/doc
85
+ ```
86
+
87
+ Development Tools:
88
+ -----------------
89
+
90
+ ```bash
91
+ # Format code
92
+ poetry run black .
93
+
94
+ ```
95
+
96
+ Building and Publishing:
97
+ -----------------------
98
+
99
+ ```bash
100
+ # Build the package
101
+ poetry build
102
+
103
+ # Publish to PyPI (requires authentication)
104
+ poetry publish
105
+
106
+ # Or publish to test PyPI first
107
+ poetry config repositories.testpypi https://test.pypi.org/legacy/
108
+ poetry publish -r testpypi
109
+ ```
110
+
111
+ accsyn(r) - secure high speed file delivery and workflow sync
112
+ https://accsyn.com
113
+ https://support.accsyn.com
114
+
115
+
@@ -0,0 +1,6 @@
1
+ accsyn_api/__init__.py,sha256=vSGlQJ7mqd5eE2vElmJ_hnvRy61FkMqFpUXyhhVapTY,121
2
+ accsyn_api/_version.py,sha256=9g_6UZ5D9rC6Se-GIbtxRPMgxhZsos3JAQ2D7E-3Km0,94
3
+ accsyn_api/session.py,sha256=PypVpJbVI5gOf90jGcLNwy82jWD5kZGkNJRiuGezIj8,56270
4
+ accsyn_python_api-3.1.0.dist-info/METADATA,sha256=NNAJc33_JgOUjYtgTSzvRzJKIimh7lbX4EUpBYm40yU,2936
5
+ accsyn_python_api-3.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
6
+ accsyn_python_api-3.1.0.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.0)
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -1,57 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: accsyn-python-api
3
- Version: 3.0.3
4
- Summary: A Python API for accsyn programmable fast and secure data delivery software
5
- Home-page: https://github.com/accsyn/accsyn-python-api.git
6
- Author: Henrik Norin
7
- Author-email: henrik.norin@accsyn.com
8
- License: Apache License (2.0)
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=2.7.9, <4.0
13
- Description-Content-Type: text/markdown
14
- Requires-Dist: requests
15
-
16
- # accsyn-python-api
17
- Official accsyn fast film delivery Python API
18
-
19
- Complete Python API reference can be found [here](https://support.accsyn.com/python-api).
20
-
21
-
22
- Changelog:
23
- ----------
24
-
25
- See doc/release_notes.rst
26
-
27
-
28
- Documentation:
29
- --------------
30
-
31
- [https://accsyn-python-api.readthedocs.io/en/latest](https://accsyn-python-api.readthedocs.io/en/latest)
32
-
33
-
34
- Building:
35
- ---------
36
-
37
- To build the documentation locally, run:
38
-
39
- ```
40
- cd doc
41
- pip install -r requirements.txt
42
- python -m sphinx -T -E -b html -d _build/doctrees -D language=en . ../dist/doc
43
- ```
44
-
45
- Deploying:
46
- ----------
47
-
48
- ```
49
- python setup.py sdist bdist_wheel
50
- twine upload --verbose --username accsyn dist/*
51
- ```
52
-
53
- Henrik Norin, HDR AB, 2023
54
- accsyn(r) - secure data delivery and workflow sync
55
- https://accsyn.com
56
- https://support.accsyn.com
57
-
@@ -1,7 +0,0 @@
1
- accsyn_api/__init__.py,sha256=vSGlQJ7mqd5eE2vElmJ_hnvRy61FkMqFpUXyhhVapTY,121
2
- accsyn_api/_version.py,sha256=SkA5Pq2uOISkNluIl0WMV5KQjV-UfWwM2jXzz9g37Sc,94
3
- accsyn_api/session.py,sha256=H4mLJOKIju54A-vFcRnzZgeBcVLYw3Sub70speUSJvI,57977
4
- accsyn_python_api-3.0.3.dist-info/METADATA,sha256=O1K0WMODXSnWfjNmYmTcqZePuJ9TzZGVzFyF6pSg928,1326
5
- accsyn_python_api-3.0.3.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
6
- accsyn_python_api-3.0.3.dist-info/top_level.txt,sha256=L5p6Syb_jH-p3-TSIARG35mGD7OALyDLMGziEzpDyFs,11
7
- accsyn_python_api-3.0.3.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- accsyn_api