sumo-wrapper-python 1.0.8__py3-none-any.whl → 1.0.10__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.

Potentially problematic release.


This version of sumo-wrapper-python might be problematic. Click here for more details.

@@ -34,6 +34,9 @@ class AuthProvider:
34
34
  self._resource_id = resource_id
35
35
  self._scope = scope_for_resource(resource_id)
36
36
  self._app = None
37
+ self._login_timeout_minutes = 5
38
+ os.system("") # Ensure color init on all platforms (win10)
39
+
37
40
  return
38
41
 
39
42
  @tn.retry(
@@ -57,11 +60,27 @@ class AuthProvider:
57
60
  return result["access_token"]
58
61
 
59
62
  def get_authorization(self):
60
- return {"Authorization": "Bearer " + self.get_token()}
63
+ token = self.get_token()
64
+ if token is None:
65
+ return ""
66
+
67
+ return {"Authorization": "Bearer " + token}
61
68
 
62
69
  pass
63
70
 
64
71
 
72
+ class AuthProviderSilent(AuthProvider):
73
+ def __init__(self, client_id, authority, resource_id):
74
+ super().__init__(resource_id)
75
+ cache = get_token_cache(resource_id, ".token")
76
+ self._app = msal.PublicClientApplication(
77
+ client_id=client_id, authority=authority, token_cache=cache
78
+ )
79
+ self._resource_id = resource_id
80
+
81
+ self._scope = scope_for_resource(resource_id)
82
+
83
+
65
84
  class AuthProviderAccessToken(AuthProvider):
66
85
  def __init__(self, access_token):
67
86
  self._access_token = access_token
@@ -195,25 +214,24 @@ class AuthProviderInteractive(AuthProvider):
195
214
  )
196
215
  def login(self):
197
216
  scopes = [self._scope + " offline_access"]
198
- login_timeout_minutes = 7
199
- os.system("") # Ensure color init on all platforms (win10)
200
217
  print(
201
218
  "\n\n \033[31m NOTE! \033[0m"
202
219
  + " Please login to Equinor Azure to enable Sumo access: "
203
- + "we opened a login web-page for you in your browser."
220
+ + "we are opening a login web-page for you in your browser."
204
221
  + "\nYou should complete your login within "
205
- + str(login_timeout_minutes)
222
+ + str(self._login_timeout_minutes)
206
223
  + " minutes, "
207
224
  + "that is before "
208
225
  + str(
209
226
  (
210
- datetime.now() + timedelta(minutes=login_timeout_minutes)
227
+ datetime.now()
228
+ + timedelta(minutes=self._login_timeout_minutes)
211
229
  ).strftime("%H:%M:%S")
212
230
  )
213
231
  )
214
232
  try:
215
233
  result = self._app.acquire_token_interactive(
216
- scopes, timeout=(login_timeout_minutes * 60)
234
+ scopes, timeout=(self._login_timeout_minutes * 60)
217
235
  )
218
236
  if "error" in result:
219
237
  print(
@@ -230,7 +248,9 @@ class AuthProviderInteractive(AuthProvider):
230
248
  return
231
249
 
232
250
  protect_token_cache(self._resource_id, ".token")
233
- print("Equinor Azure login for Sumo access was successful")
251
+ print(
252
+ "Equinor Azure login for Sumo access was successful (interactive)"
253
+ )
234
254
  return
235
255
 
236
256
  pass
@@ -261,24 +281,47 @@ class AuthProviderDeviceCode(AuthProvider):
261
281
  before_sleep=_log_retry_info,
262
282
  )
263
283
  def login(self):
