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

@@ -1,3 +1,5 @@
1
+ import platform
2
+ from pathlib import Path
1
3
  import msal
2
4
  import os
3
5
  from datetime import datetime, timedelta
@@ -34,6 +36,9 @@ class AuthProvider:
34
36
  self._resource_id = resource_id
35
37
  self._scope = scope_for_resource(resource_id)
36
38
  self._app = None
39
+ self._login_timeout_minutes = 5
40
+ os.system("") # Ensure color init on all platforms (win10)
41
+
37
42
  return
38
43
 
39
44
  @tn.retry(
@@ -57,7 +62,11 @@ class AuthProvider:
57
62
  return result["access_token"]
58
63
 
59
64
  def get_authorization(self):
60
- return {"Authorization": "Bearer " + self.get_token()}
65
+ token = self.get_token()
66
+ if token is None:
67
+ return ""
68
+
69
+ return {"Authorization": "Bearer " + token}
61
70
 
62
71
  pass
63
72
 
@@ -195,25 +204,24 @@ class AuthProviderInteractive(AuthProvider):
195
204
  )
196
205
  def login(self):
197
206
  scopes = [self._scope + " offline_access"]
198
- login_timeout_minutes = 7
199
- os.system("") # Ensure color init on all platforms (win10)
200
207
  print(
201
208
  "\n\n \033[31m NOTE! \033[0m"
202
209
  + " Please login to Equinor Azure to enable Sumo access: "
203
- + "we opened a login web-page for you in your browser."
210
+ + "we are opening a login web-page for you in your browser."
204
211
  + "\nYou should complete your login within "
205
- + str(login_timeout_minutes)
212
+ + str(self._login_timeout_minutes)
206
213
  + " minutes, "
207
214
  + "that is before "
208
215
  + str(
209
216
  (
210
- datetime.now() + timedelta(minutes=login_timeout_minutes)
217
+ datetime.now()
218
+ + timedelta(minutes=self._login_timeout_minutes)
211
219
  ).strftime("%H:%M:%S")
212
220
  )
213
221
  )
214
222
  try:
215
223
  result = self._app.acquire_token_interactive(
216
- scopes, timeout=(login_timeout_minutes * 60)
224
+ scopes, timeout=(self._login_timeout_minutes * 60)
217
225
  )
218
226
  if "error" in result:
219
227
  print(
@@ -230,7 +238,9 @@ class AuthProviderInteractive(AuthProvider):
230
238
  return
231
239
 
232
240
  protect_token_cache(self._resource_id, ".token")
233
- print("Equinor Azure login for Sumo access was successful")
241
+ print(
242
+ "Equinor Azure login for Sumo access was successful (interactive)"
243
+ )
234
244
  return
235
245
 
236
246
  pass
@@ -261,24 +271,47 @@ class AuthProviderDeviceCode(AuthProvider):
261
271
  before_sleep=_log_retry_info,
262
272
  )
263
273
  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)
274
+ try:
275
+ scopes = [self._scope + " offline_access"]
276
+ flow = self._app.initiate_device_flow(scopes)
277
+ if "error" in flow:
278
+ print(
279
+ "\n\n \033[31m"
280
+ + "Failed to initiate device-code login. Err: %s"
281
+ + "\033[0m" % json.dumps(flow, indent=4)
282
+ )
283
+ return
284
+ flow["expires_at"] = (
285
+ int(time.time()) + self._login_timeout_minutes * 60
270
286
  )
271
287
 
272
- print(flow["message"])
273
- result = self._app.acquire_token_by_device_flow(flow)
288
+ print(
289
+ "\033[31m"
290
+ + " NOTE! Please login to Equinor Azure to enable Sumo access:"
291
+ + flow["message"]
292
+ + " \033[0m"
293
+ + "\nYou should complete your login within a few minutes"
294
+ )
295
+ result = self._app.acquire_token_by_device_flow(flow)
274
296
 
