earthscope-sdk 0.2.1__py3-none-any.whl → 1.0.0b0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- earthscope_sdk/__init__.py +5 -1
- earthscope_sdk/auth/auth_flow.py +224 -347
- earthscope_sdk/auth/client_credentials_flow.py +46 -156
- earthscope_sdk/auth/device_code_flow.py +154 -207
- earthscope_sdk/auth/error.py +46 -0
- earthscope_sdk/client/__init__.py +3 -0
- earthscope_sdk/client/_client.py +35 -0
- earthscope_sdk/client/user/_base.py +39 -0
- earthscope_sdk/client/user/_service.py +94 -0
- earthscope_sdk/client/user/models.py +53 -0
- earthscope_sdk/common/__init__.py +0 -0
- earthscope_sdk/common/_sync_runner.py +141 -0
- earthscope_sdk/common/client.py +99 -0
- earthscope_sdk/common/context.py +174 -0
- earthscope_sdk/common/service.py +54 -0
- earthscope_sdk/config/__init__.py +0 -0
- earthscope_sdk/config/_compat.py +148 -0
- earthscope_sdk/config/_util.py +48 -0
- earthscope_sdk/config/error.py +4 -0
- earthscope_sdk/config/models.py +208 -0
- earthscope_sdk/config/settings.py +284 -0
- {earthscope_sdk-0.2.1.dist-info → earthscope_sdk-1.0.0b0.dist-info}/METADATA +144 -123
- earthscope_sdk-1.0.0b0.dist-info/RECORD +28 -0
- {earthscope_sdk-0.2.1.dist-info → earthscope_sdk-1.0.0b0.dist-info}/WHEEL +1 -1
- earthscope_sdk/user/user.py +0 -24
- earthscope_sdk-0.2.1.dist-info/RECORD +0 -12
- /earthscope_sdk/{user → client/user}/__init__.py +0 -0
- {earthscope_sdk-0.2.1.dist-info → earthscope_sdk-1.0.0b0.dist-info}/LICENSE +0 -0
- {earthscope_sdk-0.2.1.dist-info → earthscope_sdk-1.0.0b0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,284 @@
|
|
1
|
+
from contextlib import suppress
|
2
|
+
from functools import cached_property
|
3
|
+
from typing import Annotated, Any, Type
|
4
|
+
|
5
|
+
from pydantic import Field, TypeAdapter, ValidationError, field_validator
|
6
|
+
from pydantic_settings import (
|
7
|
+
BaseSettings,
|
8
|
+
InitSettingsSource,
|
9
|
+
PydanticBaseSettingsSource,
|
10
|
+
SettingsConfigDict,
|
11
|
+
TomlConfigSettingsSource,
|
12
|
+
)
|
13
|
+
|
14
|
+
from earthscope_sdk.config._compat import LegacyEarthScopeCLISettingsSource
|
15
|
+
from earthscope_sdk.config._util import deep_merge, get_config_dir, slugify
|
16
|
+
from earthscope_sdk.config.error import ProfileDoesNotExistError
|
17
|
+
from earthscope_sdk.config.models import SdkBaseSettings, Tokens
|
18
|
+
|
19
|
+
_DEFAULT_PROFILE = "default"
|
20
|
+
"""Default profile name"""
|
21
|
+
|
22
|
+
_LOCAL_APP_DIR = get_config_dir(app_name="earthscope")
|
23
|
+
"""Local SDK application directory"""
|
24
|
+
|
25
|
+
|
26
|
+
def _get_config_toml_path():
|
27
|
+
"""
|
28
|
+
Local SDK configuration file
|
29
|
+
"""
|
30
|
+
return _LOCAL_APP_DIR / "config.toml"
|
31
|
+
|
32
|
+
|
33
|
+
def _get_profile_dir(profile_name: str):
|
34
|
+
"""
|
35
|
+
Retrieve the local SDK directory for the named profile
|
36
|
+
"""
|
37
|
+
return _LOCAL_APP_DIR / slugify(profile_name)
|
38
|
+
|
39
|
+
|
40
|
+
def _get_profile_tokens_file(profile_name: str):
|
41
|
+
"""
|
42
|
+
Retrieve the local SDK tokens file for the named profile
|
43
|
+
"""
|
44
|
+
profile_dir = _get_profile_dir(profile_name)
|
45
|
+
return profile_dir / "tokens.json"
|
46
|
+
|
47
|
+
|
48
|
+
class _SdkGlobalSettings(BaseSettings):
|
49
|
+
"""
|
50
|
+
EarthScope SDK global settings; not for direct use.
|
51
|
+
|
52
|
+
This class only loads settings from config.toml.
|
53
|
+
"""
|
54
|
+
|
55
|
+
defaults: Annotated[
|
56
|
+
SdkBaseSettings,
|
57
|
+
Field(validation_alias="default"),
|
58
|
+
] = {}
|
59
|
+
|
60
|
+
profile_settings_by_name: Annotated[
|
61
|
+
dict[str, SdkBaseSettings],
|
62
|
+
Field(validation_alias="profile"),
|
63
|
+
] = {}
|
64
|
+
|
65
|
+
model_config = SettingsConfigDict(
|
66
|
+
extra="ignore",
|
67
|
+
frozen=True,
|
68
|
+
nested_model_default_partial_update=True,
|
69
|
+
populate_by_name=True,
|
70
|
+
)
|
71
|
+
|
72
|
+
def get_merged(self, profile_name: str):
|
73
|
+
"""
|
74
|
+
Get profile-specific settings merged with defaults
|
75
|
+
"""
|
76
|
+
try:
|
77
|
+
p = self.profile_settings_by_name[profile_name]
|
78
|
+
except KeyError:
|
79
|
+
raise ProfileDoesNotExistError(f"Profile '{profile_name}' does not exist")
|
80
|
+
|
81
|
+
defaults = self.defaults.model_dump(
|
82
|
+
exclude_none=True,
|
83
|
+
exclude_unset=True,
|
84
|
+
exclude_defaults=True,
|
85
|
+
)
|
86
|
+
profile_specific = p.model_dump(
|
87
|
+
exclude_defaults=True, # defer to top-level field defaults
|
88
|
+
exclude_unset=True, # only keep explicitly set fields
|
89
|
+
exclude_none=True,
|
90
|
+
)
|
91
|
+
|
92
|
+
# order is imporant; profile-specific args override default args
|
93
|
+
return deep_merge(defaults, profile_specific, profile_name=profile_name)
|
94
|
+
|
95
|
+
@field_validator("profile_settings_by_name", mode="before")
|
96
|
+
@classmethod
|
97
|
+
def ensure_default_profile(cls, v: dict[str, dict]):
|
98
|
+
"""
|
99
|
+
Ensure the default profile exists
|
100
|
+
"""
|
101
|
+
v.setdefault(_DEFAULT_PROFILE, {})
|
102
|
+
return v
|
103
|
+
|
104
|
+
@classmethod
|
105
|
+
def settings_customise_sources(cls, settings_cls: Type[BaseSettings], **_):
|
106
|
+
"""
|
107
|
+
Override the loading chain to exclusively check ~/.earthscope/config.toml
|
108
|
+
"""
|
109
|
+
# we get the file path dynamically to allow monkeypatching for tests
|
110
|
+
toml_path = _get_config_toml_path()
|
111
|
+
toml_settings = TomlConfigSettingsSource(settings_cls, toml_path)
|
112
|
+
|
113
|
+
return (toml_settings,)
|
114
|
+
|
115
|
+
|
116
|
+
class _GlobalSettingsSource(PydanticBaseSettingsSource):
|
117
|
+
"""
|
118
|
+
This SettingsSource facilitates falling back to settings configured in config.toml
|
119
|
+
and state in tokens.json when loading the configuration chain in SdkSettings.
|
120
|
+
|
121
|
+
Consolidates merged profile state with state loaded from a profile's tokens.json file
|
122
|
+
"""
|
123
|
+
|
124
|
+
def __init__(self, settings_cls, *keys: str):
|
125
|
+
super().__init__(settings_cls)
|
126
|
+
self._keys = keys
|
127
|
+
|
128
|
+
def __call__(self):
|
129
|
+
# Extract profile
|
130
|
+
profile_name = _DEFAULT_PROFILE
|
131
|
+
for k in self._keys:
|
132
|
+
with suppress(KeyError):
|
133
|
+
profile_name = self._current_state[k]
|
134
|
+
break
|
135
|
+
|
136
|
+
# Load global merged settings
|
137
|
+
merged = _SdkGlobalSettings().get_merged(profile_name)
|
138
|
+
|
139
|
+
# Load from tokens.json
|
140
|
+
tokens_json = {}
|
141
|
+
with suppress(FileNotFoundError, ValidationError):
|
142
|
+
raw = _get_profile_tokens_file(profile_name).read_bytes()
|
143
|
+
tokens = Tokens.model_validate_json(raw)
|
144
|
+
tokens_json = tokens.model_dump(
|
145
|
+
exclude_none=True,
|
146
|
+
exclude_unset=True,
|
147
|
+
exclude_defaults=True,
|
148
|
+
)
|
149
|
+
|
150
|
+
# order is imporant; init args override tokens.json args
|
151
|
+
return deep_merge({"oauth2": tokens_json}, merged)
|
152
|
+
|
153
|
+
def __repr__(self) -> str:
|
154
|
+
return f"{self.__class__.__name__}()"
|
155
|
+
|
156
|
+
def get_field_value(self, *args, **kwargs): ... # unused abstract method
|
157
|
+
|
158
|
+
|
159
|
+
class _InitSettingsWithoutDefaultSource(InitSettingsSource):
|
160
|
+
"""
|
161
|
+
InitSettingsSource behavior but excludes default values & unset fields
|
162
|
+
to allow values from lower-precedence settings sources to bubble up.
|
163
|
+
"""
|
164
|
+
|
165
|
+
def __init__(self, other: InitSettingsSource):
|
166
|
+
super().__init__(
|
167
|
+
other.settings_cls,
|
168
|
+
other.init_kwargs,
|
169
|
+
other.nested_model_default_partial_update,
|
170
|
+
)
|
171
|
+
self.dict_adapter = TypeAdapter(dict[str, Any])
|
172
|
+
|
173
|
+
def __call__(self) -> dict[str, Any]:
|
174
|
+
return self.dict_adapter.dump_python(
|
175
|
+
self.init_kwargs,
|
176
|
+
exclude_defaults=True,
|
177
|
+
exclude_unset=True,
|
178
|
+
)
|
179
|
+
|
180
|
+
|
181
|
+
class SdkSettings(SdkBaseSettings, BaseSettings):
|
182
|
+
"""
|
183
|
+
EarthScope SDK settings.
|
184
|
+
|
185
|
+
Use `profile_name` to utilize a named profile from `~/.earthscope/config.toml`
|
186
|
+
|
187
|
+
Settings loading chain (order of precedence):
|
188
|
+
1. arguments passed to constructor
|
189
|
+
2. environment variables
|
190
|
+
3. dotenv variables
|
191
|
+
4. ~/.earthscope/config.toml
|
192
|
+
"""
|
193
|
+
|
194
|
+
profile_name: Annotated[
|
195
|
+
str,
|
196
|
+
Field(validation_alias="es_profile"),
|
197
|
+
# NOTE: aliases do not include the env_prefix by default; add it explicitly
|
198
|
+
]
|
199
|
+
|
200
|
+
model_config = SettingsConfigDict(
|
201
|
+
env_nested_delimiter="__",
|
202
|
+
env_prefix="ES_",
|
203
|
+
extra="ignore",
|
204
|
+
frozen=True,
|
205
|
+
nested_model_default_partial_update=True,
|
206
|
+
populate_by_name=True,
|
207
|
+
)
|
208
|
+
|
209
|
+
@cached_property
|
210
|
+
def profile_dir(self):
|
211
|
+
"""
|
212
|
+
The path to the local SDK directory for the named profile
|
213
|
+
"""
|
214
|
+
return _get_profile_dir(self.profile_name)
|
215
|
+
|
216
|
+
@cached_property
|
217
|
+
def tokens_file(self):
|
218
|
+
"""
|
219
|
+
The path to the tokens file for the named profile
|
220
|
+
"""
|
221
|
+
return _get_profile_tokens_file(self.profile_name)
|
222
|
+
|
223
|
+
def delete_tokens(self, missing_ok=False):
|
224
|
+
"""
|
225
|
+
Delete tokens from this named profile's state
|
226
|
+
|
227
|
+
Args:
|
228
|
+
missing_ok: Whether or not to throw an error if the file does not exist
|
229
|
+
|
230
|
+
Returns:
|
231
|
+
this auth flow
|
232
|
+
"""
|
233
|
+
self.tokens_file.unlink(missing_ok=missing_ok)
|
234
|
+
return self
|
235
|
+
|
236
|
+
def write_tokens(self, tokens: Tokens):
|
237
|
+
"""
|
238
|
+
Write tokens to this named profile's state
|
239
|
+
|
240
|
+
Args:
|
241
|
+
tokens: the tokens to persist in local storage
|
242
|
+
"""
|
243
|
+
body = tokens.model_dump_json(
|
244
|
+
include=Tokens.model_fields.keys(),
|
245
|
+
indent=2,
|
246
|
+
context="plaintext", # write SecretStr's actual values in plaintext
|
247
|
+
exclude_none=True,
|
248
|
+
)
|
249
|
+
self.tokens_file.parent.mkdir(parents=True, exist_ok=True)
|
250
|
+
self.tokens_file.write_text(body)
|
251
|
+
|
252
|
+
@classmethod
|
253
|
+
def settings_customise_sources(
|
254
|
+
cls,
|
255
|
+
settings_cls: Type[BaseSettings],
|
256
|
+
init_settings: InitSettingsSource,
|
257
|
+
env_settings: PydanticBaseSettingsSource,
|
258
|
+
dotenv_settings: PydanticBaseSettingsSource,
|
259
|
+
**_,
|
260
|
+
):
|
261
|
+
"""
|
262
|
+
Override the loading chain to additionally check ~/.earthscope/config.toml
|
263
|
+
"""
|
264
|
+
|
265
|
+
# Init settings, but without defaults & unset values
|
266
|
+
init_settings = _InitSettingsWithoutDefaultSource(init_settings)
|
267
|
+
|
268
|
+
# Check for all the ways profile name may be supplied
|
269
|
+
alias = SdkSettings.model_fields["profile_name"].validation_alias
|
270
|
+
global_settings = _GlobalSettingsSource(settings_cls, "profile_name", alias)
|
271
|
+
|
272
|
+
# Compatibility with earthscope-cli v0.x.x state:
|
273
|
+
# If we find this file, we only care about the access and refresh tokens
|
274
|
+
keep_keys = {"access_token", "refresh_token"}
|
275
|
+
legacy_settings = LegacyEarthScopeCLISettingsSource(settings_cls, *keep_keys)
|
276
|
+
|
277
|
+
# Order of precedence
|
278
|
+
return (
|
279
|
+
init_settings,
|
280
|
+
env_settings,
|
281
|
+
dotenv_settings,
|
282
|
+
global_settings,
|
283
|
+
legacy_settings,
|
284
|
+
)
|
@@ -1,9 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: earthscope-sdk
|
3
|
-
Version: 0.
|
3
|
+
Version: 1.0.0b0
|
4
4
|
Summary: An SDK for EarthScope API
|
5
|
-
Author-email: EarthScope <
|
6
|
-
License:
|
5
|
+
Author-email: EarthScope <data-help@earthscope.org>
|
6
|
+
License: Apache License
|
7
7
|
Version 2.0, January 2004
|
8
8
|
http://www.apache.org/licenses/
|
9
9
|
|
@@ -210,138 +210,159 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
210
210
|
Classifier: Programming Language :: Python
|
211
211
|
Classifier: Programming Language :: Python :: 3
|
212
212
|
Classifier: Operating System :: OS Independent
|
213
|
-
Requires-Python: >=3.
|
213
|
+
Requires-Python: >=3.9
|
214
214
|
Description-Content-Type: text/markdown
|
215
215
|
License-File: LICENSE
|
216
|
-
Requires-Dist:
|
217
|
-
Requires-Dist:
|
216
|
+
Requires-Dist: httpx>=0.27.0
|
217
|
+
Requires-Dist: pydantic-settings[toml]>=2.7.0
|
218
218
|
Provides-Extra: dev
|
219
|
-
Requires-Dist:
|
220
|
-
Requires-Dist:
|
221
|
-
Requires-Dist:
|
222
|
-
Requires-Dist:
|
223
|
-
Requires-Dist:
|
224
|
-
Requires-Dist:
|
219
|
+
Requires-Dist: bumpver; extra == "dev"
|
220
|
+
Requires-Dist: build; extra == "dev"
|
221
|
+
Requires-Dist: pytest; extra == "dev"
|
222
|
+
Requires-Dist: twine; extra == "dev"
|
223
|
+
Requires-Dist: pip-tools; extra == "dev"
|
224
|
+
Requires-Dist: pytest-httpx; extra == "dev"
|
225
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
226
|
+
Requires-Dist: ruff; extra == "dev"
|
225
227
|
|
226
228
|
# EarthScope SDK
|
227
229
|
|
228
|
-
An SDK for
|
230
|
+
An SDK for interacting with EarthScope's APIs
|
229
231
|
|
230
232
|
## Getting Started
|
231
233
|
|
232
|
-
###
|
234
|
+
### Installation
|
233
235
|
|
234
|
-
|
236
|
+
Install from PyPI
|
235
237
|
|
236
|
-
|
237
|
-
|
238
|
-
|
238
|
+
```shell
|
239
|
+
pip install earthscope-sdk
|
240
|
+
```
|
241
|
+
|
242
|
+
### Usage
|
243
|
+
|
244
|
+
For detailed usage options and examples, visit [our usage docs](docs/usage.md).
|
245
|
+
|
246
|
+
```py
|
247
|
+
# Import and create a client
|
248
|
+
from earthscope_sdk import EarthScopeClient
|
249
|
+
|
250
|
+
client = EarthScopeClient()
|
251
|
+
|
252
|
+
# Example client method usage; retrieve your user profile
|
253
|
+
profile = client.user.get_profile()
|
254
|
+
print(profile)
|
255
|
+
|
256
|
+
# Client cleanup
|
257
|
+
client.close()
|
258
|
+
```
|
259
|
+
|
260
|
+
#### Async Usage
|
261
|
+
|
262
|
+
There is also an `async` client available
|
263
|
+
|
264
|
+
```py
|
265
|
+
import asyncio
|
266
|
+
from earthscope_sdk import AsyncEarthScopeClient
|
267
|
+
|
268
|
+
async def main():
|
269
|
+
client = AsyncEarthScopeClient()
|
270
|
+
|
271
|
+
profile = await client.user.get_profile()
|
272
|
+
print(profile)
|
273
|
+
|
274
|
+
await client.close()
|
275
|
+
|
276
|
+
asyncio.run(main())
|
277
|
+
```
|
278
|
+
|
279
|
+
#### Context Managers
|
280
|
+
|
281
|
+
Client classes can also be used as context managers to ensure resource cleanup occurs.
|
282
|
+
|
283
|
+
```py
|
284
|
+
# sync
|
285
|
+
with EarthScopeClient() as client:
|
286
|
+
client.user.get_profile()
|
287
|
+
|
288
|
+
# async
|
289
|
+
async with AsyncEarthScopeClient() as client:
|
290
|
+
await client.user.get_profile()
|
291
|
+
```
|
292
|
+
|
293
|
+
## Bootstrapping Authentication
|
294
|
+
|
295
|
+
There are a few methods of bootstrapping authentication for the SDK.
|
296
|
+
|
297
|
+
Once refreshable credentials are available to the SDK, it will transparently handle access token refresh on your behalf.
|
298
|
+
|
299
|
+
### Same host
|
300
|
+
|
301
|
+
If you have the [EarthScope CLI](TODO) installed on the same host that is running your application which uses `earthscope-sdk`, you can simply log in using the CLI. The CLI shares credentials and configuration with this SDK (when running on the same host).
|
302
|
+
|
303
|
+
Running `es login` will open your browser and prompt you to log in to your EarthScope account.
|
304
|
+
|
305
|
+
```console
|
306
|
+
$ es login
|
307
|
+
Attempting to automatically open the SSO authorization page in your default browser.
|
308
|
+
If the browser does not open or you wish to use a different device to authorize this request, open the following URL:
|
309
|
+
|
310
|
+
https://login.earthscope.org/activate?user_code=ABCD-EFGH
|
311
|
+
|
312
|
+
Successful login! Access token expires at 2024-12-27 18:50:37+00:00
|
313
|
+
```
|
314
|
+
|
315
|
+
Now when you run your application, `earthscope-sdk` will find your credentials.
|
316
|
+
|
317
|
+
### Different hosts
|
318
|
+
|
319
|
+
Sometimes your workload runs on different hosts than your main workstation and you cannot feasibly "log in" on all of them. For example, maybe you're running many containers in your workload.
|
320
|
+
|
321
|
+
You can still use the [EarthScope CLI](TODO) to facilitate auth for applications on other machines.
|
322
|
+
|
323
|
+
1. Use the CLI on your primary workstation [as described above](#same-host) to log in.
|
324
|
+
|
325
|
+
1. Use the CLI to retrieve your refresh token.
|
326
|
+
|
327
|
+
```console
|
328
|
+
$ es user get-refresh-token
|
329
|
+
<your-refresh-token>
|
239
330
|
```
|
240
331
|
|
241
|
-
|
332
|
+
> **Note: your refresh token should be treated as a secret credential. Anyone with a valid refresh token can use it to continually retrieve new access tokens on your behalf**.
|
333
|
+
|
334
|
+
1. Pass this refresh token to all the hosts needing auth for the `earthscope-sdk`. For example, inject the `ES_OAUTH2__REFRESH_TOKEN` environment variable on these hosts.
|
242
335
|
|
243
336
|
```shell
|
244
|
-
|
337
|
+
export ES_OAUTH2__REFRESH_TOKEN="<your-refresh-token>"
|
245
338
|
```
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
where client_credentials contains the machine-to-machine `client_id` and `client_secret`.
|
277
|
-
|
278
|
-
These values are all obtained from [Auth0](https://manage.auth0.com/).
|
279
|
-
<br/><br/>
|
280
|
-
4. **Use the SDK**
|
281
|
-
\
|
282
|
-
You can now use the subclasses to define actions such as logging in/out, retrieving or refreshing access tokens, etc...
|
283
|
-
\
|
284
|
-
**NOTE: Never share your access token or refresh tokens**
|
285
|
-
|
286
|
-
Additionally, once the subclasses have been instantiated, you can pass your access token as a parameter to retrieve
|
287
|
-
your user/anonymous user information using the earthscope_sdk.user.user *get_user* and *lookup_anon* functions.
|
288
|
-
|
289
|
-
|
290
|
-
5. **Example usage:**
|
291
|
-
\
|
292
|
-
Note: To see an example of an application using this SDK (and creating custom subclass), check out the [EarthScope CLI Repository](https://gitlab.com/earthscope/public/earthscope-cli).
|
293
|
-
<br/><br/>
|
294
|
-
How to use the existing simple subclass for device code flow:
|
295
|
-
\
|
296
|
-
*simple example python code*:
|
297
|
-
```
|
298
|
-
import requests
|
299
|
-
from pathlib import Path
|
300
|
-
|
301
|
-
from earthscope_sdk.auth.device_code_flow import DeviceCodeFlowSimple
|
302
|
-
from earthscope_sdk.auth.auth_flow import NoTokensError
|
303
|
-
|
304
|
-
# choose where you want the token saved - the default file name is sso_tokens.json
|
305
|
-
# if you want to keep the default name, set the path to a directory. Include a file name to rename.
|
306
|
-
token_path = "/Users/my_user/token_dir"
|
307
|
-
|
308
|
-
url = "https://data.unavco.org/path/to/data_file"
|
309
|
-
# example: "https://data.unavco.org/archive/gnss/rinex/obs/2022/298/ar272980.22d.Z"
|
310
|
-
|
311
|
-
# instantiate the device code flow subclass
|
312
|
-
device_flow = DeviceCodeFlowSimple(Path(token_path))
|
313
|
-
try:
|
314
|
-
# get access token from local path
|
315
|
-
device_flow.get_access_token_refresh_if_necessary()
|
316
|
-
except NoTokensError:
|
317
|
-
# if no token was found locally, do the device code flow
|
318
|
-
device_flow.do_flow()
|
319
|
-
token = device_flow.access_token
|
320
|
-
|
321
|
-
# request a file and provide the token in the Authorization header
|
322
|
-
file_name = Path(url).name
|
323
|
-
directory_to_save_file = Path.cwd() # where you want to save the downloaded file
|
324
|
-
|
325
|
-
r = requests.get(url, headers={"authorization": f"Bearer {token}"})
|
326
|
-
if r.status_code == requests.codes.ok:
|
327
|
-
# save the file
|
328
|
-
with open(Path(directory_to_save_file / file_name), 'wb') as f:
|
329
|
-
for data in r:
|
330
|
-
f.write(data)
|
331
|
-
else:
|
332
|
-
#problem occured
|
333
|
-
print(f"failure: {r.status_code}, {r.reason}")
|
334
|
-
```
|
335
|
-
|
336
|
-
Instantiate the subclass and set the token_path where you want to load/save the token.
|
337
|
-
If you provide only a directory, the file will be saved as sso_tokens.json.
|
338
|
-
We hard-code this variable in this simple example, but we recommend setting this path as an environment variable and
|
339
|
-
reading the environment varibale in your code.
|
340
|
-
|
341
|
-
|
342
|
-
the **get_access_token_refresh_if_necessary** method will retrieve the token and refresh the token if it is expired.
|
343
|
-
If there is no token, then the **do_flow** method will begin the device code flow and once you complete the flow,
|
344
|
-
the token will be saved at the token_path. You can use the **requests** library to download the file you want
|
345
|
-
(or files in a loop) and pass in the access token in the Authorization header.
|
346
|
-
|
347
|
-
Learn more about [data access methods](https://www.unavco.org/data/gps-gnss/data-access-methods/data-access-methods.html).
|
339
|
+
|
340
|
+
## SDK Settings
|
341
|
+
|
342
|
+
SDK Settings are provided via the following methods (in order of precedence):
|
343
|
+
|
344
|
+
1. initialization arguments (e.g. via class constructors)
|
345
|
+
1. environment variables
|
346
|
+
1. dotenv file (.env) variables
|
347
|
+
1. user's home directory settings files
|
348
|
+
1. `~/.earthscope/config.toml` (for configuration)
|
349
|
+
1. `~/.earthscope/<profile-name>/tokens.json` (for tokens)
|
350
|
+
1. legacy EarthScope CLI v0 credentials
|
351
|
+
1. default settings
|
352
|
+
|
353
|
+
SDK configuration is managed by the `SdkSettings` class, and calling the constructor performs this settings loading chain.
|
354
|
+
|
355
|
+
```py
|
356
|
+
from earthscope_sdk.config.settings import SdkSettings
|
357
|
+
|
358
|
+
settings = SdkSettings() # loads settings via loading chain
|
359
|
+
```
|
360
|
+
|
361
|
+
For more details on SDK configuration, including what options are available, see [our settings docs](docs/settings.md).
|
362
|
+
|
363
|
+
## Contributing
|
364
|
+
|
365
|
+
For details on contributing to the EarthScope SDK, please see:
|
366
|
+
|
367
|
+
- [design docs](docs/design.md)
|
368
|
+
- [development docs](docs/development.md)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
earthscope_sdk/__init__.py,sha256=6InyrqE0KEsb_XBBKCbUIb8s0LTJ6N20HFsrO-rHVtI,156
|
2
|
+
earthscope_sdk/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
earthscope_sdk/auth/auth_flow.py,sha256=HZyLrt8o3I-0KC7XRg9W0n2NAVXX7EOl9pG-5blv7sA,9613
|
4
|
+
earthscope_sdk/auth/client_credentials_flow.py,sha256=1GyDSIR1OgYP4u0xZoTov1u_YhY1AzHFpOcBCzY1h6E,2769
|
5
|
+
earthscope_sdk/auth/device_code_flow.py,sha256=dC5Ffj3HzBguRxSHCZYvTe1MD3C-iKf2AlanGuRKNvI,7922
|
6
|
+
earthscope_sdk/auth/error.py,sha256=eC33Bw1HaBEJE7-eI2krtE__5PxStc3EyiYO12v0kVw,693
|
7
|
+
earthscope_sdk/client/__init__.py,sha256=JotTr5oTiiOsUc0RTg82EVCUSg_-u80Qu_R0-crCXkY,139
|
8
|
+
earthscope_sdk/client/_client.py,sha256=ai7WdsTOYglA6bLkT-Wntvxlke6nSaGHwqrtg5PEy80,833
|
9
|
+
earthscope_sdk/client/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
earthscope_sdk/client/user/_base.py,sha256=nut1Ojsksohqy3X3L5FPDQ-rh-BmHLJ6sId5xVqLal0,1050
|
11
|
+
earthscope_sdk/client/user/_service.py,sha256=wRktOZF5GXajXXxij3Nkule6wvuWOV0vn4QsA1IXVHc,3063
|
12
|
+
earthscope_sdk/client/user/models.py,sha256=drZAMwOYC1NVCzBZQhNL-pPTB28SURKfoZF8HdjlIj8,1214
|
13
|
+
earthscope_sdk/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
+
earthscope_sdk/common/_sync_runner.py,sha256=h_A2pSEjZCLj7ov50M6cWHVoX6eXVmGzz5nX0MwLWDY,4131
|
15
|
+
earthscope_sdk/common/client.py,sha256=g5ZTNhFm33H68J9pWD5fDu760Yd5cBdfQmsbU3t8D_4,2156
|
16
|
+
earthscope_sdk/common/context.py,sha256=vrCB_Ez-98Ir7c0GrCe-g7DuRCgc9vPaoRWFYf5q8Ko,5138
|
17
|
+
earthscope_sdk/common/service.py,sha256=qBz6OV8rQf3WQojubEVfQ4HYeeKNN3_uIcXuOdvfH8w,1287
|
18
|
+
earthscope_sdk/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
+
earthscope_sdk/config/_compat.py,sha256=P3F5y_Kf5zp9m9uOhl1Bp3ke6expxq4Sm9AeVaBbAHk,4610
|
20
|
+
earthscope_sdk/config/_util.py,sha256=RZ6zvKrvjUkO7i69s7AVoIDhamRg4x71CAZLnucr9QM,1249
|
21
|
+
earthscope_sdk/config/error.py,sha256=jh25q-b317lAvp32WwQw0zdYoV-MxZtg-n5FgZOMymI,95
|
22
|
+
earthscope_sdk/config/models.py,sha256=CarL0O6RjFtufsc-q7g61uBEvETLjQr6HSmjCc0EVig,5775
|
23
|
+
earthscope_sdk/config/settings.py,sha256=I2DwEvfmETcaYbSvUybs0EIih0yiJO9D46WnWzKPqbo,8812
|
24
|
+
earthscope_sdk-1.0.0b0.dist-info/LICENSE,sha256=E_MrVXxRaMQNpvZhsDuz_J9N_ux7dlL_WpYSsE391HU,11349
|
25
|
+
earthscope_sdk-1.0.0b0.dist-info/METADATA,sha256=jbeHzNrmHRZUGOFai1WmCDK1CQ-kWSbIsaNRjHq_WhA,17935
|
26
|
+
earthscope_sdk-1.0.0b0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
27
|
+
earthscope_sdk-1.0.0b0.dist-info/top_level.txt,sha256=zTtIT9yN3JPJF7TqmTzqQcAvZZe4pAm907DLoGa5T_E,15
|
28
|
+
earthscope_sdk-1.0.0b0.dist-info/RECORD,,
|
earthscope_sdk/user/user.py
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
import requests
|
2
|
-
import os
|
3
|
-
|
4
|
-
from typing import List
|
5
|
-
|
6
|
-
API_BASE_URL = os.environ.get("API_BASE_URL", "https://www.earthscope.org/api/v0")
|
7
|
-
|
8
|
-
|
9
|
-
def get_user(access_token: str):
|
10
|
-
r = requests.get(
|
11
|
-
f"{API_BASE_URL}/user",
|
12
|
-
headers={"authorization": f"Bearer {access_token}"},
|
13
|
-
)
|
14
|
-
|
15
|
-
try:
|
16
|
-
rjson = r.json()
|
17
|
-
if r.status_code == 200:
|
18
|
-
return rjson
|
19
|
-
error = rjson.get("detail") or rjson.get("message") or rjson
|
20
|
-
except Exception:
|
21
|
-
r.raise_for_status()
|
22
|
-
error = r.text
|
23
|
-
|
24
|
-
raise RuntimeError(error)
|
@@ -1,12 +0,0 @@
|
|
1
|
-
earthscope_sdk/__init__.py,sha256=HfjVOrpTnmZ-xVFCYSVmX50EXaBQeJteUHG-PD6iQs8,22
|
2
|
-
earthscope_sdk/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
earthscope_sdk/auth/auth_flow.py,sha256=xky9ee8p3-lm-c-8rfnNXWSrPBLW3MG-sHDhjlvzohQ,11701
|
4
|
-
earthscope_sdk/auth/client_credentials_flow.py,sha256=G4NeWLtKoDZlbx7Fk6TY5fNaGXf3E43YMQcI3vFVsYE,5602
|
5
|
-
earthscope_sdk/auth/device_code_flow.py,sha256=tl87wYYVqWkM4gJ33735jccpqYqX7P1g_TDo8YAM5rk,9149
|
6
|
-
earthscope_sdk/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
earthscope_sdk/user/user.py,sha256=bCYbvq02bCKMJVYfbj3lpmO1VjTUgNDL4w4PiGvhuG8,566
|
8
|
-
earthscope_sdk-0.2.1.dist-info/LICENSE,sha256=E_MrVXxRaMQNpvZhsDuz_J9N_ux7dlL_WpYSsE391HU,11349
|
9
|
-
earthscope_sdk-0.2.1.dist-info/METADATA,sha256=hl1yJ3NWUZWhZPJ4cPqhw4iRMWKxDkqqaRXqROMLLrk,18756
|
10
|
-
earthscope_sdk-0.2.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
11
|
-
earthscope_sdk-0.2.1.dist-info/top_level.txt,sha256=zTtIT9yN3JPJF7TqmTzqQcAvZZe4pAm907DLoGa5T_E,15
|
12
|
-
earthscope_sdk-0.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|