264
- flow = self._app.initiate_device_flow([self._scope])
265
-
266
- if "error" in flow:
267
- raise ValueError(
268
- "Failed to create device flow. Err: %s"
269
- % json.dumps(flow, indent=4)
284
+ try:
285
+ scopes = [self._scope + " offline_access"]
286
+ flow = self._app.initiate_device_flow(scopes)
287
+ if "error" in flow:
288
+ print(
289
+ "\n\n \033[31m"
290
+ + "Failed to initiate device-code login. Err: %s"
291
+ + "\033[0m" % json.dumps(flow, indent=4)
292
+ )
293
+ return
294
+ flow["expires_at"] = (
295
+ int(time.time()) + self._login_timeout_minutes * 60
270
296
  )
271
297
 
272
- print(flow["message"])
273
- result = self._app.acquire_token_by_device_flow(flow)
298
+ print(
299
+ "\033[31m"
300
+ + " NOTE! Please login to Equinor Azure to enable Sumo access:"
301
+ + flow["message"]
302
+ + " \033[0m"
303
+ + "\nYou should complete your login within a few minutes"
304
+ )
305
+ result = self._app.acquire_token_by_device_flow(flow)
274
306
 
275
- if "error" in result:
276
- raise ValueError(
277
- "Failed to acquire token by device flow. Err: %s"
278
- % json.dumps(result, indent=4)
307
+ if "error" in result:
308
+ print(
309
+ "\n\n \033[31m Error during Equinor Azure login "
310
+ "for Sumo access: \033[0m"
311
+ )
312
+ print("Err: ", json.dumps(result, indent=4))
313
+ return
314
+ except Exception:
315
+ print(
316
+ "\n\n \033[31m Failed Equinor Azure login for Sumo access, "
317
+ "one possible reason is timeout \033[0m"
279
318
  )
319
+ return
280
320
 
281
321
  protect_token_cache(self._resource_id, ".token")
322
+ print(
323
+ "Equinor Azure login for Sumo access was successful (device-code)"
324
+ )
282
325
 
283
326
  return
284
327
 
@@ -363,6 +406,11 @@ def get_auth_provider(
363
406
  if os.path.exists(get_token_path(resource_id, ".sharedkey")):
364
407
  return AuthProviderSumoToken(resource_id)
365
408
  # ELSE
409
+ auth_silent = AuthProviderSilent(client_id, authority, resource_id)
410
+ token = auth_silent.get_token()
411
+ if token is not None:
412
+ return auth_silent
413
+ # ELSE
366
414
  if interactive:
367
415
  return AuthProviderInteractive(client_id, authority, resource_id)
368
416
  # ELSE
@@ -383,5 +431,3 @@ def get_auth_provider(
383
431
  ]
384
432
  ):
385
433
  return AuthProviderManaged(resource_id)
386
- # ELSE
387
- return AuthProviderInteractive(client_id, authority, resource_id)
@@ -1,17 +1,16 @@
1
- import httpx
2
-
3
1
  from ._decorators import (
4
2
  raise_for_status,
5
3
  raise_for_status_async,
6
4
  )
7
5
 
8
- from ._retry_strategy import RetryStrategy
9
-
10
6
 
11
7
  class BlobClient:
12
8
  """Upload blobs to blob store using pre-authorized URLs"""
13
9
 
14
- def __init__(self, retry_strategy=RetryStrategy()):
10
+ def __init__(self, client, async_client, timeout, retry_strategy):
11
+ self._client = client
12
+ self._async_client = async_client
13
+ self._timeout = timeout
15
14
  self._retry_strategy = retry_strategy
16
15
  return
17
16
 
@@ -30,7 +29,9 @@ class BlobClient:
30
29
  }
31
30
 
32
31
  def _put():
33
- return httpx.put(url, content=blob, headers=headers)
32
+ return self._client.put(
33
+ url, content=blob, headers=headers, timeout=self._timeout
34
+ )
34
35
 
35
36
  retryer = self._retry_strategy.make_retryer()
36
37
 
@@ -51,8 +52,9 @@ class BlobClient:
51
52
  }
52
53
 
53
54
  async def _put():
54
- async with httpx.AsyncClient() as client:
55
- return await client.put(url=url, content=blob, headers=headers)
55
+ return await self._async_client.put(
56
+ url=url, content=blob, headers=headers, timeout=self._timeout
57
+ )
56
58
 
57
59
  retryer = self._retry_strategy.make_retryer_async()
58
60
 
@@ -1,4 +1,9 @@
1
+ # For sphinx:
2
+ from functools import wraps
3
+
4
+
1
5
  def raise_for_status(func):
6
+ @wraps(func)
2
7
  def wrapper(*args, **kwargs):
3
8
  # FIXME: in newer versions of httpx, raise_for_status() is chainable,
4
9
  # so we could simply write
@@ -11,6 +16,7 @@ def raise_for_status(func):
11
16
 
