sumo-wrapper-python 1.0.25__tar.gz → 1.0.27__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.
Potentially problematic release.
This version of sumo-wrapper-python might be problematic. Click here for more details.
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/PKG-INFO +3 -2
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_auth_provider.py +28 -4
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_logging.py +3 -3
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_retry_strategy.py +2 -2
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_version.py +9 -4
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/login.py +0 -12
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/sumo_client.py +98 -21
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo_wrapper_python.egg-info/PKG-INFO +3 -2
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/tests/test_sumo_thin_client.py +16 -3
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/tests/testdata/surface.yml +1 -1
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.github/workflows/build_docs.yaml +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.github/workflows/linting.yml +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.github/workflows/publish_release.yml +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.github/workflows/pytest.yml +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.gitignore +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.pre-commit-config.yaml +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.readthedocs.yaml +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/CONTRIBUTING.md +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/LICENSE +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/README.md +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/SECURITY.md +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/Makefile +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/_static/equinor-logo.png +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/_static/equinor-logo2.jpg +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/_static/equinor_logo.jpg +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/_static/equinor_logo_only.jpg +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/_templates/layout.html +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/api.rst +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/conf.py +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/index.rst +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/make.bat +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/sumo-wrapper-python.rst +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/pyproject.toml +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/setup.cfg +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/__init__.py +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/__init__.py +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_blob_client.py +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_decorators.py +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/config.py +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo_wrapper_python.egg-info/SOURCES.txt +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo_wrapper_python.egg-info/dependency_links.txt +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo_wrapper_python.egg-info/entry_points.txt +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo_wrapper_python.egg-info/requires.txt +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo_wrapper_python.egg-info/top_level.txt +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/tests/conftest.py +0 -0
- {sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/tests/testdata/case.yml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sumo-wrapper-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.27
|
|
4
4
|
Summary: Python wrapper for the Sumo API
|
|
5
5
|
Author: Equinor
|
|
6
6
|
License: Apache License
|
|
@@ -229,6 +229,7 @@ Requires-Dist: sphinxcontrib-apidoc; extra == "docs"
|
|
|
229
229
|
Provides-Extra: dev
|
|
230
230
|
Requires-Dist: ruff; extra == "dev"
|
|
231
231
|
Requires-Dist: pre-commit; extra == "dev"
|
|
232
|
+
Dynamic: license-file
|
|
232
233
|
|
|
233
234
|
# sumo-wrapper-python
|
|
234
235
|
|
{sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_auth_provider.py
RENAMED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import errno
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
+
import platform
|
|
4
5
|
import stat
|
|
5
6
|
import sys
|
|
6
7
|
import time
|
|
7
8
|
from datetime import datetime, timedelta, timezone
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict
|
|
8
11
|
from urllib.parse import parse_qs
|
|
9
12
|
|
|
10
13
|
import jwt
|
|
@@ -74,10 +77,10 @@ class AuthProvider:
|
|
|
74
77
|
# ELSE
|
|
75
78
|
return result["access_token"]
|
|
76
79
|
|
|
77
|
-
def get_authorization(self):
|
|
80
|
+
def get_authorization(self) -> Dict:
|
|
78
81
|
token = self.get_token()
|
|
79
82
|
if token is None:
|
|
80
|
-
return
|
|
83
|
+
return {}
|
|
81
84
|
|
|
82
85
|
return {"Authorization": "Bearer " + token}
|
|
83
86
|
|
|
@@ -99,6 +102,13 @@ class AuthProvider:
|
|
|
99
102
|
pass
|
|
100
103
|
|
|
101
104
|
|
|
105
|
+
class AuthProviderNone(AuthProvider):
|
|
106
|
+
def get_token(self):
|
|
107
|
+
raise Exception("No valid authorization provider found.")
|
|
108
|
+
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
|
|
102
112
|
class AuthProviderSilent(AuthProvider):
|
|
103
113
|
def __init__(self, client_id, authority, resource_id):
|
|
104
114
|
super().__init__(resource_id)
|
|
@@ -421,7 +431,7 @@ def get_auth_provider(
|
|
|
421
431
|
refresh_token=None,
|
|
422
432
|
devicecode=False,
|
|
423
433
|
case_uuid=None,
|
|
424
|
-
):
|
|
434
|
+
) -> AuthProvider:
|
|
425
435
|
if refresh_token:
|
|
426
436
|
return AuthProviderRefreshToken(
|
|
427
437
|
refresh_token, client_id, authority, resource_id
|
|
@@ -441,6 +451,18 @@ def get_auth_provider(
|
|
|
441
451
|
pass
|
|
442
452
|
# ELSE
|
|
443
453
|
if interactive:
|
|
454
|
+
lockfile_path = Path.home() / ".config/chromium/SingletonLock"
|
|
455
|
+
|
|
456
|
+
if Path(lockfile_path).is_symlink() and not str(
|
|
457
|
+
Path(lockfile_path).resolve()
|
|
458
|
+
).__contains__(platform.node()):
|
|
459
|
+
# https://github.com/equinor/sumo-wrapper-python/issues/193
|
|
460
|
+
print(
|
|
461
|
+
"\n\n\033[1mDetected chromium lockfile for different node; using firefox to authenticate.\033[0m"
|
|
462
|
+
)
|
|
463
|
+
os.environ["BROWSER"] = "firefox"
|
|
464
|
+
pass
|
|
465
|
+
|
|
444
466
|
return AuthProviderInteractive(client_id, authority, resource_id)
|
|
445
467
|
# ELSE
|
|
446
468
|
if devicecode:
|
|
@@ -458,6 +480,8 @@ def get_auth_provider(
|
|
|
458
480
|
]
|
|
459
481
|
):
|
|
460
482
|
return AuthProviderManaged(resource_id)
|
|
483
|
+
# ELSE
|
|
484
|
+
return AuthProviderNone(resource_id)
|
|
461
485
|
|
|
462
486
|
|
|
463
487
|
def cleanup_shared_keys():
|
|
@@ -467,7 +491,7 @@ def cleanup_shared_keys():
|
|
|
467
491
|
for f in os.listdir(tokendir):
|
|
468
492
|
ff = os.path.join(tokendir, f)
|
|
469
493
|
if os.path.isfile(ff):
|
|
470
|
-
(
|
|
494
|
+
(_, ext) = os.path.splitext(ff)
|
|
471
495
|
if ext.lower() == ".sharedkey":
|
|
472
496
|
try:
|
|
473
497
|
with open(ff, "r") as file:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from datetime import datetime
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class LogHandlerSumo(logging.Handler):
|
|
@@ -11,8 +11,8 @@ class LogHandlerSumo(logging.Handler):
|
|
|
11
11
|
def emit(self, record):
|
|
12
12
|
try:
|
|
13
13
|
dt = (
|
|
14
|
-
datetime.now(
|
|
15
|
-
.replace(microsecond=0)
|
|
14
|
+
datetime.now(timezone.utc)
|
|
15
|
+
.replace(microsecond=0, tzinfo=None)
|
|
16
16
|
.isoformat()
|
|
17
17
|
+ "Z"
|
|
18
18
|
)
|
{sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo/wrapper/_retry_strategy.py
RENAMED
|
@@ -44,7 +44,7 @@ class RetryStrategy:
|
|
|
44
44
|
self._exp_base = exp_base
|
|
45
45
|
return
|
|
46
46
|
|
|
47
|
-
def make_retryer(self):
|
|
47
|
+
def make_retryer(self) -> tn.Retrying:
|
|
48
48
|
return tn.Retrying(
|
|
49
49
|
stop=tn.stop_after_attempt(self._stop_after),
|
|
50
50
|
retry=(
|
|
@@ -63,7 +63,7 @@ class RetryStrategy:
|
|
|
63
63
|
before_sleep=_log_retry_info,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
-
def make_retryer_async(self):
|
|
66
|
+
def make_retryer_async(self) -> tn.AsyncRetrying:
|
|
67
67
|
return tn.AsyncRetrying(
|
|
68
68
|
stop=tn.stop_after_attempt(self._stop_after),
|
|
69
69
|
retry=(
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
# file generated by
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
+
|
|
3
6
|
TYPE_CHECKING = False
|
|
4
7
|
if TYPE_CHECKING:
|
|
5
|
-
from typing import Tuple
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
6
11
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
12
|
else:
|
|
8
13
|
VERSION_TUPLE = object
|
|
@@ -12,5 +17,5 @@ __version__: str
|
|
|
12
17
|
__version_tuple__: VERSION_TUPLE
|
|
13
18
|
version_tuple: VERSION_TUPLE
|
|
14
19
|
|
|
15
|
-
__version__ = version = '1.0.
|
|
16
|
-
__version_tuple__ = version_tuple = (1, 0,
|
|
20
|
+
__version__ = version = '1.0.27'
|
|
21
|
+
__version_tuple__ = version_tuple = (1, 0, 27)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import platform
|
|
3
2
|
from argparse import ArgumentParser
|
|
4
|
-
from pathlib import Path
|
|
5
3
|
|
|
6
4
|
from sumo.wrapper import SumoClient
|
|
7
5
|
|
|
@@ -69,16 +67,6 @@ def main():
|
|
|
69
67
|
if mode != "silent":
|
|
70
68
|
print("Login to Sumo environment: " + env)
|
|
71
69
|
|
|
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
|
|
81
|
-
|
|
82
70
|
sumo = SumoClient(
|
|
83
71
|
env,
|
|
84
72
|
interactive=is_interactive,
|
|
@@ -2,6 +2,8 @@ import asyncio
|
|
|
2
2
|
import contextlib
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
|
+
import time
|
|
6
|
+
from typing import Dict, Optional, Tuple
|
|
5
7
|
|
|
6
8
|
import httpx
|
|
7
9
|
import jwt
|
|
@@ -24,10 +26,13 @@ DEFAULT_TIMEOUT = httpx.Timeout(30.0)
|
|
|
24
26
|
class SumoClient:
|
|
25
27
|
"""Authenticate and perform requests to the Sumo API."""
|
|
26
28
|
|
|
29
|
+
_client: httpx.Client
|
|
30
|
+
_async_client: httpx.AsyncClient
|
|
31
|
+
|
|
27
32
|
def __init__(
|
|
28
33
|
self,
|
|
29
34
|
env: str,
|
|
30
|
-
token: str = None,
|
|
35
|
+
token: Optional[str] = None,
|
|
31
36
|
interactive: bool = False,
|
|
32
37
|
devicecode: bool = False,
|
|
33
38
|
verbosity: str = "CRITICAL",
|
|
@@ -118,26 +123,23 @@ class SumoClient:
|
|
|
118
123
|
def __enter__(self):
|
|
119
124
|
return self
|
|
120
125
|
|
|
121
|
-
def __exit__(self,
|
|
126
|
+
def __exit__(self, *_):
|
|
122
127
|
if not self._borrowed_client:
|
|
123
128
|
self._client.close()
|
|
124
|
-
self._client = None
|
|
125
129
|
return False
|
|
126
130
|
|
|
127
131
|
async def __aenter__(self):
|
|
128
132
|
return self
|
|
129
133
|
|
|
130
|
-
async def __aexit__(self,
|
|
134
|
+
async def __aexit__(self, *_):
|
|
131
135
|
if not self._borrowed_async_client:
|
|
132
136
|
await self._async_client.aclose()
|
|
133
|
-
self._async_client = None
|
|
134
137
|
return False
|
|
135
138
|
|
|
136
139
|
def __del__(self):
|
|
137
140
|
if self._client is not None and not self._borrowed_client:
|
|
138
141
|
self._client.close()
|
|
139
142
|
pass
|
|
140
|
-
self._client = None
|
|
141
143
|
if self._async_client is not None and not self._borrowed_async_client:
|
|
142
144
|
|
|
143
145
|
async def closeit(client):
|
|
@@ -150,7 +152,6 @@ class SumoClient:
|
|
|
150
152
|
except RuntimeError:
|
|
151
153
|
pass
|
|
152
154
|
pass
|
|
153
|
-
self._async_client = None
|
|
154
155
|
|
|
155
156
|
def authenticate(self):
|
|
156
157
|
if self.auth is None:
|
|
@@ -185,7 +186,7 @@ class SumoClient:
|
|
|
185
186
|
)
|
|
186
187
|
|
|
187
188
|
@raise_for_status
|
|
188
|
-
def get(self, path: str, params:
|
|
189
|
+
def get(self, path: str, params: Optional[Dict] = None) -> httpx.Response:
|
|
189
190
|
"""Performs a GET-request to the Sumo API.
|
|
190
191
|
|
|
191
192
|
Args:
|
|
@@ -246,9 +247,9 @@ class SumoClient:
|
|
|
246
247
|
def post(
|
|
247
248
|
self,
|
|
248
249
|
path: str,
|
|
249
|
-
blob: bytes = None,
|
|
250
|
-
json: dict = None,
|
|
251
|
-
params: dict = None,
|
|
250
|
+
blob: Optional[bytes] = None,
|
|
251
|
+
json: Optional[dict] = None,
|
|
252
|
+
params: Optional[dict] = None,
|
|
252
253
|
) -> httpx.Response:
|
|
253
254
|
"""Performs a POST-request to the Sumo API.
|
|
254
255
|
|
|
@@ -319,7 +320,10 @@ class SumoClient:
|
|
|
319
320
|
|
|
320
321
|
@raise_for_status
|
|
321
322
|
def put(
|
|
322
|
-
self,
|
|
323
|
+
self,
|
|
324
|
+
path: str,
|
|
325
|
+
blob: Optional[bytes] = None,
|
|
326
|
+
json: Optional[dict] = None,
|
|
323
327
|
) -> httpx.Response:
|
|
324
328
|
"""Performs a PUT-request to the Sumo API.
|
|
325
329
|
|
|
@@ -364,7 +368,9 @@ class SumoClient:
|
|
|
364
368
|
return retryer(_put)
|
|
365
369
|
|
|
366
370
|
@raise_for_status
|
|
367
|
-
def delete(
|
|
371
|
+
def delete(
|
|
372
|
+
self, path: str, params: Optional[dict] = None
|
|
373
|
+
) -> httpx.Response:
|
|
368
374
|
"""Performs a DELETE-request to the Sumo API.
|
|
369
375
|
|
|
370
376
|
Args:
|
|
@@ -372,7 +378,7 @@ class SumoClient:
|
|
|
372
378
|
params: query parameters, as dictionary
|
|
373
379
|
|
|
374
380
|
Returns:
|
|
375
|
-
Sumo JSON
|
|
381
|
+
Sumo JSON response as a dictionary
|
|
376
382
|
|
|
377
383
|
Examples:
|
|
378
384
|
Deleting object::
|
|
@@ -401,6 +407,45 @@ class SumoClient:
|
|
|
401
407
|
|
|
402
408
|
return retryer(_delete)
|
|
403
409
|
|
|
410
|
+
def _get_retry_details(self, response_in) -> Tuple[str, int]:
|
|
411
|
+
assert response_in.status_code == 202, (
|
|
412
|
+
"Incorrect status code; expcted 202"
|
|
413
|
+
)
|
|
414
|
+
headers = response_in.headers
|
|
415
|
+
location: str = headers.get("location")
|
|
416
|
+
assert location is not None, "Missing header: Location"
|
|
417
|
+
assert location.startswith(self.base_url)
|
|
418
|
+
retry_after = headers.get("retry-after")
|
|
419
|
+
assert retry_after is not None, "Missing header: Retry-After"
|
|
420
|
+
location = location[len(self.base_url) :]
|
|
421
|
+
retry_after = int(retry_after)
|
|
422
|
+
return location, retry_after
|
|
423
|
+
|
|
424
|
+
def poll(
|
|
425
|
+
self, response_in: httpx.Response, timeout=None
|
|
426
|
+
) -> httpx.Response:
|
|
427
|
+
"""Poll a specific endpoint until a result is obtained.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
response_in: httpx.Response from a previous request, with 'location' and 'retry-after' headers.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
A new httpx.response object.
|
|
434
|
+
"""
|
|
435
|
+
location, retry_after = self._get_retry_details(response_in)
|
|
436
|
+
expiry = time.time() + timeout if timeout is not None else None
|
|
437
|
+
while True:
|
|
438
|
+
time.sleep(retry_after)
|
|
439
|
+
response = self.get(location)
|
|
440
|
+
if response.status_code != 202:
|
|
441
|
+
return response
|
|
442
|
+
if expiry is not None and time.time() > expiry:
|
|
443
|
+
raise httpx.TimeoutException(
|
|
444
|
+
"No response within specified timeout."
|
|
445
|
+
)
|
|
446
|
+
location, retry_after = self._get_retry_details(response)
|
|
447
|
+
pass
|
|
448
|
+
|
|
404
449
|
def getLogger(self, name):
|
|
405
450
|
"""Gets a logger object that sends log objects into the message_log
|
|
406
451
|
index for the Sumo instance.
|
|
@@ -454,7 +499,9 @@ class SumoClient:
|
|
|
454
499
|
return self
|
|
455
500
|
|
|
456
501
|
@raise_for_status_async
|
|
457
|
-
async def get_async(
|
|
502
|
+
async def get_async(
|
|
503
|
+
self, path: str, params: Optional[dict] = None
|
|
504
|
+
) -> httpx.Response:
|
|
458
505
|
"""Performs an async GET-request to the Sumo API.
|
|
459
506
|
|
|
460
507
|
Args:
|
|
@@ -515,9 +562,9 @@ class SumoClient:
|
|
|
515
562
|
async def post_async(
|
|
516
563
|
self,
|
|
517
564
|
path: str,
|
|
518
|
-
blob: bytes = None,
|
|
519
|
-
json: dict = None,
|
|
520
|
-
params: dict = None,
|
|
565
|
+
blob: Optional[bytes] = None,
|
|
566
|
+
json: Optional[dict] = None,
|
|
567
|
+
params: Optional[dict] = None,
|
|
521
568
|
) -> httpx.Response:
|
|
522
569
|
"""Performs an async POST-request to the Sumo API.
|
|
523
570
|
|
|
@@ -589,7 +636,10 @@ class SumoClient:
|
|
|
589
636
|
|
|
590
637
|
@raise_for_status_async
|
|
591
638
|
async def put_async(
|
|
592
|
-
self,
|
|
639
|
+
self,
|
|
640
|
+
path: str,
|
|
641
|
+
blob: Optional[bytes] = None,
|
|
642
|
+
json: Optional[dict] = None,
|
|
593
643
|
) -> httpx.Response:
|
|
594
644
|
"""Performs an async PUT-request to the Sumo API.
|
|
595
645
|
|
|
@@ -634,7 +684,9 @@ class SumoClient:
|
|
|
634
684
|
return await retryer(_put)
|
|
635
685
|
|
|
636
686
|
@raise_for_status_async
|
|
637
|
-
async def delete_async(
|
|
687
|
+
async def delete_async(
|
|
688
|
+
self, path: str, params: Optional[dict] = None
|
|
689
|
+
) -> httpx.Response:
|
|
638
690
|
"""Performs an async DELETE-request to the Sumo API.
|
|
639
691
|
|
|
640
692
|
Args:
|
|
@@ -642,7 +694,7 @@ class SumoClient:
|
|
|
642
694
|
params: query parameters, as dictionary
|
|
643
695
|
|
|
644
696
|
Returns:
|
|
645
|
-
Sumo JSON
|
|
697
|
+
Sumo JSON response as a dictionary
|
|
646
698
|
|
|
647
699
|
Examples:
|
|
648
700
|
Deleting object::
|
|
@@ -670,3 +722,28 @@ class SumoClient:
|
|
|
670
722
|
retryer = self._retry_strategy.make_retryer_async()
|
|
671
723
|
|
|
672
724
|
return await retryer(_delete)
|
|
725
|
+
|
|
726
|
+
async def poll_async(
|
|
727
|
+
self, response_in: httpx.Response, timeout=None
|
|
728
|
+
) -> httpx.Response:
|
|
729
|
+
"""Poll a specific endpoint until a result is obtained.
|
|
730
|
+
|
|
731
|
+
Args:
|
|
732
|
+
response_in: httpx.Response from a previous request, with 'location' and 'retry-after' headers.
|
|
733
|
+
|
|
734
|
+
Returns:
|
|
735
|
+
A new httpx.response object.
|
|
736
|
+
"""
|
|
737
|
+
location, retry_after = self._get_retry_details(response_in)
|
|
738
|
+
expiry = time.time() + timeout if timeout is not None else None
|
|
739
|
+
while True:
|
|
740
|
+
await asyncio.sleep(retry_after)
|
|
741
|
+
response = await self.get_async(location)
|
|
742
|
+
if response.status_code != 202:
|
|
743
|
+
return response
|
|
744
|
+
if expiry is not None and time.time() > expiry:
|
|
745
|
+
raise httpx.TimeoutException(
|
|
746
|
+
"No response within specified timeout."
|
|
747
|
+
)
|
|
748
|
+
location, retry_after = self._get_retry_details(response)
|
|
749
|
+
pass
|
{sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/src/sumo_wrapper_python.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sumo-wrapper-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.27
|
|
4
4
|
Summary: Python wrapper for the Sumo API
|
|
5
5
|
Author: Equinor
|
|
6
6
|
License: Apache License
|
|
@@ -229,6 +229,7 @@ Requires-Dist: sphinxcontrib-apidoc; extra == "docs"
|
|
|
229
229
|
Provides-Extra: dev
|
|
230
230
|
Requires-Dist: ruff; extra == "dev"
|
|
231
231
|
Requires-Dist: pre-commit; extra == "dev"
|
|
232
|
+
Dynamic: license-file
|
|
232
233
|
|
|
233
234
|
# sumo-wrapper-python
|
|
234
235
|
|
|
@@ -112,9 +112,13 @@ def test_upload_search_delete_ensemble_child(token):
|
|
|
112
112
|
|
|
113
113
|
fmu_surface_metadata["fmu"]["case"]["uuid"] = case_uuid
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
try:
|
|
116
|
+
response_surface = _upload_child_level_json(
|
|
117
|
+
conn=sumo_client, parent_id=case_id, json=fmu_surface_metadata
|
|
118
|
+
)
|
|
119
|
+
except Exception as ex:
|
|
120
|
+
print(ex.response.text)
|
|
121
|
+
raise ex
|
|
118
122
|
|
|
119
123
|
assert 200 <= response_surface.status_code <= 202
|
|
120
124
|
assert isinstance(response_surface.json(), dict)
|
|
@@ -237,3 +241,12 @@ def test_upload_duplicate_ensemble(token):
|
|
|
237
241
|
# Search for ensemble
|
|
238
242
|
with pytest.raises(Exception):
|
|
239
243
|
assert _download_object(conn, object_id=case_id2)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_poll(token):
|
|
247
|
+
conn = SumoClient(env="dev", token=token)
|
|
248
|
+
res = conn.get("/admin/index/orphans")
|
|
249
|
+
res2 = conn.poll(res)
|
|
250
|
+
assert res2.status_code == 200
|
|
251
|
+
indexorphans = res2.json()
|
|
252
|
+
assert isinstance(indexorphans, list)
|
|
@@ -75,7 +75,7 @@ fmu:
|
|
|
75
75
|
file:
|
|
76
76
|
relative_path: realization-33/iter-0/share/results/maps/volantis_gp_base--amplitude.gri # case-relative
|
|
77
77
|
absolute_path: /some/absolute/path//realization-33/iter-0/share/results/maps/volantis_gp_base--amplitude.gri
|
|
78
|
-
checksum_md5:
|
|
78
|
+
checksum_md5: 0123456789abcdef0123456789abcdef # checksum of the file, not the data.
|
|
79
79
|
|
|
80
80
|
data: # The data block describes the actual data (e.g. surface). Only present in data objects
|
|
81
81
|
|
|
File without changes
|
|
File without changes
|
{sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/.github/workflows/publish_release.yml
RENAMED
|
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
|
{sumo_wrapper_python-1.0.25 → sumo_wrapper_python-1.0.27}/docs/_static/equinor_logo_only.jpg
RENAMED
|
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
|