sumo-wrapper-python 1.8.1__tar.gz → 1.10.0__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.
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.github/workflows/build_docs.yaml +3 -3
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.github/workflows/linting.yml +3 -3
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.github/workflows/publish_release.yml +3 -3
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.github/workflows/pytest.yml +4 -4
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/PKG-INFO +1 -1
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/pyproject.toml +1 -1
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/__init__.py +1 -1
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/_auth_provider.py +34 -53
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/_blob_client.py +0 -1
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/_logging.py +3 -8
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/_retry_strategy.py +0 -2
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/_version.py +3 -3
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/sumo_client.py +44 -43
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo_wrapper_python.egg-info/PKG-INFO +1 -1
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo_wrapper_python.egg-info/scm_file_list.json +22 -22
- sumo_wrapper_python-1.10.0/src/sumo_wrapper_python.egg-info/scm_version.json +8 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/tests/test_sumo_thin_client.py +7 -8
- sumo_wrapper_python-1.8.1/src/sumo_wrapper_python.egg-info/scm_version.json +0 -8
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.github/pull_request_template.md +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.gitignore +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.pre-commit-config.yaml +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.readthedocs.yaml +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/CONTRIBUTING.md +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/LICENSE +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/README.md +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/SECURITY.md +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/Makefile +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/_static/equinor-logo.png +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/_static/equinor-logo2.jpg +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/_static/equinor_logo.jpg +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/_static/equinor_logo_only.jpg +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/_templates/layout.html +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/api.rst +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/conf.py +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/index.rst +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/make.bat +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/docs/sumo-wrapper-python.rst +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/setup.cfg +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/__init__.py +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/_decorators.py +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/login.py +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo_wrapper_python.egg-info/SOURCES.txt +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo_wrapper_python.egg-info/dependency_links.txt +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo_wrapper_python.egg-info/entry_points.txt +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo_wrapper_python.egg-info/requires.txt +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo_wrapper_python.egg-info/top_level.txt +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/tests/conftest.py +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/tests/testdata/case.yml +0 -0
- {sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/tests/testdata/surface.yml +0 -0
|
@@ -17,14 +17,14 @@ jobs:
|
|
|
17
17
|
runs-on: ${{ matrix.os }}
|
|
18
18
|
strategy:
|
|
19
19
|
matrix:
|
|
20
|
-
python-version: ["3.
|
|
20
|
+
python-version: ["3.12"]
|
|
21
21
|
os: [ubuntu-latest]
|
|
22
22
|
|
|
23
23
|
steps:
|
|
24
|
-
- uses: actions/checkout@
|
|
24
|
+
- uses: actions/checkout@v7.0.0
|
|
25
25
|
|
|
26
26
|
- name: Set up Python
|
|
27
|
-
uses: actions/setup-python@
|
|
27
|
+
uses: actions/setup-python@v6.3.0
|
|
28
28
|
with:
|
|
29
29
|
python-version: ${{ matrix.python-version }}
|
|
30
30
|
|
|
@@ -12,11 +12,11 @@ jobs:
|
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
15
|
+
- uses: actions/checkout@v7.0.0
|
|
16
16
|
|
|
17
|
-
- uses: actions/setup-python@
|
|
17
|
+
- uses: actions/setup-python@v6.3.0
|
|
18
18
|
with:
|
|
19
|
-
python-version: '3.
|
|
19
|
+
python-version: '3.12'
|
|
20
20
|
cache: 'pip'
|
|
21
21
|
|
|
22
22
|
- name: Installing dependencies
|
{sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/.github/workflows/publish_release.yml
RENAMED
|
@@ -13,11 +13,11 @@ jobs:
|
|
|
13
13
|
environment: production
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@v7.0.0
|
|
17
17
|
|
|
18
|
-
- uses: actions/setup-python@
|
|
18
|
+
- uses: actions/setup-python@v6.3.0
|
|
19
19
|
with:
|
|
20
|
-
python-version: "3.
|
|
20
|
+
python-version: "3.12"
|
|
21
21
|
|
|
22
22
|
- name: build
|
|
23
23
|
run: |
|
|
@@ -14,24 +14,24 @@ jobs:
|
|
|
14
14
|
runs-on: ${{ matrix.os }}
|
|
15
15
|
strategy:
|
|
16
16
|
matrix:
|
|
17
|
-
python-version: ["3.
|
|
17
|
+
python-version: ["3.12"]
|
|
18
18
|
os: [ubuntu-latest, windows-latest, macos-15]
|
|
19
19
|
permissions:
|
|
20
20
|
contents: read
|
|
21
21
|
id-token: write
|
|
22
22
|
|
|
23
23
|
steps:
|
|
24
|
-
- uses: actions/checkout@
|
|
24
|
+
- uses: actions/checkout@v7.0.0
|
|
25
25
|
|
|
26
26
|
- name: Azure Login
|
|
27
|
-
uses: Azure/login@
|
|
27
|
+
uses: Azure/login@v3.0.0
|
|
28
28
|
with:
|
|
29
29
|
client-id: f96c150d-cacf-4257-9cc9-54b2c68ec4ce
|
|
30
30
|
tenant-id: 3aa4a235-b6e2-48d5-9195-7fcf05b459b0
|
|
31
31
|
subscription-id: 87897772-fb27-495f-ae40-486a2df57baa
|
|
32
32
|
|
|
33
33
|
- name: Set up Python
|
|
34
|
-
uses: actions/setup-python@
|
|
34
|
+
uses: actions/setup-python@v6.3.0
|
|
35
35
|
with:
|
|
36
36
|
python-version: ${{ matrix.python-version }}
|
|
37
37
|
|
|
@@ -5,11 +5,11 @@ import platform
|
|
|
5
5
|
import stat
|
|
6
6
|
import sys
|
|
7
7
|
import time
|
|
8
|
-
from datetime import datetime, timedelta
|
|
8
|
+
from datetime import UTC, datetime, timedelta
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Dict
|
|
11
10
|
from urllib.parse import parse_qs
|
|
12
11
|
|
|
12
|
+
import httpx
|
|
13
13
|
import jwt
|
|
14
14
|
import msal
|
|
15
15
|
import tenacity as tn
|
|
@@ -55,8 +55,6 @@ class AuthProvider:
|
|
|
55
55
|
self._login_timeout_minutes = 5
|
|
56
56
|
os.system("") # Ensure color init on all platforms (win10)
|
|
57
57
|
|
|
58
|
-
return
|
|
59
|
-
|
|
60
58
|
@tn.retry(
|
|
61
59
|
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
62
60
|
stop=tn.stop_after_attempt(6),
|
|
@@ -77,7 +75,7 @@ class AuthProvider:
|
|
|
77
75
|
# ELSE
|
|
78
76
|
return result["access_token"]
|
|
79
77
|
|
|
80
|
-
def get_authorization(self) ->
|
|
78
|
+
def get_authorization(self) -> dict:
|
|
81
79
|
token = self.get_token()
|
|
82
80
|
if token is None:
|
|
83
81
|
return {}
|
|
@@ -92,6 +90,16 @@ class AuthProvider:
|
|
|
92
90
|
) as f:
|
|
93
91
|
f.write(token)
|
|
94
92
|
protect_token_cache(self._resource_id, ".sharedkey", case_uuid)
|
|
93
|
+
|
|
94
|
+
def _store_fallback_auth(self, sumo_client):
|
|
95
|
+
sync_client = sumo_client._client
|
|
96
|
+
if sync_client is None:
|
|
97
|
+
sumo_client._client = httpx.Client()
|
|
98
|
+
token = sumo_client.get("/createfallbackauth").text
|
|
99
|
+
self.store_shared_access_key_for_case("fallback", token)
|
|
100
|
+
sumo_client._client = sync_client
|
|
101
|
+
|
|
102
|
+
def store_fallback_auth(self, sumo_client):
|
|
95
103
|
return
|
|
96
104
|
|
|
97
105
|
def has_case_token(self, case_uuid):
|
|
@@ -102,15 +110,11 @@ class AuthProvider:
|
|
|
102
110
|
def delete_token(self):
|
|
103
111
|
return False
|
|
104
112
|
|
|
105
|
-
pass
|
|
106
|
-
|
|
107
113
|
|
|
108
114
|
class AuthProviderNone(AuthProvider):
|
|
109
115
|
def get_token(self):
|
|
110
116
|
raise Exception("No valid authorization provider found.")
|
|
111
117
|
|
|
112
|
-
pass
|
|
113
|
-
|
|
114
118
|
|
|
115
119
|
class AuthProviderSilent(AuthProvider):
|
|
116
120
|
def __init__(self, client_id, authority, resource_id):
|
|
@@ -123,6 +127,9 @@ class AuthProviderSilent(AuthProvider):
|
|
|
123
127
|
|
|
124
128
|
self._scope = scope_for_resource(resource_id)
|
|
125
129
|
|
|
130
|
+
def store_fallback_auth(self, sumo_client):
|
|
131
|
+
self._store_fallback_auth(sumo_client)
|
|
132
|
+
|
|
126
133
|
|
|
127
134
|
class AuthProviderAccessToken(AuthProvider):
|
|
128
135
|
def __init__(self, access_token):
|
|
@@ -130,7 +137,6 @@ class AuthProviderAccessToken(AuthProvider):
|
|
|
130
137
|
payload = jwt.decode(access_token, options={"verify_signature": False})
|
|
131
138
|
self._expires = payload["exp"]
|
|
132
139
|
self._resource_id = payload["aud"]
|
|
133
|
-
return
|
|
134
140
|
|
|
135
141
|
def get_token(self):
|
|
136
142
|
if time.time() >= self._expires:
|
|
@@ -138,8 +144,6 @@ class AuthProviderAccessToken(AuthProvider):
|
|
|
138
144
|
# ELSE
|
|
139
145
|
return self._access_token
|
|
140
146
|
|
|
141
|
-
pass
|
|
142
|
-
|
|
143
147
|
|
|
144
148
|
class AuthProviderRefreshToken(AuthProvider):
|
|
145
149
|
def __init__(self, refresh_token, client_id, authority, resource_id):
|
|
@@ -149,9 +153,6 @@ class AuthProviderRefreshToken(AuthProvider):
|
|
|
149
153
|
)
|
|
150
154
|
self._scope = scope_for_resource(resource_id)
|
|
151
155
|
self._app.acquire_token_by_refresh_token(refresh_token, [self._scope])
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
pass
|
|
155
156
|
|
|
156
157
|
|
|
157
158
|
@tn.retry(
|
|
@@ -186,14 +187,10 @@ def get_token_cache(resource_id, suffix):
|
|
|
186
187
|
token = FilePersistence(token_path).load()
|
|
187
188
|
with open(token_path, "w") as f:
|
|
188
189
|
f.truncate()
|
|
189
|
-
pass
|
|
190
190
|
encrypted_persistence.save(token)
|
|
191
|
-
pass
|
|
192
|
-
pass
|
|
193
191
|
|
|
194
192
|
persistence = build_encrypted_persistence(token_path)
|
|
195
193
|
cache = PersistedTokenCache(persistence)
|
|
196
|
-
pass
|
|
197
194
|
return cache
|
|
198
195
|
|
|
199
196
|
|
|
@@ -218,10 +215,7 @@ def protect_token_cache(resource_id, suffix, case_uuid=None):
|
|
|
218
215
|
foldermode = stat.filemode(os.stat(folder).st_mode)
|
|
219
216
|
if foldermode != "drwx------":
|
|
220
217
|
os.chmod(os.path.dirname(token_path), 0o700)
|
|
221
|
-
pass
|
|
222
|
-
pass
|
|
223
218
|
return
|
|
224
|
-
pass
|
|
225
219
|
|
|
226
220
|
|
|
227
221
|
class AuthProviderInteractive(AuthProvider):
|
|
@@ -237,8 +231,6 @@ class AuthProviderInteractive(AuthProvider):
|
|
|
237
231
|
|
|
238
232
|
if self.get_token() is None:
|
|
239
233
|
self.login()
|
|
240
|
-
pass
|
|
241
|
-
return
|
|
242
234
|
|
|
243
235
|
@tn.retry(
|
|
244
236
|
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
@@ -262,7 +254,7 @@ class AuthProviderInteractive(AuthProvider):
|
|
|
262
254
|
+ "that is before "
|
|
263
255
|
+ str(
|
|
264
256
|
(
|
|
265
|
-
datetime.now()
|
|
257
|
+
datetime.now().astimezone()
|
|
266
258
|
+ timedelta(minutes=self._login_timeout_minutes)
|
|
267
259
|
).strftime("%H:%M:%S")
|
|
268
260
|
)
|
|
@@ -291,7 +283,8 @@ class AuthProviderInteractive(AuthProvider):
|
|
|
291
283
|
)
|
|
292
284
|
return
|
|
293
285
|
|
|
294
|
-
|
|
286
|
+
def store_fallback_auth(self, sumo_client):
|
|
287
|
+
self._store_fallback_auth(sumo_client)
|
|
295
288
|
|
|
296
289
|
|
|
297
290
|
class AuthProviderDeviceCode(AuthProvider):
|
|
@@ -305,8 +298,6 @@ class AuthProviderDeviceCode(AuthProvider):
|
|
|
305
298
|
self._scope = scope_for_resource(resource_id)
|
|
306
299
|
if self.get_token() is None:
|
|
307
300
|
self.login()
|
|
308
|
-
pass
|
|
309
|
-
return
|
|
310
301
|
|
|
311
302
|
@tn.retry(
|
|
312
303
|
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
@@ -324,9 +315,10 @@ class AuthProviderDeviceCode(AuthProvider):
|
|
|
324
315
|
flow = self._app.initiate_device_flow(scopes)
|
|
325
316
|
if "error" in flow:
|
|
326
317
|
print(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
318
|
+
(
|
|
319
|
+
"\n\n \033[31m"
|
|
320
|
+
+ "Failed to initiate device-code login. Err: {}\033[0m"
|
|
321
|
+
).format(json.dumps(flow, indent=4))
|
|
330
322
|
)
|
|
331
323
|
return
|
|
332
324
|
flow["expires_at"] = (
|
|
@@ -363,15 +355,12 @@ class AuthProviderDeviceCode(AuthProvider):
|
|
|
363
355
|
|
|
364
356
|
return
|
|
365
357
|
|
|
366
|
-
pass
|
|
367
|
-
|
|
368
358
|
|
|
369
359
|
class AuthProviderManaged(AuthProvider):
|
|
370
360
|
def __init__(self, resource_id):
|
|
371
361
|
super().__init__(resource_id)
|
|
372
362
|
self._app = ManagedIdentityCredential()
|
|
373
363
|
self._scope = scope_for_resource(resource_id)
|
|
374
|
-
return
|
|
375
364
|
|
|
376
365
|
@tn.retry(
|
|
377
366
|
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
@@ -386,8 +375,6 @@ class AuthProviderManaged(AuthProvider):
|
|
|
386
375
|
def get_token(self):
|
|
387
376
|
return self._app.get_token(self._scope).token
|
|
388
377
|
|
|
389
|
-
pass
|
|
390
|
-
|
|
391
378
|
|
|
392
379
|
class AuthProviderSumoToken(AuthProvider):
|
|
393
380
|
@tn.retry(
|
|
@@ -407,8 +394,6 @@ class AuthProviderSumoToken(AuthProvider):
|
|
|
407
394
|
with open(self.token_path, "r") as f:
|
|
408
395
|
self._token = f.readline().strip()
|
|
409
396
|
|
|
410
|
-
return
|
|
411
|
-
|
|
412
397
|
def get_token(self):
|
|
413
398
|
return self._token
|
|
414
399
|
|
|
@@ -460,7 +445,6 @@ def get_auth_provider(
|
|
|
460
445
|
token = auth_silent.get_token()
|
|
461
446
|
if token is not None:
|
|
462
447
|
return auth_silent
|
|
463
|
-
pass
|
|
464
448
|
# ELSE
|
|
465
449
|
if all(
|
|
466
450
|
os.getenv(x)
|
|
@@ -484,14 +468,16 @@ def get_auth_provider(
|
|
|
484
468
|
"\n\n\033[1mDetected chromium lockfile for different node; using firefox to authenticate.\033[0m"
|
|
485
469
|
)
|
|
486
470
|
os.environ["BROWSER"] = "firefox"
|
|
487
|
-
pass
|
|
488
471
|
|
|
489
|
-
|
|
472
|
+
auth_interactive = AuthProviderInteractive(
|
|
473
|
+
client_id, authority, resource_id
|
|
474
|
+
)
|
|
475
|
+
token = auth_interactive.get_token()
|
|
476
|
+
if token is not None:
|
|
477
|
+
return auth_interactive
|
|
490
478
|
# ELSE
|
|
491
|
-
if
|
|
492
|
-
|
|
493
|
-
# under Equinor compliant device policy
|
|
494
|
-
return AuthProviderDeviceCode(client_id, authority, resource_id)
|
|
479
|
+
if os.path.exists(get_token_path(resource_id, ".sharedkey", "fallback")):
|
|
480
|
+
return AuthProviderSumoToken(resource_id, "fallback")
|
|
495
481
|
# ELSE
|
|
496
482
|
return AuthProviderNone(resource_id)
|
|
497
483
|
|
|
@@ -510,15 +496,10 @@ def cleanup_shared_keys():
|
|
|
510
496
|
token = file.read()
|
|
511
497
|
pq = parse_qs(token)
|
|
512
498
|
se = pq["se"][0]
|
|
513
|
-
end = datetime.
|
|
514
|
-
now = datetime.now(
|
|
499
|
+
end = datetime.fromisoformat(se)
|
|
500
|
+
now = datetime.now(UTC)
|
|
515
501
|
if now.timestamp() > end.timestamp():
|
|
516
502
|
os.unlink(ff)
|
|
517
|
-
|
|
518
|
-
pass
|
|
519
|
-
pass
|
|
520
|
-
except Exception:
|
|
503
|
+
except Exception: # noqa: S110
|
|
521
504
|
pass
|
|
522
|
-
pass
|
|
523
|
-
pass
|
|
524
505
|
return
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from datetime import
|
|
2
|
+
from datetime import UTC, datetime
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class LogHandlerSumo(logging.Handler):
|
|
6
6
|
def __init__(self, sumo_client):
|
|
7
7
|
logging.Handler.__init__(self)
|
|
8
8
|
self._sumoClient = sumo_client
|
|
9
|
-
return
|
|
10
9
|
|
|
11
10
|
def emit(self, record):
|
|
12
11
|
try:
|
|
13
12
|
dt = (
|
|
14
|
-
datetime.now(
|
|
13
|
+
datetime.now(UTC)
|
|
15
14
|
.replace(microsecond=0, tzinfo=None)
|
|
16
15
|
.isoformat()
|
|
17
16
|
+ "Z"
|
|
@@ -32,10 +31,6 @@ class LogHandlerSumo(logging.Handler):
|
|
|
32
31
|
json["details"] = record.__dict__.get("details")
|
|
33
32
|
|
|
34
33
|
self._sumoClient.post("/message-log/new", json=json)
|
|
35
|
-
except Exception:
|
|
34
|
+
except Exception: # noqa: S110
|
|
36
35
|
# Never fail on logging
|
|
37
36
|
pass
|
|
38
|
-
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
pass
|
{sumo_wrapper_python-1.8.1 → sumo_wrapper_python-1.10.0}/src/sumo/wrapper/_retry_strategy.py
RENAMED
|
@@ -12,7 +12,6 @@ def _log_retry_info(retry_state):
|
|
|
12
12
|
f"Attempts: {retry_state.attempt_number}; "
|
|
13
13
|
f"Elapsed: {retry_state.seconds_since_start}"
|
|
14
14
|
)
|
|
15
|
-
return
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
# Define the conditions for retrying based on exception types
|
|
@@ -49,7 +48,6 @@ class RetryStrategy:
|
|
|
49
48
|
self._multiplier = multiplier
|
|
50
49
|
self._exp_base = exp_base
|
|
51
50
|
self._before_sleep = before_sleep
|
|
52
|
-
return
|
|
53
51
|
|
|
54
52
|
def make_retryer(self) -> tn.Retrying:
|
|
55
53
|
return tn.Retrying(
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.
|
|
22
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.10.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 10, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g35b2b9609'
|
|
@@ -4,7 +4,6 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
6
|
import time
|
|
7
|
-
from typing import Dict, Optional, Tuple
|
|
8
7
|
|
|
9
8
|
import httpx
|
|
10
9
|
import jwt
|
|
@@ -38,16 +37,16 @@ class SumoClient:
|
|
|
38
37
|
def __init__(
|
|
39
38
|
self,
|
|
40
39
|
env: str = "prod",
|
|
41
|
-
token:
|
|
40
|
+
token: str | None = None,
|
|
42
41
|
interactive: bool = True,
|
|
43
42
|
devicecode: bool = False,
|
|
44
43
|
verbosity: str = "CRITICAL",
|
|
45
|
-
retry_strategy=
|
|
44
|
+
retry_strategy=None,
|
|
46
45
|
timeout=DEFAULT_TIMEOUT,
|
|
47
46
|
case_uuid=None,
|
|
48
47
|
http_client=None,
|
|
49
48
|
async_http_client=None,
|
|
50
|
-
client_id:
|
|
49
|
+
client_id: str | None = None,
|
|
51
50
|
):
|
|
52
51
|
"""Initialize a new Sumo object
|
|
53
52
|
|
|
@@ -71,10 +70,17 @@ class SumoClient:
|
|
|
71
70
|
AZURE_CLIENT_ID from environment variables or the config. Defaults to None.
|
|
72
71
|
"""
|
|
73
72
|
|
|
73
|
+
if retry_strategy is None:
|
|
74
|
+
retry_strategy = RetryStrategy()
|
|
74
75
|
logger.setLevel(verbosity)
|
|
75
76
|
global well_known
|
|
76
77
|
if well_known is None:
|
|
77
|
-
|
|
78
|
+
|
|
79
|
+
def _get():
|
|
80
|
+
return httpx.get(WELL_KNOWN, timeout=timeout)
|
|
81
|
+
|
|
82
|
+
retryer = retry_strategy.make_retryer()
|
|
83
|
+
well_known = retryer(_get).json()
|
|
78
84
|
if env not in well_known["envs"]:
|
|
79
85
|
raise ValueError(f"Invalid environment: {env}")
|
|
80
86
|
|
|
@@ -128,8 +134,6 @@ class SumoClient:
|
|
|
128
134
|
"treating it as a refresh token"
|
|
129
135
|
)
|
|
130
136
|
refresh_token = token
|
|
131
|
-
pass
|
|
132
|
-
pass
|
|
133
137
|
|
|
134
138
|
cleanup_shared_keys()
|
|
135
139
|
self.auth = get_auth_provider(
|
|
@@ -144,9 +148,12 @@ class SumoClient:
|
|
|
144
148
|
)
|
|
145
149
|
|
|
146
150
|
self.base_url = base_url
|
|
147
|
-
|
|
151
|
+
|
|
152
|
+
with contextlib.suppress(Exception):
|
|
153
|
+
self.auth.store_fallback_auth(self)
|
|
148
154
|
|
|
149
155
|
def __enter__(self):
|
|
156
|
+
|
|
150
157
|
return self
|
|
151
158
|
|
|
152
159
|
def __exit__(self, *_):
|
|
@@ -165,19 +172,16 @@ class SumoClient:
|
|
|
165
172
|
def __del__(self):
|
|
166
173
|
if self._client is not None and not self._borrowed_client:
|
|
167
174
|
self._client.close()
|
|
168
|
-
pass
|
|
169
175
|
if self._async_client is not None and not self._borrowed_async_client:
|
|
170
176
|
|
|
171
177
|
async def closeit(client):
|
|
172
178
|
await client.aclose()
|
|
173
|
-
return
|
|
174
179
|
|
|
175
180
|
try:
|
|
176
181
|
loop = asyncio.get_running_loop()
|
|
177
182
|
loop.create_task(closeit(self._async_client))
|
|
178
183
|
except RuntimeError:
|
|
179
184
|
pass
|
|
180
|
-
pass
|
|
181
185
|
|
|
182
186
|
def authenticate(self):
|
|
183
187
|
if self.auth is None:
|
|
@@ -222,8 +226,8 @@ class SumoClient:
|
|
|
222
226
|
def get(
|
|
223
227
|
self,
|
|
224
228
|
path: str,
|
|
225
|
-
params:
|
|
226
|
-
retry_strategy:
|
|
229
|
+
params: dict | None = None,
|
|
230
|
+
retry_strategy: RetryStrategy | None = None,
|
|
227
231
|
) -> httpx.Response:
|
|
228
232
|
"""Performs a GET-request to the Sumo API.
|
|
229
233
|
|
|
@@ -261,12 +265,12 @@ class SumoClient:
|
|
|
261
265
|
follow_redirects = False
|
|
262
266
|
if (
|
|
263
267
|
re.match(
|
|
264
|
-
r"^/objects\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/blob$",
|
|
268
|
+
r"^/objects\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/blob$",
|
|
265
269
|
path,
|
|
266
270
|
)
|
|
267
271
|
is not None
|
|
268
272
|
or re.match(
|
|
269
|
-
r"^/tasks\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/result$",
|
|
273
|
+
r"^/tasks\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/result$",
|
|
270
274
|
path,
|
|
271
275
|
)
|
|
272
276
|
is not None
|
|
@@ -291,10 +295,10 @@ class SumoClient:
|
|
|
291
295
|
def post(
|
|
292
296
|
self,
|
|
293
297
|
path: str,
|
|
294
|
-
blob:
|
|
295
|
-
json:
|
|
296
|
-
params:
|
|
297
|
-
retry_strategy:
|
|
298
|
+
blob: bytes | None = None,
|
|
299
|
+
json: dict | None = None,
|
|
300
|
+
params: dict | None = None,
|
|
301
|
+
retry_strategy: RetryStrategy | None = None,
|
|
298
302
|
) -> httpx.Response:
|
|
299
303
|
"""Performs a POST-request to the Sumo API.
|
|
300
304
|
|
|
@@ -369,9 +373,9 @@ class SumoClient:
|
|
|
369
373
|
def put(
|
|
370
374
|
self,
|
|
371
375
|
path: str,
|
|
372
|
-
blob:
|
|
373
|
-
json:
|
|
374
|
-
retry_strategy:
|
|
376
|
+
blob: bytes | None = None,
|
|
377
|
+
json: dict | None = None,
|
|
378
|
+
retry_strategy: RetryStrategy | None = None,
|
|
375
379
|
) -> httpx.Response:
|
|
376
380
|
"""Performs a PUT-request to the Sumo API.
|
|
377
381
|
|
|
@@ -421,8 +425,8 @@ class SumoClient:
|
|
|
421
425
|
def delete(
|
|
422
426
|
self,
|
|
423
427
|
path: str,
|
|
424
|
-
params:
|
|
425
|
-
retry_strategy:
|
|
428
|
+
params: dict | None = None,
|
|
429
|
+
retry_strategy: RetryStrategy | None = None,
|
|
426
430
|
) -> httpx.Response:
|
|
427
431
|
"""Performs a DELETE-request to the Sumo API.
|
|
428
432
|
|
|
@@ -462,7 +466,7 @@ class SumoClient:
|
|
|
462
466
|
|
|
463
467
|
return retryer(_delete)
|
|
464
468
|
|
|
465
|
-
def _get_retry_details(self, response_in) ->
|
|
469
|
+
def _get_retry_details(self, response_in) -> tuple[str, int]:
|
|
466
470
|
assert response_in.status_code == 202, (
|
|
467
471
|
"Incorrect status code; expcted 202"
|
|
468
472
|
)
|
|
@@ -480,7 +484,7 @@ class SumoClient:
|
|
|
480
484
|
self,
|
|
481
485
|
response_in: httpx.Response,
|
|
482
486
|
timeout=None,
|
|
483
|
-
retry_strategy:
|
|
487
|
+
retry_strategy: RetryStrategy | None = None,
|
|
484
488
|
) -> httpx.Response:
|
|
485
489
|
"""Poll a specific endpoint until a result is obtained.
|
|
486
490
|
|
|
@@ -502,7 +506,6 @@ class SumoClient:
|
|
|
502
506
|
"No response within specified timeout."
|
|
503
507
|
)
|
|
504
508
|
location, retry_after = self._get_retry_details(response)
|
|
505
|
-
pass
|
|
506
509
|
|
|
507
510
|
def getLogger(self, name):
|
|
508
511
|
"""Gets a logger object that sends log objects into the message_log
|
|
@@ -521,7 +524,6 @@ class SumoClient:
|
|
|
521
524
|
if len(logger.handlers) == 0:
|
|
522
525
|
handler = LogHandlerSumo(self)
|
|
523
526
|
logger.addHandler(handler)
|
|
524
|
-
pass
|
|
525
527
|
return logger
|
|
526
528
|
|
|
527
529
|
def create_shared_access_key_for_case(self, case_uuid):
|
|
@@ -561,8 +563,8 @@ class SumoClient:
|
|
|
561
563
|
async def get_async(
|
|
562
564
|
self,
|
|
563
565
|
path: str,
|
|
564
|
-
params:
|
|
565
|
-
retry_strategy:
|
|
566
|
+
params: dict | None = None,
|
|
567
|
+
retry_strategy: RetryStrategy | None = None,
|
|
566
568
|
) -> httpx.Response:
|
|
567
569
|
"""Performs an async GET-request to the Sumo API.
|
|
568
570
|
|
|
@@ -600,12 +602,12 @@ class SumoClient:
|
|
|
600
602
|
follow_redirects = False
|
|
601
603
|
if (
|
|
602
604
|
re.match(
|
|
603
|
-
r"^/objects\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/blob$",
|
|
605
|
+
r"^/objects\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/blob$",
|
|
604
606
|
path,
|
|
605
607
|
)
|
|
606
608
|
is not None
|
|
607
609
|
or re.match(
|
|
608
|
-
r"^/tasks\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/result$",
|
|
610
|
+
r"^/tasks\('[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{4}-[0-9a-fA-F-]{12}'\)/result$",
|
|
609
611
|
path,
|
|
610
612
|
)
|
|
611
613
|
is not None
|
|
@@ -631,10 +633,10 @@ class SumoClient:
|
|
|
631
633
|
async def post_async(
|
|
632
634
|
self,
|
|
633
635
|
path: str,
|
|
634
|
-
blob:
|
|
635
|
-
json:
|
|
636
|
-
params:
|
|
637
|
-
retry_strategy:
|
|
636
|
+
blob: bytes | None = None,
|
|
637
|
+
json: dict | None = None,
|
|
638
|
+
params: dict | None = None,
|
|
639
|
+
retry_strategy: RetryStrategy | None = None,
|
|
638
640
|
) -> httpx.Response:
|
|
639
641
|
"""Performs an async POST-request to the Sumo API.
|
|
640
642
|
|
|
@@ -710,9 +712,9 @@ class SumoClient:
|
|
|
710
712
|
async def put_async(
|
|
711
713
|
self,
|
|
712
714
|
path: str,
|
|
713
|
-
blob:
|
|
714
|
-
json:
|
|
715
|
-
retry_strategy:
|
|
715
|
+
blob: bytes | None = None,
|
|
716
|
+
json: dict | None = None,
|
|
717
|
+
retry_strategy: RetryStrategy | None = None,
|
|
716
718
|
) -> httpx.Response:
|
|
717
719
|
"""Performs an async PUT-request to the Sumo API.
|
|
718
720
|
|
|
@@ -762,8 +764,8 @@ class SumoClient:
|
|
|
762
764
|
async def delete_async(
|
|
763
765
|
self,
|
|
764
766
|
path: str,
|
|
765
|
-
params:
|
|
766
|
-
retry_strategy:
|
|
767
|
+
params: dict | None = None,
|
|
768
|
+
retry_strategy: RetryStrategy | None = None,
|
|
767
769
|
) -> httpx.Response:
|
|
768
770
|
"""Performs an async DELETE-request to the Sumo API.
|
|
769
771
|
|
|
@@ -807,7 +809,7 @@ class SumoClient:
|
|
|
807
809
|
self,
|
|
808
810
|
response_in: httpx.Response,
|
|
809
811
|
timeout=None,
|
|
810
|
-
retry_strategy:
|
|
812
|
+
retry_strategy: RetryStrategy | None = None,
|
|
811
813
|
) -> httpx.Response:
|
|
812
814
|
"""Poll a specific endpoint until a result is obtained.
|
|
813
815
|
|
|
@@ -831,4 +833,3 @@ class SumoClient:
|
|
|
831
833
|
"No response within specified timeout."
|
|
832
834
|
)
|
|
833
835
|
location, retry_after = self._get_retry_details(response)
|
|
834
|
-
pass
|
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": [
|
|
3
|
+
".github/pull_request_template.md",
|
|
4
|
+
".github/workflows/build_docs.yaml",
|
|
5
|
+
".github/workflows/linting.yml",
|
|
6
|
+
".github/workflows/publish_release.yml",
|
|
7
|
+
".github/workflows/pytest.yml",
|
|
8
|
+
".gitignore",
|
|
3
9
|
".pre-commit-config.yaml",
|
|
4
|
-
"
|
|
10
|
+
".readthedocs.yaml",
|
|
11
|
+
"CONTRIBUTING.md",
|
|
5
12
|
"LICENSE",
|
|
6
|
-
"
|
|
13
|
+
"README.md",
|
|
7
14
|
"SECURITY.md",
|
|
8
|
-
"CONTRIBUTING.md",
|
|
9
|
-
".gitignore",
|
|
10
|
-
".readthedocs.yaml",
|
|
11
15
|
"docs/Makefile",
|
|
12
|
-
"docs/index.rst",
|
|
13
|
-
"docs/api.rst",
|
|
14
|
-
"docs/make.bat",
|
|
15
|
-
"docs/sumo-wrapper-python.rst",
|
|
16
|
-
"docs/conf.py",
|
|
17
|
-
"docs/_static/equinor-logo2.jpg",
|
|
18
16
|
"docs/_static/equinor-logo.png",
|
|
19
|
-
"docs/_static/
|
|
17
|
+
"docs/_static/equinor-logo2.jpg",
|
|
20
18
|
"docs/_static/equinor_logo.jpg",
|
|
19
|
+
"docs/_static/equinor_logo_only.jpg",
|
|
21
20
|
"docs/_templates/layout.html",
|
|
21
|
+
"docs/api.rst",
|
|
22
|
+
"docs/conf.py",
|
|
23
|
+
"docs/index.rst",
|
|
24
|
+
"docs/make.bat",
|
|
25
|
+
"docs/sumo-wrapper-python.rst",
|
|
26
|
+
"pyproject.toml",
|
|
22
27
|
"src/sumo/__init__.py",
|
|
23
|
-
"src/sumo/wrapper/_logging.py",
|
|
24
28
|
"src/sumo/wrapper/__init__.py",
|
|
25
|
-
"src/sumo/wrapper/_decorators.py",
|
|
26
|
-
"src/sumo/wrapper/sumo_client.py",
|
|
27
29
|
"src/sumo/wrapper/_auth_provider.py",
|
|
28
30
|
"src/sumo/wrapper/_blob_client.py",
|
|
31
|
+
"src/sumo/wrapper/_decorators.py",
|
|
32
|
+
"src/sumo/wrapper/_logging.py",
|
|
29
33
|
"src/sumo/wrapper/_retry_strategy.py",
|
|
30
34
|
"src/sumo/wrapper/login.py",
|
|
31
|
-
"
|
|
35
|
+
"src/sumo/wrapper/sumo_client.py",
|
|
32
36
|
"tests/conftest.py",
|
|
37
|
+
"tests/test_sumo_thin_client.py",
|
|
33
38
|
"tests/testdata/case.yml",
|
|
34
|
-
"tests/testdata/surface.yml"
|
|
35
|
-
".github/pull_request_template.md",
|
|
36
|
-
".github/workflows/build_docs.yaml",
|
|
37
|
-
".github/workflows/publish_release.yml",
|
|
38
|
-
".github/workflows/linting.yml",
|
|
39
|
-
".github/workflows/pytest.yml"
|
|
39
|
+
"tests/testdata/surface.yml"
|
|
40
40
|
]
|
|
41
41
|
}
|
|
@@ -7,10 +7,11 @@ from time import sleep
|
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
import yaml
|
|
10
|
+
from httpx import HTTPStatusError
|
|
10
11
|
|
|
11
12
|
sys.path.append(os.path.abspath(os.path.join("src")))
|
|
12
13
|
|
|
13
|
-
from sumo.wrapper import SumoClient
|
|
14
|
+
from sumo.wrapper import SumoClient
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def _upload_parent_object(conn, json):
|
|
@@ -99,7 +100,7 @@ def test_upload_search_delete_ensemble_child(token):
|
|
|
99
100
|
)
|
|
100
101
|
except Exception as ex:
|
|
101
102
|
print(ex.response.text)
|
|
102
|
-
raise
|
|
103
|
+
raise
|
|
103
104
|
|
|
104
105
|
assert 200 <= response_surface.status_code <= 202
|
|
105
106
|
assert isinstance(response_surface.json(), dict)
|
|
@@ -169,10 +170,8 @@ def test_fail_on_wrong_metadata(token):
|
|
|
169
170
|
Upload a parent object with erroneous metadata, confirm failure
|
|
170
171
|
"""
|
|
171
172
|
conn = SumoClient(env="dev", token=token)
|
|
172
|
-
with pytest.raises(
|
|
173
|
-
|
|
174
|
-
conn=conn, json={"some field": "some value"}
|
|
175
|
-
)
|
|
173
|
+
with pytest.raises(HTTPStatusError):
|
|
174
|
+
_upload_parent_object(conn=conn, json={"some field": "some value"})
|
|
176
175
|
|
|
177
176
|
|
|
178
177
|
def test_upload_duplicate_ensemble(token):
|
|
@@ -220,8 +219,8 @@ def test_upload_duplicate_ensemble(token):
|
|
|
220
219
|
sleep(61)
|
|
221
220
|
|
|
222
221
|
# Search for ensemble
|
|
223
|
-
with pytest.raises(
|
|
224
|
-
|
|
222
|
+
with pytest.raises(HTTPStatusError):
|
|
223
|
+
_download_object(conn, object_id=case_id2)
|
|
225
224
|
|
|
226
225
|
|
|
227
226
|
def test_poll(token):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|