12
17
 
13
18
  def raise_for_status_async(func):
19
+ @wraps(func)
14
20
  async def wrapper(*args, **kwargs):
15
21
  # FIXME: in newer versions of httpx, raise_for_status() is chainable,
16
22
  # so we could simply write
sumo/wrapper/_logging.py CHANGED
@@ -20,6 +20,9 @@ class LogHandlerSumo(logging.Handler):
20
20
  "funcname": record.funcName,
21
21
  "linenumber": record.lineno,
22
22
  }
23
+ if "objectUuid" in record.__dict__.keys():
24
+ json["objectUuid"] = record.__dict__.get("objectUuid")
25
+
23
26
  self._sumoClient.post("/message-log/new", json=json)
24
27
  except Exception:
25
28
  # Never fail on logging
sumo/wrapper/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.0.8'
16
- __version_tuple__ = version_tuple = (1, 0, 8)
15
+ __version__ = version = '1.0.10'
16
+ __version_tuple__ = version_tuple = (1, 0, 10)
sumo/wrapper/login.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import logging
2
-
2
+ import platform
3
+ from pathlib import Path
3
4
  from argparse import ArgumentParser
4
5
  from sumo.wrapper import SumoClient
5
6
 
@@ -7,6 +8,8 @@ from sumo.wrapper import SumoClient
7
8
  logger = logging.getLogger("sumo.wrapper")
8
9
  logger.setLevel(level="CRITICAL")
9
10
 
11
+ modes = ["interactive", "devicecode", "silent"]
12
+
10
13
 
11
14
  def get_parser() -> ArgumentParser:
12
15
  parser = ArgumentParser(description="Login to Sumo on azure")
@@ -29,21 +32,12 @@ def get_parser() -> ArgumentParser:
29
32
  )
30
33
 
31
34
  parser.add_argument(
32
- "-i",
33
- "--interactive",
34
- dest="interactive",
35
- action="store_true",
36
- default=False,
37
- help="Login interactively",
38
- )
39
-
40
- parser.add_argument(
41
- "-d",
42
- "--devicecode",
43
- dest="devicecode",
44
- action="store_true",
45
- default=False,
46
- help="Login with device-code",
35
+ "-m",
36
+ "--mode",
37
+ dest="mode",
38
+ action="store",
39
+ default="interactive",
40
+ help=f"Valid modes: {', '.join(modes)}",
47
41
  )
48
42
 
49
43
  parser.add_argument(
@@ -62,17 +56,48 @@ def main():
62
56
  args = get_parser().parse_args()
63
57
  logger.setLevel(level=args.verbosity)
64
58
  env = args.env
59
+ mode = args.mode
60
+ is_interactive = mode == "interactive"
61
+ is_devicecode = mode == "devicecode"
62
+
65
63
  logger.debug("env is %s", env)
66
64
 
67
- print("Login to Sumo environment: " + env)
65
+ if mode not in modes:
66
+ print(f"Invalid mode: {mode}")
67
+ return 1
68
+
69
+ if mode != "silent":
70
+ print("Login to Sumo environment: " + env)
71
+
72
+ if mode == "interactive":
73
+ lockfile_path = Path.home() / ".config/chromium/SingletonLock"
74
+
75
+ if Path(lockfile_path).is_symlink() and not str(
76
+ Path(lockfile_path).resolve()
77
+ ).__contains__(platform.node()):
78
+ # https://github.com/equinor/sumo-wrapper-python/issues/193
79
+ is_interactive = False
80
+ is_devicecode = True
68
81
 
69
82
  sumo = SumoClient(
70
- args.env, interactive=args.interactive, devicecode=args.devicecode
83
+ env,
84
+ interactive=is_interactive,
85
+ devicecode=is_devicecode,
71
86
  )
72
87
  token = sumo.authenticate()
73
88
 
74
- if args.print_token:
75
- print(f"TOKEN: {token}")
89
+ if mode != "silent":
90
+ if args.print_token:
91
+ print(token)
92
+
93
+ if token is not None:
94
+ print("Successfully logged in to Sumo environment: " + env)
95
+ else:
96
+ print("Failed login to Sumo environment: " + env)
97
+
98
+ if token is None:
99
+ return 1
100
+ return 0
76
101
 
77
102
 
78
103
  if __name__ == "__main__":
@@ -1,7 +1,6 @@
1
1
  import logging
2
-
2
+ import asyncio
3
3
  import httpx
4
-
5
4
  import jwt
6
5
 
7
6
  from ._blob_client import BlobClient
@@ -18,7 +17,7 @@ from ._retry_strategy import RetryStrategy
18
17
 
19
18
  logger = logging.getLogger("sumo.wrapper")
20
19
 
21
- DEFAULT_TIMEOUT = httpx.Timeout(20.0)
20
+ DEFAULT_TIMEOUT = httpx.Timeout(30.0)
22
21
 
23
22
 
24
23
  class SumoClient:
@@ -32,6 +31,7 @@ class SumoClient:
32
31
  devicecode: bool = False,
33
32
  verbosity: str = "CRITICAL",
34
33
  retry_strategy=RetryStrategy(),
34
+ timeout=DEFAULT_TIMEOUT,
35
35
  ):
