sumo-wrapper-python 1.0.7__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
@@ -7,9 +9,13 @@ import json
7
9
  import jwt
8
10
  import time
9
11
  from azure.identity import ManagedIdentityCredential
12
+ import tenacity as tn
13
+ from ._retry_strategy import _log_retry_info, _return_last_value
10
14
 
11
15
  from msal_extensions.persistence import FilePersistence
12
16
  from msal_extensions.token_cache import PersistedTokenCache
17
+ import errno
18
+
13
19
 
14
20
  if not sys.platform.startswith("linux"):
15
21
  from msal_extensions import build_encrypted_persistence
@@ -19,13 +25,32 @@ def scope_for_resource(resource_id):
19
25
  return f"{resource_id}/.default"
20
26
 
21
27
 
28
+ def _maybe_nfs_exception(exception):
29
+ return isinstance(exception, OSError) and (
30
+ exception.errno in (errno.EAGAIN, errno.ESTALE)
31
+ )
32
+
33
+
22
34
  class AuthProvider:
23
35
  def __init__(self, resource_id):
24
36
  self._resource_id = resource_id
25
37
  self._scope = scope_for_resource(resource_id)
26
38
  self._app = None
39
+ self._login_timeout_minutes = 5
40
+ os.system("") # Ensure color init on all platforms (win10)
41
+
27
42
  return
28
43
 
44
+ @tn.retry(
45
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
46
+ stop=tn.stop_after_attempt(6),
47
+ wait=(
48
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
49
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
50
+ ),
51
+ retry_error_callback=_return_last_value,
52
+ before_sleep=_log_retry_info,
53
+ )
29
54
  def get_token(self):
30
55
  accounts = self._app.get_accounts()
31
56
  if len(accounts) == 0:
@@ -37,7 +62,11 @@ class AuthProvider:
37
62
  return result["access_token"]
38
63
 
39
64
  def get_authorization(self):
40
- return {"Authorization": "Bearer " + self.get_token()}
65
+ token = self.get_token()
66
+ if token is None:
67
+ return ""
68
+
69
+ return {"Authorization": "Bearer " + token}
41
70
 
42
71
  pass
43
72
 
@@ -77,6 +106,16 @@ def get_token_path(resource_id, suffix):
77
106
  )
78
107
 
79
108
 
109
+ @tn.retry(
110
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
111
+ stop=tn.stop_after_attempt(6),
112
+ wait=(
113
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
114
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
115
+ ),
116
+ retry_error_callback=_return_last_value,
117
+ before_sleep=_log_retry_info,
118
+ )
80
119
  def get_token_cache(resource_id, suffix):
81
120
  # https://github.com/AzureAD/microsoft-authentication-extensions-\
82
121
  # for-python
@@ -110,6 +149,16 @@ def get_token_cache(resource_id, suffix):
110
149
  return cache
111
150
 
112
151
 
152
+ @tn.retry(
153
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
154
+ stop=tn.stop_after_attempt(6),
155
+ wait=(
156
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
157
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
158
+ ),
159
+ retry_error_callback=_return_last_value,
160
+ before_sleep=_log_retry_info,
161
+ )
113
162
  def protect_token_cache(resource_id, suffix):
114
163
  token_path = get_token_path(resource_id, suffix)
115
164
 
@@ -143,27 +192,36 @@ class AuthProviderInteractive(AuthProvider):
143
192
  pass
144
193
  return
145
194
 
195
+ @tn.retry(
196
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
197
+ stop=tn.stop_after_attempt(6),
198
+ wait=(
199
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
200
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
201
+ ),
202
+ retry_error_callback=_return_last_value,
203
+ before_sleep=_log_retry_info,
204
+ )
146
205
  def login(self):
147
206
  scopes = [self._scope + " offline_access"]
148
- login_timeout_minutes = 7
149
- os.system("") # Ensure color init on all platforms (win10)
150
207
  print(
151
208
  "\n\n \033[31m NOTE! \033[0m"
152
209
  + " Please login to Equinor Azure to enable Sumo access: "
153
- + "we opened a login web-page for you in your browser."
210
+ + "we are opening a login web-page for you in your browser."
154
211
  + "\nYou should complete your login within "
155
- + str(login_timeout_minutes)
212
+ + str(self._login_timeout_minutes)
156
213
  + " minutes, "
157
214
  + "that is before "
158
215
  + str(
159
216
  (
160
- datetime.now() + timedelta(minutes=login_timeout_minutes)
217
+ datetime.now()
218
+ + timedelta(minutes=self._login_timeout_minutes)
161
219
  ).strftime("%H:%M:%S")
162
220
  )
163
221
  )
164
222
  try:
165
223
  result = self._app.acquire_token_interactive(
166
- scopes, timeout=(login_timeout_minutes * 60)
224
+ scopes, timeout=(self._login_timeout_minutes * 60)
167
225
  )
168
226
  if "error" in result:
