earthscope-sdk 1.0.0b0__tar.gz → 1.0.0b1__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.
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/PKG-INFO +5 -3
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/pyproject.toml +7 -3
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/__init__.py +1 -1
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/auth/auth_flow.py +19 -4
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/user/_base.py +2 -2
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/common/service.py +5 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/config/models.py +85 -2
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk.egg-info/PKG-INFO +5 -3
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk.egg-info/requires.txt +2 -1
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/tests/test_auth.py +30 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/tests/test_client.py +32 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/tests/test_settings.py +40 -2
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/LICENSE +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/README.md +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/setup.cfg +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/setup.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/auth/__init__.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/auth/client_credentials_flow.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/auth/device_code_flow.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/auth/error.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/__init__.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/_client.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/user/__init__.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/user/_service.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/user/models.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/common/__init__.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/common/_sync_runner.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/common/client.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/common/context.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/config/__init__.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/config/_compat.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/config/_util.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/config/error.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/config/settings.py +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk.egg-info/SOURCES.txt +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk.egg-info/dependency_links.txt +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk.egg-info/top_level.txt +0 -0
- {earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/tests/test_context.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: earthscope-sdk
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.0b1
|
4
4
|
Summary: An SDK for EarthScope API
|
5
5
|
Author-email: EarthScope <data-help@earthscope.org>
|
6
6
|
License: Apache License
|
@@ -214,7 +214,8 @@ Requires-Python: >=3.9
|
|
214
214
|
Description-Content-Type: text/markdown
|
215
215
|
License-File: LICENSE
|
216
216
|
Requires-Dist: httpx>=0.27.0
|
217
|
-
Requires-Dist: pydantic-settings[toml]>=2.
|
217
|
+
Requires-Dist: pydantic-settings[toml]>=2.8.0
|
218
|
+
Requires-Dist: stamina>=24.3.0
|
218
219
|
Provides-Extra: dev
|
219
220
|
Requires-Dist: bumpver; extra == "dev"
|
220
221
|
Requires-Dist: build; extra == "dev"
|
@@ -224,6 +225,7 @@ Requires-Dist: pip-tools; extra == "dev"
|
|
224
225
|
Requires-Dist: pytest-httpx; extra == "dev"
|
225
226
|
Requires-Dist: pytest-asyncio; extra == "dev"
|
226
227
|
Requires-Dist: ruff; extra == "dev"
|
228
|
+
Dynamic: license-file
|
227
229
|
|
228
230
|
# EarthScope SDK
|
229
231
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "earthscope-sdk"
|
7
|
-
version = "1.0.
|
7
|
+
version = "1.0.0b1"
|
8
8
|
description = "An SDK for EarthScope API"
|
9
9
|
readme = "README.md"
|
10
10
|
authors = [{ name = "EarthScope", email = "data-help@earthscope.org" }]
|
@@ -17,7 +17,11 @@ classifiers = [
|
|
17
17
|
]
|
18
18
|
|
19
19
|
#Suggestion not to pin dependencies since the package should work in many different python environments
|
20
|
-
dependencies = [
|
20
|
+
dependencies = [
|
21
|
+
"httpx>=0.27.0",
|
22
|
+
"pydantic-settings[toml]>=2.8.0",
|
23
|
+
"stamina>=24.3.0",
|
24
|
+
]
|
21
25
|
requires-python = ">=3.9"
|
22
26
|
|
23
27
|
[project.optional-dependencies]
|
@@ -36,7 +40,7 @@ dev = [
|
|
36
40
|
Homepage = "https://gitlab.com/earthscope/public/earthscope-sdk"
|
37
41
|
|
38
42
|
[tool.bumpver]
|
39
|
-
current_version = "1.0.
|
43
|
+
current_version = "1.0.0b1"
|
40
44
|
version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"
|
41
45
|
commit_message = "chore: bump version {old_version} -> {new_version}"
|
42
46
|
commit = true
|
@@ -157,12 +157,14 @@ class AuthFlow(httpx.Auth):
|
|
157
157
|
NoRefreshTokenError: no refresh token is present
|
158
158
|
InvalidRefreshTokenError: the token refresh failed
|
159
159
|
"""
|
160
|
+
from httpx import HTTPStatusError, ReadTimeout
|
161
|
+
|
160
162
|
refresh_token = self.refresh_token
|
161
163
|
scope = scope or self._settings.scope
|
162
164
|
|
163
|
-
|
165
|
+
request = self._ctx.httpx_client.build_request(
|
166
|
+
"POST",
|
164
167
|
f"{self._settings.domain}oauth/token",
|
165
|
-
auth=None, # override client default
|
166
168
|
headers={"content-type": "application/x-www-form-urlencoded"},
|
167
169
|
data={
|
168
170
|
"grant_type": "refresh_token",
|
@@ -171,9 +173,22 @@ class AuthFlow(httpx.Auth):
|
|
171
173
|
"scopes": scope,
|
172
174
|
},
|
173
175
|
)
|
174
|
-
|
175
|
-
|
176
|
+
|
177
|
+
try:
|
178
|
+
async for attempt in self._settings.retry.retry_context(ReadTimeout):
|
179
|
+
with attempt:
|
180
|
+
r = await self._ctx.httpx_client.send(request, auth=None)
|
181
|
+
r.raise_for_status()
|
182
|
+
except HTTPStatusError as e:
|
183
|
+
logger.error(
|
184
|
+
f"error during token refresh ({attempt.num} attempts): {e.response.content}"
|
185
|
+
)
|
176
186
|
raise InvalidRefreshTokenError("refresh token exchange failed")
|
187
|
+
except Exception as e:
|
188
|
+
logger.error(
|
189
|
+
f"error during token refresh ({attempt.num} attempts)", exc_info=e
|
190
|
+
)
|
191
|
+
raise InvalidRefreshTokenError("refresh token exchange failed") from e
|
177
192
|
|
178
193
|
# add previous refresh token to new tokens if omitted from resp
|
179
194
|
# (i.e. we have a non-rotating refresh token)
|
@@ -18,7 +18,7 @@ class UserBaseService(SdkService):
|
|
18
18
|
url=f"{self.resources.api_url}beta/user/credentials/aws/{role}",
|
19
19
|
)
|
20
20
|
|
21
|
-
resp = await self.
|
21
|
+
resp = await self._send_with_retries(req)
|
22
22
|
|
23
23
|
return AwsTemporaryCredentials.model_validate_json(resp.content)
|
24
24
|
|
@@ -34,6 +34,6 @@ class UserBaseService(SdkService):
|
|
34
34
|
url=f"{self.resources.api_url}beta/user/profile",
|
35
35
|
)
|
36
36
|
|
37
|
-
resp = await self.
|
37
|
+
resp = await self._send_with_retries(req)
|
38
38
|
|
39
39
|
return UserProfile.model_validate_json(resp.content)
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import base64
|
2
2
|
import binascii
|
3
3
|
import datetime as dt
|
4
|
+
import functools
|
4
5
|
from contextlib import suppress
|
5
6
|
from enum import Enum
|
6
7
|
from functools import cached_property
|
7
|
-
from typing import Annotated, Any, Optional, Union
|
8
|
+
from typing import Annotated, Any, Optional, Type, Union
|
8
9
|
|
10
|
+
from annotated_types import Ge, Gt
|
9
11
|
from pydantic import (
|
10
12
|
AliasChoices,
|
11
13
|
BaseModel,
|
@@ -86,7 +88,7 @@ class Tokens(BaseModel):
|
|
86
88
|
return None
|
87
89
|
|
88
90
|
with suppress(IndexError, binascii.Error, ValidationError):
|
89
|
-
payload_b64 = self.access_token.get_secret_value().split(".")[1]
|
91
|
+
payload_b64 = self.access_token.get_secret_value().split(".", 2)[1]
|
90
92
|
payload = base64.b64decode(payload_b64 + "==") # extra padding
|
91
93
|
return AccessTokenBody.model_validate_json(payload)
|
92
94
|
|
@@ -121,6 +123,76 @@ class Tokens(BaseModel):
|
|
121
123
|
raise ValueError("At least one of access token and refresh token is required.")
|
122
124
|
|
123
125
|
|
126
|
+
class RetrySettings(BaseModel):
|
127
|
+
"""
|
128
|
+
Retry configuration for the [Stamina library](https://stamina.hynek.me/en/stable/index.html)
|
129
|
+
"""
|
130
|
+
|
131
|
+
# same defaults as AWS SDK "standard" mode:
|
132
|
+
# https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html#standard-retry-mode
|
133
|
+
|
134
|
+
attempts: Annotated[int, Ge(0)] = 3
|
135
|
+
timeout: Timedelta = dt.timedelta(seconds=20)
|
136
|
+
|
137
|
+
wait_initial: Timedelta = dt.timedelta(milliseconds=100)
|
138
|
+
wait_max: Timedelta = dt.timedelta(seconds=5)
|
139
|
+
wait_jitter: Timedelta = dt.timedelta(seconds=1)
|
140
|
+
wait_exp_base: Annotated[float, Gt(0)] = 2
|
141
|
+
|
142
|
+
async def retry_context(self, *retry_exc: Type[Exception]):
|
143
|
+
"""
|
144
|
+
Obtain a [Stamina](https://stamina.hynek.me/en/stable/index.html) retry iterator.
|
145
|
+
"""
|
146
|
+
from stamina import retry_context
|
147
|
+
|
148
|
+
retry_on = functools.partial(self.is_retriable, retry_exc=retry_exc)
|
149
|
+
|
150
|
+
ctx = retry_context(
|
151
|
+
on=retry_on,
|
152
|
+
attempts=self.attempts,
|
153
|
+
timeout=self.timeout,
|
154
|
+
wait_initial=self.wait_initial,
|
155
|
+
wait_jitter=self.wait_jitter,
|
156
|
+
wait_max=self.wait_max,
|
157
|
+
wait_exp_base=self.wait_exp_base,
|
158
|
+
)
|
159
|
+
async for attempt in ctx:
|
160
|
+
yield attempt
|
161
|
+
|
162
|
+
def is_retriable(
|
163
|
+
self,
|
164
|
+
exc: Exception,
|
165
|
+
*args,
|
166
|
+
retry_exc: tuple[Type[Exception]] = (),
|
167
|
+
**kwargs,
|
168
|
+
) -> bool:
|
169
|
+
"""
|
170
|
+
Check if the given exception can be retried
|
171
|
+
"""
|
172
|
+
if retry_exc and isinstance(exc, retry_exc):
|
173
|
+
return True
|
174
|
+
|
175
|
+
return False
|
176
|
+
|
177
|
+
|
178
|
+
class HttpRetrySettings(RetrySettings):
|
179
|
+
status_codes: set[int] = {429, 500, 502, 503, 504}
|
180
|
+
|
181
|
+
def is_retriable(
|
182
|
+
self,
|
183
|
+
exc: Exception,
|
184
|
+
*args,
|
185
|
+
**kwargs,
|
186
|
+
) -> bool:
|
187
|
+
from httpx import HTTPStatusError
|
188
|
+
|
189
|
+
if isinstance(exc, HTTPStatusError):
|
190
|
+
if exc.response.status_code in self.status_codes:
|
191
|
+
return True
|
192
|
+
|
193
|
+
return super().is_retriable(exc, *args, **kwargs)
|
194
|
+
|
195
|
+
|
124
196
|
class AuthFlowSettings(Tokens):
|
125
197
|
"""
|
126
198
|
Auth flow configuration
|
@@ -135,6 +207,14 @@ class AuthFlowSettings(Tokens):
|
|
135
207
|
scope: str = "offline_access"
|
136
208
|
client_secret: Optional[SecretStr] = None
|
137
209
|
|
210
|
+
# Auth exchange retries
|
211
|
+
retry: HttpRetrySettings = HttpRetrySettings(
|
212
|
+
attempts=5,
|
213
|
+
timeout=dt.timedelta(seconds=30),
|
214
|
+
wait_initial=dt.timedelta(seconds=1),
|
215
|
+
wait_jitter=dt.timedelta(seconds=3),
|
216
|
+
)
|
217
|
+
|
138
218
|
@cached_property
|
139
219
|
def auth_flow_type(self) -> AuthFlowType:
|
140
220
|
if self.client_secret is not None:
|
@@ -157,6 +237,9 @@ class HttpSettings(BaseModel):
|
|
157
237
|
timeout_connect: Timedelta = dt.timedelta(seconds=5)
|
158
238
|
timeout_read: Timedelta = dt.timedelta(seconds=5)
|
159
239
|
|
240
|
+
# automatically retry requests
|
241
|
+
retry: HttpRetrySettings = HttpRetrySettings()
|
242
|
+
|
160
243
|
# Other
|
161
244
|
user_agent: str = f"earthscope-sdk py/{__version__}"
|
162
245
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: earthscope-sdk
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.0b1
|
4
4
|
Summary: An SDK for EarthScope API
|
5
5
|
Author-email: EarthScope <data-help@earthscope.org>
|
6
6
|
License: Apache License
|
@@ -214,7 +214,8 @@ Requires-Python: >=3.9
|
|
214
214
|
Description-Content-Type: text/markdown
|
215
215
|
License-File: LICENSE
|
216
216
|
Requires-Dist: httpx>=0.27.0
|
217
|
-
Requires-Dist: pydantic-settings[toml]>=2.
|
217
|
+
Requires-Dist: pydantic-settings[toml]>=2.8.0
|
218
|
+
Requires-Dist: stamina>=24.3.0
|
218
219
|
Provides-Extra: dev
|
219
220
|
Requires-Dist: bumpver; extra == "dev"
|
220
221
|
Requires-Dist: build; extra == "dev"
|
@@ -224,6 +225,7 @@ Requires-Dist: pip-tools; extra == "dev"
|
|
224
225
|
Requires-Dist: pytest-httpx; extra == "dev"
|
225
226
|
Requires-Dist: pytest-asyncio; extra == "dev"
|
226
227
|
Requires-Dist: ruff; extra == "dev"
|
228
|
+
Dynamic: license-file
|
227
229
|
|
228
230
|
# EarthScope SDK
|
229
231
|
|
@@ -2,6 +2,7 @@ import time
|
|
2
2
|
import webbrowser
|
3
3
|
|
4
4
|
import pytest
|
5
|
+
from pytest_httpx import HTTPXMock
|
5
6
|
|
6
7
|
from earthscope_sdk.auth.client_credentials_flow import ClientCredentialsFlow
|
7
8
|
from earthscope_sdk.auth.device_code_flow import DeviceCodeFlow
|
@@ -17,6 +18,8 @@ from .util import (
|
|
17
18
|
is_pipeline,
|
18
19
|
missing_m2m_creds,
|
19
20
|
missing_refresh_token,
|
21
|
+
register_mock_token_response,
|
22
|
+
retries_enabled,
|
20
23
|
)
|
21
24
|
|
22
25
|
|
@@ -135,6 +138,33 @@ class TestAuthDeviceCodeFlow:
|
|
135
138
|
flow.refresh_if_necessary()
|
136
139
|
assert flow.access_token == at
|
137
140
|
|
141
|
+
@pytest.mark.asyncio
|
142
|
+
async def test_refresh_retry(
|
143
|
+
self,
|
144
|
+
httpx_mock: HTTPXMock,
|
145
|
+
):
|
146
|
+
httpx_mock.add_response(429)
|
147
|
+
httpx_mock.add_response(500)
|
148
|
+
httpx_mock.add_response(502)
|
149
|
+
httpx_mock.add_response(504)
|
150
|
+
mock_token_body = register_mock_token_response(httpx_mock)
|
151
|
+
|
152
|
+
settings = SdkSettings(
|
153
|
+
oauth2=AuthFlowSettings(
|
154
|
+
audience=AUDIENCE,
|
155
|
+
domain=DOMAIN,
|
156
|
+
refresh_token="mock-refresh-token",
|
157
|
+
)
|
158
|
+
)
|
159
|
+
flow = DeviceCodeFlow(SdkContext(settings))
|
160
|
+
|
161
|
+
with retries_enabled(5):
|
162
|
+
await flow.async_refresh()
|
163
|
+
|
164
|
+
assert len(httpx_mock.get_requests()) == 5
|
165
|
+
assert flow.refresh_token == "mock-refresh-token"
|
166
|
+
assert flow.access_token_body == mock_token_body
|
167
|
+
|
138
168
|
|
139
169
|
class TestAuthClientCredentialsFlow:
|
140
170
|
def test_no_device_code_with_m2m_creds(self):
|
@@ -8,6 +8,7 @@ from earthscope_sdk.client import AsyncEarthScopeClient, EarthScopeClient
|
|
8
8
|
from earthscope_sdk.client.user.models import AwsTemporaryCredentials, UserProfile
|
9
9
|
from earthscope_sdk.common.context import SdkContext
|
10
10
|
from earthscope_sdk.config.settings import SdkSettings
|
11
|
+
from tests.util import retries_enabled
|
11
12
|
|
12
13
|
|
13
14
|
class TestSyncClient:
|
@@ -128,3 +129,34 @@ class TestAsyncClient:
|
|
128
129
|
|
129
130
|
c_resp = await client.user.get_aws_credentials(force=True)
|
130
131
|
assert c_resp == c2, "got new credentials when forced"
|
132
|
+
|
133
|
+
@pytest.mark.asyncio
|
134
|
+
async def test_retries(
|
135
|
+
self,
|
136
|
+
mock_settings: SdkSettings,
|
137
|
+
httpx_mock: HTTPXMock,
|
138
|
+
):
|
139
|
+
# reuse context
|
140
|
+
ctx = SdkContext(settings=mock_settings)
|
141
|
+
|
142
|
+
httpx_mock.add_response(429)
|
143
|
+
httpx_mock.add_response(429)
|
144
|
+
|
145
|
+
u = UserProfile(
|
146
|
+
first_name="Jane",
|
147
|
+
last_name="Doe",
|
148
|
+
country_code="US",
|
149
|
+
region_code="CO",
|
150
|
+
institution="EarthScope Consortium",
|
151
|
+
work_sector="non-profit",
|
152
|
+
user_id="user-id-123",
|
153
|
+
primary_email="jane.doe@earthscope.org",
|
154
|
+
created_at="2024-01-01T00:00:00Z",
|
155
|
+
updated_at="2024-03-01T00:00:00Z",
|
156
|
+
)
|
157
|
+
httpx_mock.add_response(json=u.model_dump(mode="json"))
|
158
|
+
|
159
|
+
with retries_enabled(3):
|
160
|
+
async with AsyncEarthScopeClient(ctx=ctx) as client:
|
161
|
+
u_resp = await client.user.get_profile()
|
162
|
+
assert u == u_resp
|
@@ -8,7 +8,12 @@ from pytest import MonkeyPatch
|
|
8
8
|
from earthscope_sdk import __version__
|
9
9
|
from earthscope_sdk.config._compat import _get_legacy_auth_state_path
|
10
10
|
from earthscope_sdk.config.error import ProfileDoesNotExistError
|
11
|
-
from earthscope_sdk.config.models import
|
11
|
+
from earthscope_sdk.config.models import (
|
12
|
+
AuthFlowSettings,
|
13
|
+
HttpRetrySettings,
|
14
|
+
HttpSettings,
|
15
|
+
Tokens,
|
16
|
+
)
|
12
17
|
from earthscope_sdk.config.settings import SdkSettings, _get_config_toml_path
|
13
18
|
|
14
19
|
|
@@ -41,9 +46,21 @@ class TestSdkSettings:
|
|
41
46
|
assert s.http.max_keepalive_connections is None
|
42
47
|
assert s.http.keepalive_expiry.total_seconds() == 5.0
|
43
48
|
assert s.http.timeout_read.total_seconds() == 5.0
|
49
|
+
assert s.http.retry.attempts == 3
|
50
|
+
assert s.http.retry.timeout.total_seconds() == 20.0
|
51
|
+
assert s.http.retry.wait_initial.total_seconds() == 0.1
|
52
|
+
assert s.http.retry.wait_max.total_seconds() == 5.0
|
53
|
+
assert s.http.retry.wait_jitter.total_seconds() == 1.0
|
54
|
+
assert s.http.retry.wait_exp_base == 2
|
44
55
|
assert s.oauth2.client_secret is None
|
45
56
|
assert s.oauth2.access_token is None
|
46
57
|
assert s.oauth2.refresh_token is None
|
58
|
+
assert s.oauth2.retry.attempts == 5
|
59
|
+
assert s.oauth2.retry.timeout.total_seconds() == 30.0
|
60
|
+
assert s.oauth2.retry.wait_initial.total_seconds() == 1.0
|
61
|
+
assert s.oauth2.retry.wait_max.total_seconds() == 5.0
|
62
|
+
assert s.oauth2.retry.wait_jitter.total_seconds() == 3.0
|
63
|
+
assert s.oauth2.retry.wait_exp_base == 2
|
47
64
|
|
48
65
|
def test_profile_does_not_exist_init(self):
|
49
66
|
with pytest.raises(ProfileDoesNotExistError):
|
@@ -228,24 +245,45 @@ class TestSdkSettingsProfiles:
|
|
228
245
|
http.max_keepalive_connections = 11
|
229
246
|
http.timeout_read = 11.1
|
230
247
|
http.keepalive_expiry = 11.1
|
248
|
+
|
249
|
+
http.retry.attempts = 11
|
250
|
+
http.retry.timeout = 11.1
|
251
|
+
http.retry.wait_jitter = 11.1
|
252
|
+
http.retry.wait_initial = 11.1
|
231
253
|
|
232
254
|
[profile.pytest]
|
233
255
|
http.max_connections = 22
|
234
256
|
http.max_keepalive_connections = 22
|
235
257
|
http.keepalive_expiry = 22.2
|
258
|
+
http.retry.timeout = 22.2
|
259
|
+
http.retry.wait_jitter = 22.2
|
260
|
+
http.retry.wait_initial = 22.2
|
236
261
|
""")
|
237
262
|
)
|
238
263
|
|
239
264
|
monkeypatch.setenv("ES_HTTP__MAX_CONNECTIONS", "33")
|
240
265
|
monkeypatch.setenv("ES_HTTP__KEEPALIVE_EXPIRY", "33.3")
|
266
|
+
monkeypatch.setenv("ES_HTTP__RETRY__WAIT_JITTER", "33.3")
|
267
|
+
monkeypatch.setenv("ES_HTTP__RETRY__WAIT_INITIAL", "33.3")
|
241
268
|
|
242
|
-
s = SdkSettings(
|
269
|
+
s = SdkSettings(
|
270
|
+
profile_name="pytest",
|
271
|
+
http=HttpSettings(
|
272
|
+
keepalive_expiry=44.4,
|
273
|
+
retry=HttpRetrySettings(wait_initial=44.4),
|
274
|
+
),
|
275
|
+
)
|
243
276
|
|
244
277
|
assert s.http.keepalive_expiry.total_seconds() == 44.4
|
245
278
|
assert s.http.max_connections == 33
|
246
279
|
assert s.http.max_keepalive_connections == 22
|
247
280
|
assert s.http.timeout_read.total_seconds() == 11.1
|
248
281
|
|
282
|
+
assert s.http.retry.attempts == 11
|
283
|
+
assert s.http.retry.timeout.total_seconds() == 22.2
|
284
|
+
assert s.http.retry.wait_jitter.total_seconds() == 33.3
|
285
|
+
assert s.http.retry.wait_initial.total_seconds() == 44.4
|
286
|
+
|
249
287
|
|
250
288
|
class TestTokens:
|
251
289
|
def test_defaults(self):
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/auth/client_credentials_flow.py
RENAMED
File without changes
|
{earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/auth/device_code_flow.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/user/__init__.py
RENAMED
File without changes
|
{earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk/client/user/_service.py
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
|
{earthscope_sdk-1.0.0b0 → earthscope_sdk-1.0.0b1}/src/earthscope_sdk.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|