accsyn-python-api 3.2.0__tar.gz → 3.2.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: accsyn-python-api
3
- Version: 3.2.0
3
+ Version: 3.2.1
4
4
  Summary: A Python API for accsyn programmable fast and secure data delivery software
5
5
  Home-page: https://accsyn.com
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "accsyn-python-api"
7
- version = "3.2.0"
7
+ version = "3.2.1"
8
8
  description = "A Python API for accsyn programmable fast and secure data delivery software"
9
9
  authors = ["Henrik Norin <support@accsyn.com>"]
10
10
  license = "Apache-2.0"
@@ -28,4 +28,3 @@ def run_pytest(argv: Optional[Sequence[str]] = None) -> int:
28
28
  def test() -> None:
29
29
  """Poetry script entrypoint: `poetry run test [pytest-args...]`."""
30
30
  raise SystemExit(run_pytest())
31
-
@@ -1,4 +1,4 @@
1
1
  # :coding: utf-8
2
2
  # :copyright: Copyright (c) 2015-2023 accsyn/HDR AB
3
3
 
4
- __version__ = "3.2.0-1"
4
+ __version__ = "3.2.1-0"
@@ -63,14 +63,29 @@ CLIENT_STATE_OFFLINE = "offline"
63
63
  CLIENT_STATE_DISABLED = "disabled"
64
64
  CLIENT_STATE_DISABLED_OFFLINE = "disabled-offline"
65
65
 
66
- JOB_TYPE_TRANSFER = 1 # p2p transfer job, either standalone or beneath a delivery/request
66
+ JOB_TYPE_TRANSFER = 1 # p2p transfer job, either standalone or beneath a delivery/request
67
67
  JOB_TYPE_QUEUE = 2 # A job contai
68
- JOB_TYPE_COMPUTE = 3 # A compute/render job
69
- JOB_TYPE_DELIVERY = 7 # An outgoing delivery job, hold one or more upload jobs for managers and one download job per recipient
70
- JOB_TYPE_REQUEST = 8 # An inbound upload request, holds one upload job per recipient and then one or more download job for managers
71
- JOB_TYPE_STREAM = 10 # An accsyn streaming delivery, same as delivery but containing one or more streamable media
72
-
73
- UNIQUE_ENTITY_TYPES = ["user","workspace","share","volume","folder","home","collection","site","engine","queue"]
68
+ JOB_TYPE_COMPUTE = 3 # A compute/render job
69
+ JOB_TYPE_DELIVERY = (
70
+ 7 # An outgoing delivery job, hold one or more upload jobs for managers and one download job per recipient
71
+ )
72
+ JOB_TYPE_REQUEST = (
73
+ 8 # An inbound upload request, holds one upload job per recipient and then one or more download job for managers
74
+ )
75
+ JOB_TYPE_STREAM = 10 # An accsyn streaming delivery, same as delivery but containing one or more streamable media
76
+
77
+ UNIQUE_ENTITY_TYPES = [
78
+ "user",
79
+ "workspace",
80
+ "share",
81
+ "volume",
82
+ "folder",
83
+ "home",
84
+ "collection",
85
+ "site",
86
+ "engine",
87
+ "queue",
88
+ ]
74
89
  # Entity types were code is unique and can be used to find the entity by code
75
90
 
76
91
 
@@ -140,13 +155,13 @@ class JSONDecoder(json.JSONDecoder):
140
155
  def _load_env_file(path: str, override: bool = False) -> None:
141
156
  """
142
157
  Load environment variables from a .env file.
143
-
158
+
144
159
  :param path: Path to the .env file
145
160
  :param override: If True, override existing environment variables. If False, only set if not already set.
146
161
  """
147
162
  if not os.path.exists(path):
148
163
  return
149
-
164
+
150
165
  try:
151
166
  with open(path, "r", encoding="utf-8") as f:
152
167
  for line in f:
@@ -154,24 +169,24 @@ def _load_env_file(path: str, override: bool = False) -> None:
154
169
  line = line.strip()
155
170
  if not line:
156
171
  continue
157
-
172
+
158
173
  # Skip comments (lines starting with #)
159
174
  if line.startswith("#"):
160
175
  continue
161
-
176
+
162
177
  # Parse KEY=VALUE
163
178
  if "=" in line:
164
179
  key, value = line.split("=", 1)
165
180
  key = key.strip()
166
181
  value = value.strip()