36
36
  """Initialize a new Sumo object
37
37
 
@@ -49,7 +49,10 @@ class SumoClient:
49
49
  raise ValueError(f"Invalid environment: {env}")
50
50
 
51
51
  self._retry_strategy = retry_strategy
52
- self._blob_client = BlobClient(retry_strategy)
52
+ self._client = httpx.Client(follow_redirects=True)
53
+ self._async_client = httpx.AsyncClient(follow_redirects=True)
54
+
55
+ self._timeout = timeout
53
56
 
54
57
  access_token = None
55
58
  refresh_token = None
@@ -92,7 +95,44 @@ class SumoClient:
92
95
  pass
93
96
  return
94
97
 
98
+ def __enter__(self):
99
+ return self
100
+
101
+ def __exit__(self, exc_type, exc_value, traceback):
102
+ self._client.close()
103
+ self._client = None
104
+ return False
105
+
106
+ async def __aenter__(self):
107
+ return self
108
+
109
+ async def __aexit__(self, exc_type, exc_value, traceback):
110
+ await self._async_client.aclose()
111
+ self._async_client = None
112
+ return False
113
+
114
+ def __del__(self):
115
+ if self._client is not None:
116
+ self._client.close()
117
+ self._client = None
118
+ pass
119
+ if self._async_client is not None:
120
+
121
+ async def closeit(client):
122
+ await client.aclose()
123
+ return
124
+
125
+ try:
126
+ loop = asyncio.get_running_loop()
127
+ loop.create_task(closeit(self._async_client))
128
+ except RuntimeError:
129
+ pass
130
+ self._async_client = None
131
+ pass
132
+
95
133
  def authenticate(self):
134
+ if self.auth is None:
135
+ return None
96
136
  return self.auth.get_token()
97
137
 
98
138
  @property
@@ -115,7 +155,12 @@ class SumoClient:
115
155
  await sumo.blob_client.upload_blob_async(blob, blob_url)
116
156
  """
117
157
 
118
- return self._blob_client
158
+ return BlobClient(
159
+ self._client,
160
+ self._async_client,
161
+ self._timeout,
162
+ self._retry_strategy,
163
+ )
119
164
 
120
165
  @raise_for_status
121
166
  def get(self, path: str, params: dict = None) -> dict:
@@ -153,12 +198,12 @@ class SumoClient:
153
198
  headers.update(self.auth.get_authorization())
154
199
 
155
200
  def _get():
