datacosmos 0.0.13__tar.gz → 0.0.15__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 datacosmos might be problematic. Click here for more details.
- {datacosmos-0.0.13 → datacosmos-0.0.15}/PKG-INFO +2 -1
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/datacosmos_client.py +76 -47
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos.egg-info/PKG-INFO +2 -1
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos.egg-info/requires.txt +1 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/pyproject.toml +4 -3
- {datacosmos-0.0.13 → datacosmos-0.0.15}/LICENSE.md +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/README.md +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/auth/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/auth/local_token_fetcher.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/auth/token.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/auth/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/auth/factory.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/config.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/constants.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/loaders/yaml_source.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/authentication_config.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/local_user_account_authentication_config.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/m2m_authentication_config.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/no_authentication_config.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/url.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/exceptions/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/exceptions/datacosmos_exception.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/collection/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/collection/collection_client.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/collection/models/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/collection/models/collection_update.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/constants/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/constants/satellite_name_mapping.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/enums/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/enums/processing_level.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/enums/product_type.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/enums/season.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/item_client.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/asset.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/catalog_search_parameters.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/datacosmos_item.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/eo_band.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/item_update.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/raster_band.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/stac_client.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/storage/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/storage/dataclasses/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/storage/dataclasses/upload_path.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/storage/storage_base.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/storage/storage_client.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/storage/uploader.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/check_api_response.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/models/__init__.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/models/datacosmos_error.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/models/datacosmos_response.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/url.py +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos.egg-info/SOURCES.txt +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos.egg-info/dependency_links.txt +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos.egg-info/top_level.txt +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/setup.cfg +0 -0
- {datacosmos-0.0.13 → datacosmos-0.0.15}/tests/test_pass.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datacosmos
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.15
|
|
4
4
|
Summary: A library for interacting with DataCosmos from Python code
|
|
5
5
|
Author-email: Open Cosmos <support@open-cosmos.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -15,6 +15,7 @@ Requires-Dist: pydantic>=2
|
|
|
15
15
|
Requires-Dist: pystac==1.12.1
|
|
16
16
|
Requires-Dist: pyyaml==6.0.2
|
|
17
17
|
Requires-Dist: structlog==24.4.0
|
|
18
|
+
Requires-Dist: tenacity>=8.2.3
|
|
18
19
|
Provides-Extra: dev
|
|
19
20
|
Requires-Dist: black==22.3.0; extra == "dev"
|
|
20
21
|
Requires-Dist: ruff==0.9.5; extra == "dev"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Client to interact with the Datacosmos API with authentication and request handling."""
|
|
2
2
|
|
|
3
|
+
import threading
|
|
3
4
|
from datetime import datetime, timedelta, timezone
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Any, Optional
|
|
@@ -8,6 +9,12 @@ import requests
|
|
|
8
9
|
from oauthlib.oauth2 import BackendApplicationClient
|
|
9
10
|
from requests.exceptions import ConnectionError, HTTPError, RequestException, Timeout
|
|
10
11
|
from requests_oauthlib import OAuth2Session
|
|
12
|
+
from tenacity import (
|
|
13
|
+
retry,
|
|
14
|
+
retry_if_exception_type,
|
|
15
|
+
stop_after_attempt,
|
|
16
|
+
wait_exponential,
|
|
17
|
+
)
|
|
11
18
|
|
|
12
19
|
from datacosmos.config.config import Config
|
|
13
20
|
from datacosmos.exceptions.datacosmos_exception import DatacosmosException
|
|
@@ -16,6 +23,8 @@ from datacosmos.exceptions.datacosmos_exception import DatacosmosException
|
|
|
16
23
|
class DatacosmosClient:
|
|
17
24
|
"""Client to interact with the Datacosmos API with authentication and request handling."""
|
|
18
25
|
|
|
26
|
+
TOKEN_EXPIRY_SKEW_SECONDS = 60
|
|
27
|
+
|
|
19
28
|
def __init__(
|
|
20
29
|
self,
|
|
21
30
|
config: Optional[Config | Any] = None,
|
|
@@ -24,13 +33,13 @@ class DatacosmosClient:
|
|
|
24
33
|
"""Initialize the DatacosmosClient.
|
|
25
34
|
|
|
26
35
|
Args:
|
|
27
|
-
config:
|
|
28
|
-
http_session: Pre-authenticated session
|
|
29
|
-
with 'Authorization: Bearer ...').
|
|
36
|
+
config (Optional[Config]): Configuration object (only needed when SDK creates its own session).
|
|
37
|
+
http_session (Optional[requests.Session]): Pre-authenticated session.
|
|
30
38
|
"""
|
|
31
39
|
self.config = self._coerce_config(config)
|
|
32
40
|
self.token: Optional[str] = None
|
|
33
41
|
self.token_expiry: Optional[datetime] = None
|
|
42
|
+
self._refresh_lock = threading.Lock()
|
|
34
43
|
|
|
35
44
|
if http_session is not None:
|
|
36
45
|
self._init_with_injected_session(http_session)
|
|
@@ -42,7 +51,6 @@ class DatacosmosClient:
|
|
|
42
51
|
# --------------------------- init helpers ---------------------------
|
|
43
52
|
|
|
44
53
|
def _coerce_config(self, cfg: Optional[Config | Any]) -> Config:
|
|
45
|
-
"""Normalize various config inputs into a Config instance."""
|
|
46
54
|
if cfg is None:
|
|
47
55
|
return Config()
|
|
48
56
|
if isinstance(cfg, Config):
|
|
@@ -50,7 +58,7 @@ class DatacosmosClient:
|
|
|
50
58
|
if isinstance(cfg, dict):
|
|
51
59
|
return Config(**cfg)
|
|
52
60
|
try:
|
|
53
|
-
return Config.model_validate(cfg)
|
|
61
|
+
return Config.model_validate(cfg)
|
|
54
62
|
except Exception as e:
|
|
55
63
|
raise DatacosmosException(
|
|
56
64
|
"Invalid config provided to DatacosmosClient"
|
|
@@ -59,7 +67,6 @@ class DatacosmosClient:
|
|
|
59
67
|
def _init_with_injected_session(
|
|
60
68
|
self, http_session: requests.Session | OAuth2Session
|
|
61
69
|
) -> None:
|
|
62
|
-
"""Adopt a caller-provided session and extract token/expiry."""
|
|
63
70
|
self._http_client = http_session
|
|
64
71
|
self._owns_session = False
|
|
65
72
|
|
|
@@ -69,19 +76,15 @@ class DatacosmosClient:
|
|
|
69
76
|
raise DatacosmosException(
|
|
70
77
|
"Failed to extract access token from injected session"
|
|
71
78
|
)
|
|
72
|
-
|
|
73
79
|
self.token_expiry = self._compute_expiry(
|
|
74
|
-
token_data.get("expires_at"),
|
|
75
|
-
token_data.get("expires_in"),
|
|
80
|
+
token_data.get("expires_at"), token_data.get("expires_in")
|
|
76
81
|
)
|
|
77
82
|
|
|
78
83
|
def _extract_token_data(
|
|
79
84
|
self, http_session: requests.Session | OAuth2Session
|
|
80
85
|
) -> dict:
|
|
81
|
-
"""Return {'access_token', 'expires_at'?, 'expires_in'?} from the session."""
|
|
82
86
|
if isinstance(http_session, OAuth2Session):
|
|
83
87
|
return getattr(http_session, "token", {}) or {}
|
|
84
|
-
|
|
85
88
|
if isinstance(http_session, requests.Session):
|
|
86
89
|
auth_header = http_session.headers.get("Authorization", "")
|
|
87
90
|
if not auth_header.startswith("Bearer "):
|
|
@@ -89,7 +92,6 @@ class DatacosmosClient:
|
|
|
89
92
|
"Injected requests.Session must include a 'Bearer' token in its headers"
|
|
90
93
|
)
|
|
91
94
|
return {"access_token": auth_header.split(" ", 1)[1]}
|
|
92
|
-
|
|
93
95
|
raise DatacosmosException(f"Unsupported session type: {type(http_session)}")
|
|
94
96
|
|
|
95
97
|
def _compute_expiry(
|
|
@@ -97,7 +99,6 @@ class DatacosmosClient:
|
|
|
97
99
|
expires_at: Optional[datetime | int | float],
|
|
98
100
|
expires_in: Optional[int | float],
|
|
99
101
|
) -> Optional[datetime]:
|
|
100
|
-
"""Normalize expiry inputs to an absolute UTC datetime (or None)."""
|
|
101
102
|
if isinstance(expires_at, datetime):
|
|
102
103
|
return expires_at
|
|
103
104
|
if isinstance(expires_at, (int, float)):
|
|
@@ -106,51 +107,33 @@ class DatacosmosClient:
|
|
|
106
107
|
try:
|
|
107
108
|
return datetime.now(timezone.utc) + timedelta(seconds=int(expires_in))
|
|
108
109
|
except (TypeError, ValueError):
|
|
110
|
+
# Unknown/invalid expiry -> mark as unknown so refresh logic kicks in
|
|
109
111
|
return None
|
|
110
112
|
return None
|
|
111
113
|
|
|
112
114
|
# --------------------------- auth/session ---------------------------
|
|
113
115
|
|
|
114
116
|
def _authenticate_and_initialize_client(self) -> requests.Session:
|
|
115
|
-
"""Authenticate and initialize the HTTP client with a valid token."""
|
|
116
117
|
auth = self.config.authentication
|
|
117
118
|
auth_type = getattr(auth, "type", "m2m")
|
|
118
|
-
|
|
119
119
|
if auth_type == "m2m":
|
|
120
120
|
return self.__build_m2m_session()
|
|
121
|
-
|
|
122
121
|
if auth_type == "local":
|
|
123
122
|
return self.__build_local_session()
|
|
124
|
-
|
|
125
123
|
raise DatacosmosException(f"Unsupported authentication type: {auth_type}")
|
|
126
124
|
|
|
127
|
-
def _refresh_token_if_needed(self):
|
|
128
|
-
"""Refresh the token if it has expired (only if SDK created it)."""
|
|
129
|
-
if not getattr(self, "_owns_session", False):
|
|
130
|
-
return
|
|
131
|
-
now = datetime.now(timezone.utc)
|
|
132
|
-
# Treat missing token or missing expiry as 'needs refresh'
|
|
133
|
-
if (
|
|
134
|
-
(not self.token)
|
|
135
|
-
or (self.token_expiry is None)
|
|
136
|
-
or (self.token_expiry <= now)
|
|
137
|
-
):
|
|
138
|
-
self._http_client = self._authenticate_and_initialize_client()
|
|
139
|
-
|
|
140
125
|
def __build_m2m_session(self) -> requests.Session:
|
|
141
126
|
"""Client Credentials (M2M) flow using requests-oauthlib."""
|
|
142
127
|
auth = self.config.authentication
|
|
143
128
|
try:
|
|
144
129
|
client = BackendApplicationClient(client_id=auth.client_id)
|
|
145
130
|
oauth_session = OAuth2Session(client=client)
|
|
146
|
-
|
|
147
131
|
token_response = oauth_session.fetch_token(
|
|
148
132
|
token_url=auth.token_url,
|
|
149
133
|
client_id=auth.client_id,
|
|
150
134
|
client_secret=auth.client_secret,
|
|
151
135
|
audience=auth.audience,
|
|
152
136
|
)
|
|
153
|
-
|
|
154
137
|
self.token = token_response["access_token"]
|
|
155
138
|
expires_at = token_response.get("expires_at")
|
|
156
139
|
if isinstance(expires_at, (int, float)):
|
|
@@ -159,11 +142,9 @@ class DatacosmosClient:
|
|
|
159
142
|
self.token_expiry = datetime.now(timezone.utc) + timedelta(
|
|
160
143
|
seconds=int(token_response.get("expires_in", 3600))
|
|
161
144
|
)
|
|
162
|
-
|
|
163
145
|
http_client = requests.Session()
|
|
164
146
|
http_client.headers.update({"Authorization": f"Bearer {self.token}"})
|
|
165
147
|
return http_client
|
|
166
|
-
|
|
167
148
|
except (HTTPError, ConnectionError, Timeout) as e:
|
|
168
149
|
raise DatacosmosException(f"Authentication failed: {e}") from e
|
|
169
150
|
except RequestException as e:
|
|
@@ -195,38 +176,86 @@ class DatacosmosClient:
|
|
|
195
176
|
|
|
196
177
|
http_client = requests.Session()
|
|
197
178
|
http_client.headers.update({"Authorization": f"Bearer {self.token}"})
|
|
198
|
-
|
|
199
|
-
# keep for potential reuse in refresh path (optional)
|
|
200
179
|
self._local_token_fetcher = fetcher
|
|
201
180
|
return http_client
|
|
202
181
|
|
|
182
|
+
# --------------------------- refresh logic ---------------------------
|
|
183
|
+
|
|
184
|
+
def _needs_refresh(self) -> bool:
|
|
185
|
+
if not getattr(self, "_owns_session", False):
|
|
186
|
+
return False
|
|
187
|
+
if not self.token or self.token_expiry is None:
|
|
188
|
+
return True
|
|
189
|
+
return (self.token_expiry - datetime.now(timezone.utc)) <= timedelta(
|
|
190
|
+
seconds=self.TOKEN_EXPIRY_SKEW_SECONDS
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def _refresh_now(self) -> None:
|
|
194
|
+
"""Force refresh.
|
|
195
|
+
|
|
196
|
+
In case of local auth it uses LocalTokenFetcher (non-interactive refresh/cached token).
|
|
197
|
+
In case of m2m auth it re-runs client-credentials flow.
|
|
198
|
+
"""
|
|
199
|
+
with self._refresh_lock:
|
|
200
|
+
if not self._needs_refresh():
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
auth_type = getattr(self.config.authentication, "type", "m2m")
|
|
204
|
+
if auth_type == "local" and hasattr(self, "_local_token_fetcher"):
|
|
205
|
+
tok = self._local_token_fetcher.get_token()
|
|
206
|
+
self.token = tok.access_token
|
|
207
|
+
self.token_expiry = datetime.fromtimestamp(
|
|
208
|
+
tok.expires_at, tz=timezone.utc
|
|
209
|
+
)
|
|
210
|
+
self._http_client.headers.update(
|
|
211
|
+
{"Authorization": f"Bearer {self.token}"}
|
|
212
|
+
)
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
# default/m2m:
|
|
216
|
+
self._http_client = self.__build_m2m_session()
|
|
217
|
+
|
|
218
|
+
def _refresh_token_if_needed(self) -> None:
|
|
219
|
+
if self._needs_refresh():
|
|
220
|
+
self._refresh_now()
|
|
221
|
+
|
|
203
222
|
# --------------------------- request API ---------------------------
|
|
204
223
|
|
|
224
|
+
@retry(
|
|
225
|
+
stop=stop_after_attempt(5),
|
|
226
|
+
wait=wait_exponential(multiplier=1, min=2, max=20),
|
|
227
|
+
retry=retry_if_exception_type((ConnectionError, Timeout)),
|
|
228
|
+
)
|
|
205
229
|
def request(
|
|
206
230
|
self, method: str, url: str, *args: Any, **kwargs: Any
|
|
207
231
|
) -> requests.Response:
|
|
208
|
-
"""Send an HTTP request using the authenticated session."""
|
|
232
|
+
"""Send an HTTP request using the authenticated session (with auto-refresh and retries)."""
|
|
209
233
|
self._refresh_token_if_needed()
|
|
210
234
|
try:
|
|
211
235
|
response = self._http_client.request(method, url, *args, **kwargs)
|
|
212
236
|
response.raise_for_status()
|
|
213
237
|
return response
|
|
214
238
|
except HTTPError as e:
|
|
239
|
+
status = getattr(e.response, "status_code", None)
|
|
240
|
+
if status in (401, 403) and getattr(self, "_owns_session", False):
|
|
241
|
+
# token likely expired/invalid — refresh once and retry
|
|
242
|
+
self._refresh_now()
|
|
243
|
+
retry_response = self._http_client.request(method, url, *args, **kwargs)
|
|
244
|
+
try:
|
|
245
|
+
retry_response.raise_for_status()
|
|
246
|
+
return retry_response
|
|
247
|
+
except HTTPError as e:
|
|
248
|
+
raise DatacosmosException(
|
|
249
|
+
f"HTTP error during {method.upper()} request to {url} after refresh",
|
|
250
|
+
response=e.response,
|
|
251
|
+
) from e
|
|
215
252
|
raise DatacosmosException(
|
|
216
253
|
f"HTTP error during {method.upper()} request to {url}",
|
|
217
|
-
response=e
|
|
218
|
-
) from e
|
|
219
|
-
except ConnectionError as e:
|
|
220
|
-
raise DatacosmosException(
|
|
221
|
-
f"Connection error during {method.upper()} request to {url}: {str(e)}"
|
|
222
|
-
) from e
|
|
223
|
-
except Timeout as e:
|
|
224
|
-
raise DatacosmosException(
|
|
225
|
-
f"Request timeout during {method.upper()} request to {url}: {str(e)}"
|
|
254
|
+
response=getattr(e, "response", None),
|
|
226
255
|
) from e
|
|
227
256
|
except RequestException as e:
|
|
228
257
|
raise DatacosmosException(
|
|
229
|
-
f"Unexpected request failure during {method.upper()} request to {url}: {
|
|
258
|
+
f"Unexpected request failure during {method.upper()} request to {url}: {e}"
|
|
230
259
|
) from e
|
|
231
260
|
|
|
232
261
|
def get(self, url: str, *args: Any, **kwargs: Any) -> requests.Response:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datacosmos
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.15
|
|
4
4
|
Summary: A library for interacting with DataCosmos from Python code
|
|
5
5
|
Author-email: Open Cosmos <support@open-cosmos.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -15,6 +15,7 @@ Requires-Dist: pydantic>=2
|
|
|
15
15
|
Requires-Dist: pystac==1.12.1
|
|
16
16
|
Requires-Dist: pyyaml==6.0.2
|
|
17
17
|
Requires-Dist: structlog==24.4.0
|
|
18
|
+
Requires-Dist: tenacity>=8.2.3
|
|
18
19
|
Provides-Extra: dev
|
|
19
20
|
Requires-Dist: black==22.3.0; extra == "dev"
|
|
20
21
|
Requires-Dist: ruff==0.9.5; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "datacosmos"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.15"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Open Cosmos", email="support@open-cosmos.com" },
|
|
10
10
|
]
|
|
@@ -22,7 +22,8 @@ dependencies = [
|
|
|
22
22
|
"pydantic>=2",
|
|
23
23
|
"pystac==1.12.1",
|
|
24
24
|
"pyyaml==6.0.2",
|
|
25
|
-
"structlog==24.4.0"
|
|
25
|
+
"structlog==24.4.0",
|
|
26
|
+
"tenacity>=8.2.3"
|
|
26
27
|
]
|
|
27
28
|
|
|
28
29
|
[project.optional-dependencies]
|
|
@@ -51,4 +52,4 @@ multi_line_output = 3
|
|
|
51
52
|
include_trailing_comma = true
|
|
52
53
|
force_grid_wrap = 0
|
|
53
54
|
use_parentheses = true
|
|
54
|
-
line_length = 88
|
|
55
|
+
line_length = 88
|
|
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
|
{datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/m2m_authentication_config.py
RENAMED
|
File without changes
|
{datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/config/models/no_authentication_config.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
|
{datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/collection/models/collection_update.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
|
{datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/stac/item/models/catalog_search_parameters.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
|
|
File without changes
|
|
File without changes
|
{datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/check_api_response.py
RENAMED
|
File without changes
|
|
File without changes
|
{datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/models/datacosmos_error.py
RENAMED
|
File without changes
|
{datacosmos-0.0.13 → datacosmos-0.0.15}/datacosmos/utils/http_response/models/datacosmos_response.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|