167
-
182
+
168
183
  # Remove quotes if present
169
184
  if len(value) >= 2:
170
185
  if (value.startswith('"') and value.endswith('"')) or (
171
186
  value.startswith("'") and value.endswith("'")
172
187
  ):
173
188
  value = value[1:-1]
174
-
189
+
175
190
  # Only set if not already set (unless override=True)
176
191
  if override or key not in os.environ:
177
192
  os.environ[key] = value
@@ -408,12 +423,20 @@ class Session(object):
408
423
  if isinstance(data, list):
409
424
  data = dict(tasks=data)
410
425
  assert data is not None and 0 < len(data), "Empty create data submitted!"
411
- uri="create"
426
+ uri = "create"
412
427
  if entitytype == "task" and "tasks" not in data:
413
428
  data = dict(tasks=data)
414
429
  if entitytype == "file":
415
- uri="add"
416
- if allow_duplicates is not None and entitytype in ["transfer", "compute", "delivery", "request", "stream", "job", "task"]:
430
+ uri = "add"
431
+ if allow_duplicates is not None and entitytype in [
432
+ "transfer",
433
+ "compute",
434
+ "delivery",
435
+ "request",
436
+ "stream",
437
+ "job",
438
+ "task",
439
+ ]:
417
440
  data["allow_duplicates"] = allow_duplicates
418
441
  d = self._event(
419
442
  "POST",
@@ -439,7 +462,7 @@ class Session(object):
439
462
  attributes: Optional[List[str]] = None,
440
463
  finished: Optional[bool] = None,
441
464
  inactive: Optional[bool] = None,
442
- offline: Optional[bool] = None, # Deprecated, use inactive instead
465
+ offline: Optional[bool] = None, # Deprecated, use inactive instead
443
466
  archived: Optional[bool] = None,
444
467
  limit: Optional[int] = None,
445
468
  skip: Optional[int] = None,
@@ -569,7 +592,7 @@ class Session(object):
569
592
  )
570
593
  if result and 0 < len(result):
571
594
  retval = result[0]
572
- if 1<len(result):
595
+ if 1 < len(result):
573
596
  Session._warning(f"Multiple entities retreived({len(result)}), returning first one.")
574
597
  return retval
575
598
  return None
@@ -596,7 +619,7 @@ class Session(object):
596
619
  if not result:
597
620
  return None
598
621
  retval = result[0]
599
- if 1<len(result):
622
+ if 1 < len(result):
600
623
  Session._warning(f"Multiple entities retreived({len(result)}), returning first one.")
601
624
  return retval
602
625
  return None
@@ -637,12 +660,7 @@ class Session(object):
637
660
 
638
661
  # Update an entity
639
662
 