156
- return httpx.get(
201
+ return self._client.get(
157
202
  f"{self.base_url}{path}",
158
203
  params=params,
159
204
  headers=headers,
160
205
  follow_redirects=True,
161
- timeout=DEFAULT_TIMEOUT,
206
+ timeout=self._timeout,
162
207
  )
163
208
 
164
209
  retryer = self._retry_strategy.make_retryer()
@@ -227,13 +272,13 @@ class SumoClient:
227
272
  headers.update(self.auth.get_authorization())
228
273
 
229
274
  def _post():
230
- return httpx.post(
275
+ return self._client.post(
231
276
  f"{self.base_url}{path}",
232
277
  content=blob,
233
278
  json=json,
234
279
  headers=headers,
235
280
  params=params,
236
- timeout=DEFAULT_TIMEOUT,
281
+ timeout=self._timeout,
237
282
  )
238
283
 
239
284
  retryer = self._retry_strategy.make_retryer()
@@ -274,12 +319,12 @@ class SumoClient:
274
319
  headers.update(self.auth.get_authorization())
275
320
 
276
321
  def _put():
277
- return httpx.put(
322
+ return self._client.put(
278
323
  f"{self.base_url}{path}",
279
324
  content=blob,
280
325
  json=json,
281
326
  headers=headers,
282
- timeout=DEFAULT_TIMEOUT,
327
+ timeout=self._timeout,
283
328
  )
284
329
 
285
330
  retryer = self._retry_strategy.make_retryer()
@@ -313,11 +358,11 @@ class SumoClient:
313
358
  headers.update(self.auth.get_authorization())
314
359
 
315
360
  def _delete():
316
- return httpx.delete(
361
+ return self._client.delete(
317
362
  f"{self.base_url}{path}",
318
363
  headers=headers,
319
364
  params=params,
320
- timeout=DEFAULT_TIMEOUT,
365
+ timeout=self._timeout,
321
366
  )
322
367
 
323
368
  retryer = self._retry_strategy.make_retryer()
@@ -378,13 +423,12 @@ class SumoClient:
378
423
  headers.update(self.auth.get_authorization())
379
424
 
380
425
  async def _get():
381
- async with httpx.AsyncClient(follow_redirects=True) as client:
382
- return await client.get(
383
- f"{self.base_url}{path}",
384
- params=params,
385
- headers=headers,
386
- timeout=DEFAULT_TIMEOUT,
387
- )
426
+ return await self._async_client.get(
427
+ f"{self.base_url}{path}",
428
+ params=params,
429
+ headers=headers,
430
+ timeout=self._timeout,
431
+ )
388
432
 
389
433
  retryer = self._retry_strategy.make_retryer_async()
390
434
 
@@ -453,15 +497,14 @@ class SumoClient:
453
497
  headers.update(self.auth.get_authorization())
454
498
 
455
499
  async def _post():
456
- async with httpx.AsyncClient() as client:
457
- return await client.post(
458
- url=f"{self.base_url}{path}",
459
- content=blob,
460
- json=json,
461
- headers=headers,
462
- params=params,
463
- timeout=DEFAULT_TIMEOUT,
464
- )
500
+ return await self._async_client.post(
501
+ url=f"{self.base_url}{path}",
502
+ content=blob,
503
+ json=json,
504
+ headers=headers,
505
+ params=params,
506
+ timeout=self._timeout,
507
+ )
465
508
 
466
509
  retryer = self._retry_strategy.make_retryer_async()
467
510
 
@@ -501,14 +544,13 @@ class SumoClient:
501
544
  headers.update(self.auth.get_authorization())
502
545
 
503
546
  async def _put():
504
- async with httpx.AsyncClient() as client:
505
- return await client.put(
506
- url=f"{self.base_url}{path}",
507
- content=blob,
508
- json=json,
509
- headers=headers,
510
- timeout=DEFAULT_TIMEOUT,
511
- )
547
+ return await self._async_client.put(
548
+ url=f"{self.base_url}{path}",
549
+ content=blob,
550
+ json=json,
551
+ headers=headers,
552
+ timeout=self._timeout,
553
+ )
512
554
 
513
555
  retryer = self._retry_strategy.make_retryer_async()
514
556
 
@@ -541,13 +583,12 @@ class SumoClient:
541
583
  headers.update(self.auth.get_authorization())
542
584
 
543
585
  async def _delete():
544
- async with httpx.AsyncClient() as client:
545
- return await client.delete(
546
- url=f"{self.base_url}{path}",
547
- headers=headers,
548
- params=params,
549
- timeout=DEFAULT_TIMEOUT,
550
- )
586
+ return await self._async_client.delete(
587
+ url=f"{self.base_url}{path}",
588
+ headers=headers,
589
+ params=params,
590
+ timeout=self._timeout,
591
+ )
551
592
 
552
593
  retryer = self._retry_strategy.make_retryer_async()
553
594
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sumo-wrapper-python
3
- Version: 1.0.8
3
+ Version: 1.0.10
4
4
  Summary: Python wrapper for the Sumo API
5
5
  Author: Equinor
6
6
  License: Apache License
@@ -211,174 +211,30 @@ Classifier: Programming Language :: Python
211
211
  Requires-Python: >=3.8
212
212
  Description-Content-Type: text/markdown
213
213
  License-File: LICENSE
214
- Requires-Dist: msal >=1.20.0
215
- Requires-Dist: msal-extensions >=1.0.0
216
- Requires-Dist: pyjwt >=2.4.0
217
- Requires-Dist: httpx >=0.24.1
218
- Requires-Dist: tenacity >=8.2.2
219
- Requires-Dist: azure-identity >=1.13.0
214
+ Requires-Dist: msal>=1.20.0
215
+ Requires-Dist: msal-extensions>=1.0.0
216
+ Requires-Dist: pyjwt>=2.4.0
217
+ Requires-Dist: httpx>=0.24.1
218
+ Requires-Dist: tenacity!=8.4.0,>=8.2.2
219
+ Requires-Dist: azure-identity>=1.13.0
220
220
  Provides-Extra: docs
221
- Requires-Dist: sphinx ; extra == 'docs'
222
- Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
221
+ Requires-Dist: sphinx; extra == "docs"
222
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
223
+ Requires-Dist: autoapi; extra == "docs"
224
+ Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
225
+ Requires-Dist: sphinxcontrib-apidoc; extra == "docs"
223
226
  Provides-Extra: test
224
- Requires-Dist: pytest ; extra == 'test'
225
- Requires-Dist: PyYAML ; extra == 'test'
227
+ Requires-Dist: pytest; extra == "test"
228
+ Requires-Dist: PyYAML; extra == "test"
226
229
 
227
230
  # sumo-wrapper-python
228
231
 
229
- Python wrappers for Sumo APIs
232
+ [![Documentation Status](https://readthedocs.org/projects/sumo-wrapper-python/badge/?version=latest)](https://sumo-wrapper-python.readthedocs.io/en/latest/?badge=latest)
233
+ [![SCM Compliance](https://scm-compliance-api.radix.equinor.com/repos/equinor/sumo-wrapper-python/badge)](https://scm-compliance-api.radix.equinor.com/repos/equinor/sumo-wrapper-python/badge)
230
234
 
231
- Want to contribute? Read our [contributing](./CONTRIBUTING.md) guidelines
235
+ ## Documentation and guidelines
236
+ [sumo-wrapper-python documentation](https://sumo-wrapper-python.readthedocs.io/en/latest/)
232
237
 
233
- ## Install:
238
+ ## Contribute
239
+ [Contribution guidelines](./CONTRIBUTING.md)
234
240
 
235
- pip install sumo-wrapper-python
236
-
237
- For internal Equinor users, this package is available through the Komodo
238
- distribution.
239
-
240
- # Table of contents
241
-
242
- - [sumo-wrapper-python](#sumo-wrapper-python)
243
- - [Install:](#install)
244
- - [Table of contents](#table-of-contents)
245
- - [SumoClient](#sumoclient)
246
- - [Initialization](#initialization)
247
- - [Parameters](#parameters)
248
- - [`token` logic](#token-logic)
249
- - [Methods](#methods)
250
- - [get(path, \*\*params)](#getpath-params)
251
- - [post(path, json, blob, params)](#postpath-json-blob-params)
252
- - [put(path, json, blob)](#putpath-json-blob)
253
- - [delete(path)](#deletepath)
254
- - [Async methods](#async-methods)
255
-
256
- # SumoClient
257
-
258
- A thin wrapper class for the Sumo API.
259
-
260
- ### Initialization
261
-
262
- ```python
263
- from sumo.wrapper import SumoClient
264
-
265
- sumo = SumoClient(env="dev")
266
- ```
267
-
268
- ### Parameters
269
-
270
- ```python
271
- class SumoClient:
272
- def __init__(
273
- self,
274
- env:str,
275
- token:str=None,
276
- interactive:bool=False,
277
- verbosity:str="CRITICAL"
278
- ):
279
- ```
280
-
281
- - `env`: sumo environment
282
- - `token`: bearer token or refresh token
283
- - `interactive`: use interactive flow when authenticating
284
- - `verbosity`: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
285
-
286
- ###### `token` logic
287
-
288
- If an access token is provided in the `token` parameter, it will be used as long
289
- as it's valid. An error will be raised when it expires.
290
-
291
- If we are unable to decode the provided `token` as a JWT, we treat it as a
292
- refresh token and attempt to use it to retrieve an access token.
293
-
294
- If no `token` is provided, an authentication code flow/interactive flow is
295
- triggered to retrieve a token.
296
-
297
- ## Methods
298
-
299
- `SumoClient` has one method for each HTTP-method that is used in the sumo-core
300
- API. See examples of how to use these methods below.
301
-
302
- All methods accepts a path argument. Path parameters can be interpolated into
303
- the path string. Example:
304
-
305
- ```python
306
- object_id = "1234"
307
-
308
- # GET/objects('{obejctid}')
309
- sumo.get(f"/objects('{object_id}')")
310
- ```
311
-
312
- ### get(path, \*\*params)
313
-
314
- Performs a GET-request to sumo-core. Accepts query parameters as keyword
315
- arguments.
316
-
317
- ```python
318
- # Retrieve userdata
319
- user_data = sumo.get("/userdata")
320
-
321
- # Search for objects
322
- results = sumo.get("/search",
323
- query="class:surface",
324
- size:3,
325
- select=["_id"]
326
- )
327
-
328
- # Get object by id
329
- object_id = "159405ba-0046-b321-55ce-542f383ba5c7"
330
-
331
- obj = sumo.get(f"/objects('{object_id}')")
332
- ```
333
-
334
- ### post(path, json, blob, params)
335
-
336
- Performs a POST-request to sumo-core. Accepts json and blob, but not both at the
337
- same time.
338
-
339
- ```python
340
- # Upload new parent object
341
- parent_object = sumo.post("/objects", json=parent_meta_data)
342
-
343
- # Upload child object
344
- parent_id = parent_object["_id"]
345
-
346
- child_object = sumo.post(f"/objects('{parent_id}')", json=child_meta_data)
347
- ```
348
-
349
- ### put(path, json, blob)
350
-
351
- Performs a PUT-request to sumo-core. Accepts json and blob, but not both at the
352
- same time.
353
-
354
- ```python
355
- # Upload blob to child object
356
- child_id = child_object["_id"]
357
-
358
- sumo.put(f"/objects('{child_id}')/blob", blob=blob)
359
- ```
360
-
361
- ### delete(path)
362
-
363
- Performs a DELETE-request to sumo-core.
364
-
365
- ```python
366
- # Delete blob
367
- sumo.delete(f"/objects('{child_id}')/blob")
368
-
369
- # Delete child object
370
- sumo.delete(f"/objects('{child_id}')")
371
-
372
- # Delete parent object
373
- sumo.delete(f"/objects('{parent_id}')")
374
- ```
375
-
376
- ## Async methods
377
-
378
- `SumoClient` also has *async* alternatives `get_async`, `post_async`, `put_async` and `delete_async`.
379
- These accept the same parameters as their synchronous counterparts, but have to be *awaited*.
380
-
381
- ```python
382
- # Retrieve userdata
383
- user_data = await sumo.get_async("/userdata")
384
- ```
@@ -0,0 +1,17 @@
1
+ sumo/__init__.py,sha256=ftS-xRPSH-vU7fIHlnZQaCTWbNvs4owJivNW65kzsIM,85
2
+ sumo/wrapper/__init__.py,sha256=wW7Bjl0btCgK9_exGVrfJsnFmTOybIdyeCudz0LueQM,235
3
+ sumo/wrapper/_auth_provider.py,sha256=PGLOnQmOC77caKfEZaSNLmETrN48PNBZtatiJnzrzOI,13618
4
+ sumo/wrapper/_blob_client.py,sha256=SyfyFZ1hHVWKU4lmgUqSjl5TaK_OJNQ__0wDETrp-ag,1623
5
+ sumo/wrapper/_decorators.py,sha256=3IEi6GXVkkDACHoo8dOeDoBtZh5TlJ6Tw0qlpOVHqLQ,806
6
+ sumo/wrapper/_logging.py,sha256=lnhjn6oQna33jZpzeZ7IeBya2uKNfrzXr_C3nw7txo0,965
7
+ sumo/wrapper/_retry_strategy.py,sha256=9PjOT0hOhLbCHrJu02thUkGdY39NpB2rqa_XUODoguw,2415
8
+ sumo/wrapper/_version.py,sha256=6mwHFYvRmuR_NDSMZ-tZ0hVgwAgYaLihPiAGrb850ks,413
9
+ sumo/wrapper/config.py,sha256=6t7qqjrrmd11m4VMlRryiMYw2JDU_R51305woAP1TAs,865
10
+ sumo/wrapper/login.py,sha256=IlENRNdSc2UPmGdrcxjziovMVYpV40qQSnAuDy5LKh4,2375
11
+ sumo/wrapper/sumo_client.py,sha256=Td2K1MkSP_ZDD2AIWcMmvFrwugtBWdDdme8h3VWSGJk,15707
12
+ sumo_wrapper_python-1.0.10.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
+ sumo_wrapper_python-1.0.10.dist-info/METADATA,sha256=PR41RcBbNUOYGFvT1KuhgTCqqRpdHVi2hJlOxxsINDI,14455
14
+ sumo_wrapper_python-1.0.10.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
15
+ sumo_wrapper_python-1.0.10.dist-info/entry_points.txt,sha256=V_vGky2C3He5vohJAxnBdvpt_fqfUDFj5irUm9HtoFc,55
16
+ sumo_wrapper_python-1.0.10.dist-info/top_level.txt,sha256=rLbKyH9rWgCj3PoLeR7fvC5X8vCaUc5LF8-Y_GBWZL0,5
17
+ sumo_wrapper_python-1.0.10.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (74.1.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,17 +0,0 @@
1
- sumo/__init__.py,sha256=ftS-xRPSH-vU7fIHlnZQaCTWbNvs4owJivNW65kzsIM,85
2
- sumo/wrapper/__init__.py,sha256=wW7Bjl0btCgK9_exGVrfJsnFmTOybIdyeCudz0LueQM,235
3
- sumo/wrapper/_auth_provider.py,sha256=jxhLxaImD0PMKZDy6YNcBOn-ogF7G5F5Fvh4oDwW40g,12023
4
- sumo/wrapper/_blob_client.py,sha256=y15_TThJ4ENFtEAF37mgju6Htc8D3ZyzcYCqsYQZo7Y,1495
5
- sumo/wrapper/_decorators.py,sha256=FSorPaTEBpq2N0qrqfdTt0jMToe_0PpOYlsL6vZSbIg,728
6
- sumo/wrapper/_logging.py,sha256=XgXcOAEoVw0aqij7V7EPTiP3ALqa8xNDgJgKyWOxeok,838
7
- sumo/wrapper/_retry_strategy.py,sha256=9PjOT0hOhLbCHrJu02thUkGdY39NpB2rqa_XUODoguw,2415
8
- sumo/wrapper/_version.py,sha256=vy0P95Si-KzUl6NsixjrWEHfr5xcmkA_oO_S3bQRmvE,411
9
- sumo/wrapper/config.py,sha256=6t7qqjrrmd11m4VMlRryiMYw2JDU_R51305woAP1TAs,865
10
- sumo/wrapper/login.py,sha256=HB_USKw6pR4j5EyNevaFVJGQjQ3t_8F__ZBaR29XLf4,1610
11
- sumo/wrapper/sumo_client.py,sha256=q5k6qP_oSgzmfFf1Bc2zw8L2KK85GFXg99Ro8GT3GZA,14741
12
- sumo_wrapper_python-1.0.8.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
- sumo_wrapper_python-1.0.8.dist-info/METADATA,sha256=CVL2k02sgRkHOJk1EvXcED6XHZbcxS75ZVrXejy9mB8,17407
14
- sumo_wrapper_python-1.0.8.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
15
- sumo_wrapper_python-1.0.8.dist-info/entry_points.txt,sha256=V_vGky2C3He5vohJAxnBdvpt_fqfUDFj5irUm9HtoFc,55
16
- sumo_wrapper_python-1.0.8.dist-info/top_level.txt,sha256=rLbKyH9rWgCj3PoLeR7fvC5X8vCaUc5LF8-Y_GBWZL0,5
17
- sumo_wrapper_python-1.0.8.dist-info/RECORD,,