169
227
  print(
@@ -180,7 +238,9 @@ class AuthProviderInteractive(AuthProvider):
180
238
  return
181
239
 
182
240
  protect_token_cache(self._resource_id, ".token")
183
- print("Equinor Azure login for Sumo access was successful")
241
+ print(
242
+ "Equinor Azure login for Sumo access was successful (interactive)"
243
+ )
184
244
  return
185
245
 
186
246
  pass
@@ -200,25 +260,58 @@ class AuthProviderDeviceCode(AuthProvider):
200
260
  pass
201
261
  return
202
262
 
263
+ @tn.retry(
264
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
265
+ stop=tn.stop_after_attempt(6),
266
+ wait=(
267
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
268
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
269
+ ),
270
+ retry_error_callback=_return_last_value,
271
+ before_sleep=_log_retry_info,
272
+ )
203
273
  def login(self):
204
- flow = self._app.initiate_device_flow([self._scope])
205
-
206
- if "error" in flow:
207
- raise ValueError(
208
- "Failed to create device flow. Err: %s"
209
- % 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
210
286
  )
211
287
 
212
- print(flow["message"])
213
- 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)
214
296
 
215
- if "error" in result:
216
- raise ValueError(
217
- "Failed to acquire token by device flow. Err: %s"
218
- % 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"
219
308
  )
309
+ return
220
310
 
221
311
  protect_token_cache(self._resource_id, ".token")
312
+ print(
313
+ "Equinor Azure login for Sumo access was successful (device-code)"
314
+ )
222
315
 
223
316
  return
224
317
 
@@ -232,6 +325,16 @@ class AuthProviderManaged(AuthProvider):
232
325
  self._scope = scope_for_resource(resource_id)
233
326
  return
234
327
 
328
+ @tn.retry(
329
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
330
+ stop=tn.stop_after_attempt(6),
331
+ wait=(
332
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
333
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
334
+ ),
335
+ retry_error_callback=_return_last_value,
336
+ before_sleep=_log_retry_info,
337
+ )
235
338
  def get_token(self):
236
339
  return self._app.get_token(self._scope).token
237
340
 
@@ -239,6 +342,16 @@ class AuthProviderManaged(AuthProvider):
239
342
 
240
343
 
241
344
  class AuthProviderSumoToken(AuthProvider):
345
+ @tn.retry(
346
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
347
+ stop=tn.stop_after_attempt(6),
348
+ wait=(
349
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
350
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
351
+ ),
352
+ retry_error_callback=_return_last_value,
353
+ before_sleep=_log_retry_info,
354
+ )
242
355
  def __init__(self, resource_id):
243
356
  protect_token_cache(resource_id, ".sharedkey")
244
357
  token_path = get_token_path(resource_id, ".sharedkey")
@@ -253,6 +366,16 @@ class AuthProviderSumoToken(AuthProvider):
253
366
  return {"X-SUMO-Token": self._token}
254
367
 
255
368
 
369
+ @tn.retry(
370
+ retry=tn.retry_if_exception(_maybe_nfs_exception),
371
+ stop=tn.stop_after_attempt(6),
372
+ wait=(
373
+ tn.wait_exponential(multiplier=0.5, exp_base=2)
374
+ + tn.wait_random_exponential(multiplier=0.5, exp_base=2)
375
+ ),
376
+ retry_error_callback=_return_last_value,
377
+ before_sleep=_log_retry_info,
378
+ )
256
379
  def get_auth_provider(
257
380
  client_id,
258
381
  authority,
@@ -294,4 +417,11 @@ def get_auth_provider(
294
417
  ):
295
418
  return AuthProviderManaged(resource_id)
296
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
297
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
@@ -17,7 +17,10 @@ def _log_retry_info(retry_state):
17
17
 
18
18
  # Define the conditions for retrying based on exception types
19
19
  def _is_retryable_exception(exception):
20
- return isinstance(exception, (httpx.TimeoutException, httpx.ConnectError))
20
+ return isinstance(
21
+ exception,
22
+ (httpx.TimeoutException, httpx.NetworkError, httpx.ProxyError),
23
+ )
21
24
 
22
25
 
23
26
  # Define the conditions for retrying based on HTTP status codes
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.7'
16
- __version_tuple__ = version_tuple = (1, 0, 7)
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.7
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=rfTgOwmfOyg7DJwUoejCINfrzhi1lWIx-UH0Pir-8XM,9005
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=sata8h2tXWQIm9GQYWJCmzNBLm9Jy_odGYkaOH27j3o,2374
8
- sumo/wrapper/_version.py,sha256=B_2jTRPLk-a7dhEz8Rto7VRGwxfpXrqO4mYrudjn7bw,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.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
- sumo_wrapper_python-1.0.7.dist-info/METADATA,sha256=7qpWbfF_muUNT0kIfYzzK_YUeqZOraqlpWWS1eAQVKQ,17407
14
- sumo_wrapper_python-1.0.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
15
- sumo_wrapper_python-1.0.7.dist-info/entry_points.txt,sha256=V_vGky2C3He5vohJAxnBdvpt_fqfUDFj5irUm9HtoFc,55
16
- sumo_wrapper_python-1.0.7.dist-info/top_level.txt,sha256=rLbKyH9rWgCj3PoLeR7fvC5X8vCaUc5LF8-Y_GBWZL0,5
17
- sumo_wrapper_python-1.0.7.dist-info/RECORD,,