640
- def update(
641
- self,
642
- entitytype: str,
643
- entityid: str,
644
- data: Dict[str, Any]
645
- ) -> Optional[Dict[str, Any]]:
663
+ def update(self, entitytype: str, entityid: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
646
664
  """
647
665
  Update/modify an entity.
648
666
 
@@ -660,7 +678,9 @@ class Session(object):
660
678
  assert 0 < len(entityid or "") and Session._is_str(
661
679
  entityid
662
680
  ), "Invalid entity ID supplied, must be of string type!"
663
- assert 0 < len(data or dict()) and isinstance(data, dict), "Invalid data supplied, must be dict and have content!"
681
+ assert 0 < len(data or dict()) and isinstance(
682
+ data, dict
683
+ ), "Invalid data supplied, must be dict and have content!"
664
684
  response = self._event(
665
685
  "PUT",
666
686
  f"{entitytype}/edit",
@@ -670,12 +690,7 @@ class Session(object):
670
690
  if response:
671
691
  return response["result"][0]
672
692
 
673
- def update_one(
674
- self,
675
- entitytype: str,
676
- entityid: str,
677
- data: Dict[str, Any]
678
- ) -> Optional[Dict[str, Any]]:
693
+ def update_one(self, entitytype: str, entityid: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
679
694
  '''
680
695
  Update/modify an entity.
681
696
 
@@ -692,10 +707,10 @@ class Session(object):
692
707
  return self.update(entitytype, entityid, data)
693
708
 
694
709
  def update_many(
695
- self,
696
- entitytype: str,
710
+ self,
711
+ entitytype: str,
697
712
  entityid: str,
698
- data: List[Dict[str, Any]],
713
+ data: List[Dict[str, Any]],
699
714
  ) -> Optional[List[Dict[str, Any]]]:
700
715
  """
701
716
  Update/modify multiple entities - tasks beneath a job.
@@ -764,9 +779,13 @@ class Session(object):
764
779
  if entitytype_parent in ["volume", "share"] and entitytype in ["server", "client"]:
765
780
  # Assign a server to a share, expect share and client supplied
766
781
  share_id = data.get("volume", data.get("share"))
767
- assert re.match("^[a-z0-9]{24}$", (share_id or "")), "Please supply parent entity ID as 'volume' with assignment data!"
782
+ assert re.match(
783
+ "^[a-z0-9]{24}$", (share_id or "")
784
+ ), "Please supply parent entity ID as 'volume' with assignment data!"
768
785
  client_id = data.get("server", data.get("client"))
769
- assert re.match("^[a-z0-9]{24}$", (client_id or "")), "Please supply entity ID as 'server' with assignment data!"
786
+ assert re.match(
787
+ "^[a-z0-9]{24}$", (client_id or "")
788
+ ), "Please supply entity ID as 'server' with assignment data!"
770
789
  response = self._event(
771
790
  "PUT",
772
791
  f"share/server",
@@ -778,34 +797,38 @@ class Session(object):
778
797
  elif entitytype_parent in ["delivery"] and entitytype in ["user"]:
779
798
  # Assign a user to a delivery, expect delivery and user supplied
780
799
  delivery_id = data.get("delivery")
781
- assert re.match("^[a-z0-9]{24}$", (delivery_id or "")), "Please supply parent entity ID as 'delivery' with assignment data!"
800
+ assert re.match(
801
+ "^[a-z0-9]{24}$", (delivery_id or "")
802
+ ), "Please supply parent entity ID as 'delivery' with assignment data!"
782
803
  user_id = data.get("user")
783
- assert re.match("^[a-z0-9]{24}$", (user_id or "")), "Please supply entity ID as 'user' with assignment data!"
804
+ assert re.match(
805
+ "^[a-z0-9]{24}$", (user_id or "")
806
+ ), "Please supply entity ID as 'user' with assignment data!"
784
807
  response = self._event(
785
808
  "PUT",
786
809
  f"job/recipient/add",
787
810
  dict(recipient=user_id),
788
811
  entityid=delivery_id,
789
812
  )
790
- elif entitytype_parent in ["volume","folder","home","collection"] and entitytype in ["user"]:
813
+ elif entitytype_parent in ["volume", "folder", "home", "collection"] and entitytype in ["user"]:
791
814
  # Assign an employee user to a volume
792
815
  volume_id = data.get("volume")
793
- assert re.match("^[a-z0-9]{24}$", (volume_id or "")), "Please supply parent entity ID as 'volume' with assignment data!"
816
+ assert re.match(
817
+ "^[a-z0-9]{24}$", (volume_id or "")
818
+ ), "Please supply parent entity ID as 'volume' with assignment data!"
794
819
  user_id = data.get("user")
795
- assert re.match("^[a-z0-9]{24}$", (user_id or "")), "Please supply entity ID as 'user' with assignment data!"
820
+ assert re.match(
821
+ "^[a-z0-9]{24}$", (user_id or "")
822
+ ), "Please supply entity ID as 'user' with assignment data!"
796
823
  payload = dict(
797
824
  entity=f"user:{user_id}",
798
825
  target=f"share:{volume_id}",
799
826
  read=data.get("read", True),
800
827
  write=data.get("write", True),
801
828
  notify=data.get("notify", True),
802
- message=data.get("message", "")
803
- )
804
- response = self._event(
805
- "POST",
806
- f"acl/create",
807
- payload
829
+ message=data.get("message", ""),
808
830
  )
831
+ response = self._event("POST", f"acl/create", payload)
809
832
  if response is not None:
810
833
  return response["result"]
811
834
  else:
@@ -825,7 +848,7 @@ class Session(object):
825
848
  assert 0 < len(entitytype or "") and Session._is_str(
826
849
  entitytype
827
850
  ), "Invalid parent entity type supplied, must be of string type!"
828
- if entitytype.lower() in ["volume", "share"]: # share is deprecated since 3.2
851
+ if entitytype.lower() in ["volume", "share"]: # share is deprecated since 3.2
829
852
  # List servers assigned to a volume
830
853
  response = self._event(
831
854
  "GET",
@@ -868,9 +891,13 @@ class Session(object):
868
891
  if entitytype_parent in ["volume", "share"] and entitytype == "server":
869
892
  # Assign a server to a share, expect share and client supplied
870
893
  share_id = data.get("volume", data.get("share"))
871
- assert re.match("^[a-z0-9]{24}$", (share_id or "")), "Please supply parent entity ID as 'volume' with de-assignment data!"
894
+ assert re.match(
895
+ "^[a-z0-9]{24}$", (share_id or "")
896
+ ), "Please supply parent entity ID as 'volume' with de-assignment data!"
872
897
  client_id = data.get("server", data.get("client"))
873
- assert re.match("^[a-z0-9]{24}$", (client_id or "")), "Please supply entity ID as 'server' with de-assignment data!"
898
+ assert re.match(
899
+ "^[a-z0-9]{24}$", (client_id or "")
900
+ ), "Please supply entity ID as 'server' with de-assignment data!"
874
901
  response = self._event(
875
902
  "DELETE",
876
903
  f"{entitytype_parent}/server",
@@ -882,7 +909,7 @@ class Session(object):
882
909
  else:
883
910
  raise AccsynException("Unsupported assignment operation!")
884
911
 
885
- # Entity access grant / revocation
912
+ # Entity access grant / revocation
886
913
 
887
914
  def grant(
888
915
  self,
@@ -921,13 +948,15 @@ class Session(object):
921
948
  f"{entitytype}/find",
922
949
  query=f"code='{entityid}'",
923
950
  )
924
- if 0<len(response.get("result", [])):
951
+ if 0 < len(response.get("result", [])):
925
952
  # Use the correct ID
926
953
  entityid = response["result"][0]["id"]
927
954
  else:
928
955
  # Allow this - user will be invited
929
956
  if (data or dict()).get("invite", True) is False:
930
- raise AccsynException(f"No {entitytype} found with code '{entityid}', and invite is not allowed!")
957
+ raise AccsynException(
958
+ f"No {entitytype} found with code '{entityid}', and invite is not allowed!"
959
+ )
931
960
  else:
932
961
  Session._warning(f"No {entitytype} found with code '{entityid}', will be invited!")
933
962
  else:
@@ -955,7 +984,7 @@ class Session(object):
955
984
  raise AccsynException(f"Please supply a valid {targettype} ID!")
956
985
  result = None
957
986
  if entitytype == "user":
958
- if targettype in ["delivery","request","stream"]:
987
+ if targettype in ["delivery", "request", "stream"]:
959
988
  # Assign a user to a delivery, expect delivery and user supplied
960
989
  user_id = entityid
961
990
  delivery_id = targetid
@@ -970,7 +999,7 @@ class Session(object):
970
999
  entityid=delivery_id,
971
1000
  )
972
1001
  result = response["result"]
973
- elif targettype in ["volume","folder","home","collection"]:
1002
+ elif targettype in ["volume", "folder", "home", "collection"]:
974
1003
  # Assign an employee user to a volume
975
1004
  assert (
976
1005
  data is not None and isinstance(data, dict) and (0 < len(data or dict()))
@@ -988,11 +1017,7 @@ class Session(object):
988
1017
  )
989
1018
  if (data or dict()).get("invite", True) is False:
990
1019
  payload["invite"] = False
991
- response = self._event(
992
- "POST",
993
- f"acl/create",
994
- payload
995
- )
1020
+ response = self._event("POST", f"acl/create", payload)
996
1021
  result = response["result"][0]
997
1022
  if result is not None:
998
1023
  return result
@@ -1031,7 +1056,7 @@ class Session(object):
1031
1056
  entityid=targetid,
1032
1057
  )
1033
1058
  return response["result"]
1034
- elif targettype.lower() in ["volume", "folder", "home","collection"]:
1059
+ elif targettype.lower() in ["volume", "folder", "home", "collection"]:
1035
1060
  # List users with access to a share
1036
1061
  response = self._event(
1037
1062
  "GET",
@@ -1041,27 +1066,23 @@ class Session(object):
1041
1066
  )
1042
1067
  result = []
1043
1068
  for acl in response["result"]:
1044
- result.append(dict(
1045
- user=acl["entity"].split(":")[1],
1046
- user_hr=acl.get("entity_hr", ""),
1047
- share=acl["target"].split(":")[1],
1048
- share_hr=acl.get("target_hr", ""),
1049
- read=acl["read"],
1050
- write=acl["write"],
1051
- acknowledged=acl.get("acknowledged", False),
1052
- path=acl.get("path", "/"),
1053
- ))
1069
+ result.append(
1070
+ dict(
1071
+ user=acl["entity"].split(":")[1],
1072
+ user_hr=acl.get("entity_hr", ""),
1073
+ share=acl["target"].split(":")[1],
1074
+ share_hr=acl.get("target_hr", ""),
1075
+ read=acl["read"],
1076
+ write=acl["write"],
1077
+ acknowledged=acl.get("acknowledged", False),
1078
+ path=acl.get("path", "/"),
1079
+ )
1080
+ )
1054
1081
  return result
1055
1082
  else:
1056
1083
  raise AccsynException("Unsupported access operation!")
1057
1084
 
1058
- def revoke(
1059
- self,
1060
- entitytype: str,
1061
- entityid: str,
1062
- targettype: str,
1063
- targetid: str
1064
- ) -> bool:
1085
+ def revoke(self, entitytype: str, entityid: str, targettype: str, targetid: str) -> bool:
1065
1086
  """
1066
1087
  Revoke access to an entity.
1067
1088
 
@@ -1090,7 +1111,7 @@ class Session(object):
1090
1111
  f"{entitytype}/find",
1091
1112
  query=f"code='{entityid}'",
1092
1113
  )
1093
- if 0<len(response.get("result", [])):
1114
+ if 0 < len(response.get("result", [])):
1094
1115
  # Use the correct ID
1095
1116
  entityid = response["result"][0]["id"]
1096
1117
  else:
@@ -1118,12 +1139,14 @@ class Session(object):
1118
1139
  dict(recipient=user_id),
1119
1140
  entityid=delivery_id,
1120
1141
  )
1121
- elif targettype in ["volume","folder","home","collection"] and entitytype == "user":
1142
+ elif targettype in ["volume", "folder", "home", "collection"] and entitytype == "user":
1122
1143
  # Revoke user access from a share
1123
1144
  share_id = targetid
1124
1145
  user_id = entityid
1125
1146
  # First, located the ACL
1126
- response = self._event("GET", f"acl/find", dict(), query=f"acl WHERE entity=user:{user_id} AND target=share:{share_id}")
1147
+ response = self._event(
1148
+ "GET", f"acl/find", dict(), query=f"acl WHERE entity=user:{user_id} AND target=share:{share_id}"
1149
+ )
1127
1150
  acls = response["result"]
1128
1151
  if len(acls) == 0:
1129
1152
  raise AccsynException(f"No ACL found for user {user_id} and share {share_id}")
@@ -1138,7 +1161,6 @@ class Session(object):
1138
1161
  else:
1139
1162
  raise AccsynException("Unsupported revoke access operation!")
1140
1163
 
1141
-
1142
1164
  # Deactivate/Delete an entity
1143
1165
 
1144
1166
  def offline_one(self, entitytype: str, entityid: str) -> Any:
@@ -1224,7 +1246,9 @@ class Session(object):
1224
1246
  ), "Invalid entity ID supplied, must be of string type!"
1225
1247
  if not re.match("^[a-z0-9]{24}$", (entityid or "")):
1226
1248
  raise AccsynException("Invalid parent entity ID supplied!")
1227
- assert 0 < len(data or dict()) and isinstance(data, dict), "Invalid data supplied, must be dict and have content!"
1249
+ assert 0 < len(data or dict()) and isinstance(
1250
+ data, dict
1251
+ ), "Invalid data supplied, must be dict and have content!"
1228
1252
  if entitytype == "collection":
1229
1253
  uri = "file/remove"
1230
1254
  else:
@@ -1258,7 +1282,7 @@ class Session(object):
1258
1282
  f"{entitytype}/activate",
1259
1283
  dict(),
1260
1284
  entityid=entityident if re.match("^[a-z0-9]{24}$", (entityident or "")) else None,
1261
- query=entityident if not re.match("^[a-z0-9]{24}$", (entityident or "")) else None
1285
+ query=entityident if not re.match("^[a-z0-9]{24}$", (entityident or "")) else None,
1262
1286
  )
1263
1287
  if response:
1264
1288
  return response["result"]
@@ -1298,13 +1322,7 @@ class Session(object):
1298
1322
  assert 0 < len(path or "") and (
1299
1323
  Session._is_str(path) or isinstance(path, dict) or isinstance(path, list)
1300
1324
  ), "No path supplied, or not a string/list/dict!"
1301
- data = dict(
1302
- op="ls",
1303
- path=path,
1304
- download=True,
1305
- recursive=recursive,
1306
- getsize=getsize
1307
- )
1325
+ data = dict(op="ls", path=path, download=True, recursive=recursive, getsize=getsize)
1308
1326
  if maxdepth:
1309
1327
  data["maxdepth"] = maxdepth
1310
1328
  if directories_only:
@@ -1353,9 +1371,7 @@ class Session(object):
1353
1371
  if response:
1354
1372
  return response["result"]
1355
1373
 
1356
- def exists(
1357
- self, path: Union[str, Dict[str, Any], List[str]]
1358
- ) -> Optional[bool]:
1374
+ def exists(self, path: Union[str, Dict[str, Any], List[str]]) -> Optional[bool]:
1359
1375
  """
1360
1376
  Check if a file or directory exists.
1361
1377
 
@@ -1373,9 +1389,7 @@ class Session(object):
1373
1389
  if response:
1374
1390
  return response["result"]
1375
1391
 
1376
- def mkdir(
1377
- self, path: Union[str, Dict[str, Any], List[str]]
1378
- ) -> Optional[Any]:
1392
+ def mkdir(self, path: Union[str, Dict[str, Any], List[str]]) -> Optional[Any]:
1379
1393
  """
1380
1394
  Create a directory on a share.
1381
1395
 
@@ -1454,7 +1468,8 @@ class Session(object):
1454
1468
  return response["result"]
1455
1469
 
1456
1470
  def delete(
1457
- self, path: Union[str, Dict[str, Any], List[str]],
1471
+ self,
1472
+ path: Union[str, Dict[str, Any], List[str]],
1458
1473
  force: bool = False,
1459
1474
  ) -> Optional[Any]:
1460
1475
  """
@@ -1602,9 +1617,7 @@ class Session(object):
1602
1617
  break
1603
1618
  return retval
1604
1619
 
1605
- def integration(
1606
- self, name: str, operation: str, data: Dict[str, Any]
1607
- ) -> Any:
1620
+ def integration(self, name: str, operation: str, data: Dict[str, Any]) -> Any:
1608
1621
  '''Make an integration utility call for integration pointed out by *name* and providing the *operation* as string and *data* as a dictionary'''
1609
1622
  assert len(name) > 0, 'No name provided'
1610
1623
  assert len(operation) > 0, 'No operation provided'
@@ -1620,15 +1633,17 @@ class Session(object):
1620
1633
  # Help
1621
1634
  def help(self) -> None:
1622
1635
  documentation_url = "https://accsyn-python-api.readthedocs.io/en/latest/"
1623
- print(f"Please have a look at the Python API reference: {documentation_url}, attempting to open in default browser...")
1624
- # Open the documentation url in the browser
1636
+ print(
1637
+ f"Please have a look at the Python API reference: {documentation_url}, attempting to open in default browser..."
1638
+ )
1639
+ # Open the documentation url in the browser
1625
1640
  webbrowser.open(documentation_url)
1626
-
1641
+
1627
1642
  @staticmethod
1628
1643
  def str(d: Optional[Dict[str, Any]], indent: int = 4) -> str:
1629
1644
  """
1630
1645
  Return a string representation of an dictionary.
1631
-
1646
+
1632
1647
  .. deprecated:: 3.2.0
1633
1648
  Use the :func:`dump` function instead
1634
1649
  """
@@ -1639,7 +1654,7 @@ class Session(object):
1639
1654
  def dump(d: Optional[Dict[str, Any]], indent: int = 4) -> str:
1640
1655
  """
1641
1656
  Return a string representation of an dictionary.
1642
-
1657
+
1643
1658
  .. versionchanged:: 3.2.0
1644
1659
  Was 'str' function.
1645
1660
 
@@ -1738,7 +1753,9 @@ class Session(object):
1738
1753
  try:
1739
1754
  import socks
1740
1755
  except ImportError as ie:
1741
- logging.error("Socks module is not installed, please install it with 'pip install socks' or add it to your PYTHONPATH")
1756
+ logging.error(
1757
+ "Socks module is not installed, please install it with 'pip install socks' or add it to your PYTHONPATH"
1758
+ )
1742
1759
  raise ie
1743
1760
 
1744
1761
  socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, proxy_hostname, proxy_port)