275
- if "error" in result:
276
- raise ValueError(
277
- "Failed to acquire token by device flow. Err: %s"
278
- % json.dumps(result, indent=4)
297
+ if "error" in result:
298
+ print(
299
+ "\n\n \033[31m Error during Equinor Azure login "
300
+ "for Sumo access: \033[0m"
301
+ )
302
+ print("Err: ", json.dumps(result, indent=4))
303
+ return
304
+ except Exception:
305
+ print(
306
+ "\n\n \033[31m Failed Equinor Azure login for Sumo access, "
307
+ "one possible reason is timeout \033[0m"
279
308
  )
309
+ return
280
310
 
281
311
  protect_token_cache(self._resource_id, ".token")
312
+ print(
313
+ "Equinor Azure login for Sumo access was successful (device-code)"
314
+ )
282
315
 
283
316
  return
284
317
 
@@ -384,4 +417,11 @@ def get_auth_provider(
384
417
  ):
385
418
  return AuthProviderManaged(resource_id)
386
419
  # ELSE
420
+ lockfile_path = Path.home() / ".config/chromium/SingletonLock"
421
+ if Path(lockfile_path).is_symlink() and not str(
422
+ Path(lockfile_path).resolve()
423
+ ).__contains__(platform.node()):
424
+ # https://github.com/equinor/sumo-wrapper-python/issues/193
425
+ return AuthProviderDeviceCode(client_id, authority, resource_id)
426
+ # ELSE
387
427
  return AuthProviderInteractive(client_id, authority, resource_id)
@@ -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.9'
16
+ __version_tuple__ = version_tuple = (1, 0, 9)
sumo/wrapper/login.py CHANGED
@@ -74,6 +74,11 @@ def main():
74
74
  if args.print_token:
75
75
  print(f"TOKEN: {token}")
76
76
 
77
+ if token is not None:
78
+ print("Successfully logged in to Sumo environment: " + env)
79
+ else:
80
+ print("Failed login to Sumo environment: " + env)
81
+
77
82
 
78
83
  if __name__ == "__main__":
79
84
  main()
@@ -84,6 +84,27 @@ class SumoClient:
84
84
  access_token=access_token,
85
85
  devicecode=devicecode,
86
86
  )
87
+ if (
88
+ self.auth.get_token() is None
89
+ and refresh_token is None
90
+ and access_token is None
91
+ ):
92
+ print("\n \033[31m !!! Falling back to device-code login:\033[0m")
93
+ self.auth = get_auth_provider(
94
+ client_id=APP_REGISTRATION[env]["CLIENT_ID"],
95
+ authority=f"{AUTHORITY_HOST_URI}/{TENANT_ID}",
96
+ resource_id=APP_REGISTRATION[env]["RESOURCE_ID"],
97
+ interactive=False,
98
+ refresh_token=None,
99
+ access_token=None,
100
+ devicecode=True,
101
+ )
102
+ if self.auth.get_token() is None:
103
+ print(
104
+ "\n\n \033[31m "
105
+ + "NOTE! Login failed/timed out. Giving up."
106
+ + "\033[0m"
107
+ )
87
108
 
88
109
  if env == "localhost":
89
110
  self.base_url = "http://localhost:8084/api/v1"
@@ -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.9
4
4
  Summary: Python wrapper for the Sumo API
5
5
  Author: Equinor
6
6
  License: Apache License
@@ -220,165 +220,20 @@ Requires-Dist: azure-identity >=1.13.0
220
220
  Provides-Extra: docs
221
221
  Requires-Dist: sphinx ; extra == 'docs'
222
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
227
  Requires-Dist: pytest ; extra == 'test'
225
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)
230
233
 
231
- Want to contribute? Read our [contributing](./CONTRIBUTING.md) guidelines
234
+ ## Documentation and guidelines
235
+ [sumo-wrapper-python documentation](https://sumo-wrapper-python.readthedocs.io/en/latest/)
232
236
 
233
- ## Install:
237
+ ## Contribute
238
+ [Contribution guidelines](./CONTRIBUTING.md)
234
239
 
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=HieseMcqT9lMBqgyzwL5Pr6CCyk-eFjKyYhJQx8UKRw,13494
4
+ sumo/wrapper/_blob_client.py,sha256=y15_TThJ4ENFtEAF37mgju6Htc8D3ZyzcYCqsYQZo7Y,1495
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=29gfaFnVGHlO4YwJu6vgvQjCQwPqDmuCvkEOYMZyflo,411
9
+ sumo/wrapper/config.py,sha256=6t7qqjrrmd11m4VMlRryiMYw2JDU_R51305woAP1TAs,865
10
+ sumo/wrapper/login.py,sha256=HY3CLu3u0RZtgSbitpdyk2T0P-M9ttZ6LRDsGO4BwRM,1773
11
+ sumo/wrapper/sumo_client.py,sha256=Mjco-nFF28GgmjXgDmGmXmFXr7LJI50M54zL0zt3CJs,15545
12
+ sumo_wrapper_python-1.0.9.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
+ sumo_wrapper_python-1.0.9.dist-info/METADATA,sha256=pmewZzDIe8sALypH6d9EVNxBr5qiuoCFlW5MfBjytSg,14267
14
+ sumo_wrapper_python-1.0.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
+ sumo_wrapper_python-1.0.9.dist-info/entry_points.txt,sha256=V_vGky2C3He5vohJAxnBdvpt_fqfUDFj5irUm9HtoFc,55
16
+ sumo_wrapper_python-1.0.9.dist-info/top_level.txt,sha256=rLbKyH9rWgCj3PoLeR7fvC5X8vCaUc5LF8-Y_GBWZL0,5
17
+ sumo_wrapper_python-1.0.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
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,,