datacosmos 0.0.20__py3-none-any.whl → 0.0.21__py3-none-any.whl

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.

@@ -5,11 +5,6 @@ This module normalizes the `authentication` config into a concrete model:
5
5
  - `apply_auth_defaults` fills sensible defaults per auth type without inventing secrets.
6
6
  - `check_required_auth_fields` enforces the minimum required inputs.
7
7
  - `normalize_authentication` runs the whole pipeline.
8
-
9
- Design notes:
10
- - Auth models accept partial data (fields are Optional with None defaults).
11
- - We DO NOT pass `None` explicitly when constructing models here.
12
- - Required-ness is enforced centrally by `check_required_auth_fields`, not by model init.
13
8
  """
14
9
 
15
10
  from typing import Optional, Union, cast
@@ -33,49 +28,45 @@ AuthModel = Union[M2MAuthenticationConfig, LocalUserAccountAuthenticationConfig]
33
28
 
34
29
 
35
30
  def parse_auth_config(raw: dict | AuthModel | None) -> Optional[AuthModel]:
36
- """Turn a raw dict (e.g., from YAML) into a concrete auth model.
37
-
38
- - If `raw` is already an auth model (M2M or local), return it unchanged.
39
- - If `raw` is a dict, choose/validate the type using `raw['type']`
40
- (or DEFAULT_AUTH_TYPE), then construct the corresponding model.
41
- For missing fields we *may* apply non-secret defaults (endpoints, etc.).
42
- """
43
- if raw is None or isinstance(
44
- raw, (M2MAuthenticationConfig, LocalUserAccountAuthenticationConfig)
45
- ):
31
+ """Turn a raw dict (e.g., from YAML/env) into a concrete auth model."""
32
+ if isinstance(raw, (M2MAuthenticationConfig, LocalUserAccountAuthenticationConfig)):
46
33
  return cast(Optional[AuthModel], raw)
47
34
 
48
- auth_type = _normalize_auth_type(raw.get("type") or DEFAULT_AUTH_TYPE)
35
+ if raw is None:
36
+ raw_data = {}
37
+ else:
38
+ raw_data = raw.copy()
39
+
40
+ if raw is None and not raw_data:
41
+ return None
42
+
43
+ auth_type = _normalize_auth_type(raw_data.get("type") or DEFAULT_AUTH_TYPE)
49
44
 
50
45
  if auth_type == "local":
51
46
  return LocalUserAccountAuthenticationConfig(
52
47
  type="local",
53
- client_id=raw.get("client_id"),
54
- authorization_endpoint=raw.get(
48
+ client_id=raw_data.get("client_id"),
49
+ authorization_endpoint=raw_data.get(
55
50
  "authorization_endpoint", DEFAULT_LOCAL_AUTHORIZATION_ENDPOINT
56
51
  ),
57
- token_endpoint=raw.get("token_endpoint", DEFAULT_LOCAL_TOKEN_ENDPOINT),
58
- redirect_port=raw.get("redirect_port", DEFAULT_LOCAL_REDIRECT_PORT),
59
- scopes=raw.get("scopes", DEFAULT_LOCAL_SCOPES),
60
- audience=raw.get("audience", DEFAULT_AUTH_AUDIENCE),
61
- cache_file=raw.get("cache_file", DEFAULT_LOCAL_CACHE_FILE),
52
+ token_endpoint=raw_data.get("token_endpoint", DEFAULT_LOCAL_TOKEN_ENDPOINT),
53
+ redirect_port=raw_data.get("redirect_port", DEFAULT_LOCAL_REDIRECT_PORT),
54
+ scopes=raw_data.get("scopes", DEFAULT_LOCAL_SCOPES),
55
+ audience=raw_data.get("audience", DEFAULT_AUTH_AUDIENCE),
56
+ cache_file=raw_data.get("cache_file", DEFAULT_LOCAL_CACHE_FILE),
62
57
  )
63
58
 
64
59
  return M2MAuthenticationConfig(
65
60
  type="m2m",
66
- token_url=raw.get("token_url", DEFAULT_AUTH_TOKEN_URL),
67
- audience=raw.get("audience", DEFAULT_AUTH_AUDIENCE),
68
- client_id=raw.get("client_id"),
69
- client_secret=raw.get("client_secret"),
61
+ token_url=raw_data.get("token_url", DEFAULT_AUTH_TOKEN_URL),
62
+ audience=raw_data.get("audience", DEFAULT_AUTH_AUDIENCE),
63
+ client_id=raw_data.get("client_id"),
64
+ client_secret=raw_data.get("client_secret"),
70
65
  )
71
66
 
72
67
 
73
68
  def apply_auth_defaults(auth: AuthModel | None) -> AuthModel:
74
- """Fill in any missing defaults by type (non-secret values only).
75
-
76
- If `auth` is None, construct a default "shell" based on DEFAULT_AUTH_TYPE,
77
- without passing None for unknown credentials.
78
- """
69
+ """Fill in any missing defaults by type (non-secret values only)."""
79
70
  if auth is None:
80
71
  default_type = _normalize_auth_type(DEFAULT_AUTH_TYPE)
81
72
  if default_type == "local":
@@ -101,7 +92,6 @@ def apply_auth_defaults(auth: AuthModel | None) -> AuthModel:
101
92
  auth.audience = auth.audience or DEFAULT_AUTH_AUDIENCE
102
93
  return auth
103
94
 
104
- # Local defaults (Pydantic already coerces types; only set when missing)
105
95
  auth.type = auth.type or "local"
106
96
  auth.authorization_endpoint = (
107
97
  auth.authorization_endpoint or DEFAULT_LOCAL_AUTHORIZATION_ENDPOINT
@@ -116,11 +106,7 @@ def apply_auth_defaults(auth: AuthModel | None) -> AuthModel:
116
106
 
117
107
 
118
108
  def check_required_auth_fields(auth: AuthModel) -> None:
119
- """Enforce required fields per auth type.
120
-
121
- - m2m requires client_id and client_secret.
122
- - local requires client_id.
123
- """
109
+ """Enforce required fields per auth type."""
124
110
  if isinstance(auth, M2MAuthenticationConfig):
125
111
  missing = [f for f in ("client_id", "client_secret") if not getattr(auth, f)]
126
112
  if missing:
@@ -7,7 +7,7 @@ and supports environment variable-based overrides.
7
7
 
8
8
  from typing import Optional
9
9
 
10
- from pydantic import field_validator
10
+ from pydantic import Field, field_validator
11
11
  from pydantic_settings import BaseSettings, SettingsConfigDict
12
12
 
13
13
  from datacosmos.config.auth.factory import normalize_authentication, parse_auth_config
@@ -18,6 +18,10 @@ from datacosmos.config.constants import (
18
18
  )
19
19
  from datacosmos.config.loaders.yaml_source import yaml_settings_source
20
20
  from datacosmos.config.models.authentication_config import AuthenticationConfig
21
+ from datacosmos.config.models.local_user_account_authentication_config import (
22
+ LocalUserAccountAuthenticationConfig,
23
+ )
24
+ from datacosmos.config.models.m2m_authentication_config import M2MAuthenticationConfig
21
25
  from datacosmos.config.models.url import URL
22
26
 
23
27
 
@@ -31,9 +35,14 @@ class Config(BaseSettings):
31
35
  )
32
36
 
33
37
  authentication: Optional[AuthenticationConfig] = None
34
- stac: URL | None = None
35
- datacosmos_cloud_storage: URL | None = None
36
- datacosmos_public_cloud_storage: URL | None = None
38
+
39
+ stac: URL = Field(default_factory=lambda: URL(**DEFAULT_STAC))
40
+ datacosmos_cloud_storage: URL = Field(
41
+ default_factory=lambda: URL(**DEFAULT_STORAGE)
42
+ )
43
+ datacosmos_public_cloud_storage: URL = Field(
44
+ default_factory=lambda: URL(**DEFAULT_STORAGE)
45
+ )
37
46
 
38
47
  @classmethod
39
48
  def settings_customise_sources(cls, *args, **kwargs):
@@ -65,13 +74,6 @@ class Config(BaseSettings):
65
74
  def _parse_authentication(cls, raw):
66
75
  if raw is None:
67
76
  return None
68
- from datacosmos.config.models.local_user_account_authentication_config import (
69
- LocalUserAccountAuthenticationConfig,
70
- )
71
- from datacosmos.config.models.m2m_authentication_config import (
72
- M2MAuthenticationConfig,
73
- )
74
-
75
77
  if isinstance(
76
78
  raw, (M2MAuthenticationConfig, LocalUserAccountAuthenticationConfig)
77
79
  ):
@@ -84,18 +86,3 @@ class Config(BaseSettings):
84
86
  @classmethod
85
87
  def _validate_authentication(cls, auth: Optional[AuthenticationConfig]):
86
88
  return normalize_authentication(auth)
87
-
88
- @field_validator("stac", mode="before")
89
- @classmethod
90
- def _default_stac(cls, value: URL | None) -> URL:
91
- return value or URL(**DEFAULT_STAC)
92
-
93
- @field_validator("datacosmos_cloud_storage", mode="before")
94
- @classmethod
95
- def _default_cloud_storage(cls, value: URL | None) -> URL:
96
- return value or URL(**DEFAULT_STORAGE)
97
-
98
- @field_validator("datacosmos_public_cloud_storage", mode="before")
99
- @classmethod
100
- def _default_public_cloud_storage(cls, value: URL | None) -> URL:
101
- return value or URL(**DEFAULT_STORAGE)
@@ -62,7 +62,7 @@ class StorageBase:
62
62
  try:
63
63
  result = future.result()
64
64
  except Exception as e:
65
- failures.append({'error': str(e), 'exception': e})
65
+ failures.append({"error": str(e), "exception": e})
66
66
  else:
67
67
  successes.append(result)
68
68
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacosmos
3
- Version: 0.0.20
3
+ Version: 0.0.21
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
@@ -7,10 +7,10 @@ datacosmos/auth/local_token_fetcher.py,sha256=E4MI2lTRHAmxIQA7qY6hmpAUZETTzXNO8M
7
7
  datacosmos/auth/m2m_authenticator.py,sha256=_n5qpyK9ngqHec8uMDBOKL-U3k989geIQ-rzH9PMhZc,2621
8
8
  datacosmos/auth/token.py,sha256=neSV9gnnFa-rxEwMAJlZe_cReV6g4PQf8mq4-1mZzB8,2558
9
9
  datacosmos/config/__init__.py,sha256=KCsaTb9-ZgFui1GM8wZFIPLJy0D0O8l8Z1Sv3NRD9UM,140
10
- datacosmos/config/config.py,sha256=JHWmS6Q_T32iMly7cvJncjPCvtIgamBigKJJcvjGH7g,3465
10
+ datacosmos/config/config.py,sha256=SAi0au3NkPn7MhVgvqsI2lUjKELzODSsEayrwDEkGbY,3008
11
11
  datacosmos/config/constants.py,sha256=HztzJ0OCti20uMXPQGvCFf_M4Vz1xRdxD2_2k9mAThs,855
12
12
  datacosmos/config/auth/__init__.py,sha256=jsqJQby3tyugcGtY7BoDKvv5qotkAomPc2uOELUlKtE,51
13
- datacosmos/config/auth/factory.py,sha256=svkL58xfqZ2ubcqQGZq9llb4RH-zkwrMPgBDD1Wk3Js,6213
13
+ datacosmos/config/auth/factory.py,sha256=15cBLQ7bAdH_hM3KFcCmBmHTn6Mgyf3vvwhrfAJua28,5541
14
14
  datacosmos/config/loaders/yaml_source.py,sha256=GY3RZvIjFJagTPiPmOhmFRa8aD0p0rZtTXgywU28fxo,2281
15
15
  datacosmos/config/models/__init__.py,sha256=r3lThPkyKjBjUZXRNscFzOrmn_-m_i9DvG3RePfCFYc,41
16
16
  datacosmos/config/models/authentication_config.py,sha256=01Q90-yupbJ5orYDtatZIm9EaL7roQ-oUMoZfFMRzIM,499
@@ -43,7 +43,7 @@ datacosmos/stac/item/models/eo_band.py,sha256=YC3Scn_wFhIo51pIVcJeuJienF7JGWoEv3
43
43
  datacosmos/stac/item/models/item_update.py,sha256=_CpjQn9SsfedfuxlHSiGeptqY4M-p15t9YX__mBRueI,2088
44
44
  datacosmos/stac/item/models/raster_band.py,sha256=CoEVs-YyPE5Fse0He9DdOs4dGZpzfCsCuVzOcdXa_UM,354
45
45
  datacosmos/stac/storage/__init__.py,sha256=hivfSpOaoSwCAymgU0rTgvSk9LSPAn1cPLQQ9fLmFX0,151
46
- datacosmos/stac/storage/storage_base.py,sha256=NpCKAA3qEI212WUNZ2-eG9XfSJKDMiJcu4pEmr10-JI,2843
46
+ datacosmos/stac/storage/storage_base.py,sha256=4ciTrNisxFV1A-BduOS3COvCHQbVhHA97W1QLhotbI4,2843
47
47
  datacosmos/stac/storage/storage_client.py,sha256=4boqQ3zVMrk9X2IXus-Cs429juLe0cUQ0XEzg_y3yOA,1205
48
48
  datacosmos/stac/storage/uploader.py,sha256=DawtNn4-uEtpUYPZS1fKv77wu-zNne9ltlGZiArzBFI,5919
49
49
  datacosmos/stac/storage/dataclasses/__init__.py,sha256=IjcyA8Vod-z1_Gi1FMZhK58Owman0foL25Hs0YtkYYs,43
@@ -55,8 +55,8 @@ datacosmos/utils/http_response/check_api_response.py,sha256=l_yQiiekNcNbhFec_5Ue
55
55
  datacosmos/utils/http_response/models/__init__.py,sha256=Wj8YT6dqw7rAz_rctllxo5Or_vv8DwopvQvBzwCTvpw,45
56
56
  datacosmos/utils/http_response/models/datacosmos_error.py,sha256=Uqi2uM98nJPeCbM7zngV6vHSk97jEAb_nkdDEeUjiQM,740
57
57
  datacosmos/utils/http_response/models/datacosmos_response.py,sha256=oV4n-sue7K1wwiIQeHpxdNU8vxeqF3okVPE2rydw5W0,336
58
- datacosmos-0.0.20.dist-info/licenses/LICENSE.md,sha256=vpbRI-UUbZVQfr3VG_CXt9HpRnL1b5kt8uTVbirxeyI,1486
59
- datacosmos-0.0.20.dist-info/METADATA,sha256=WfpmuajQ7GSenjnr8X04rNXDg6U6NH3pmbFMtJYqFV4,1000
60
- datacosmos-0.0.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
61
- datacosmos-0.0.20.dist-info/top_level.txt,sha256=ueobs5CNeyDbPMgXPcVV0d0yNdm8CvGtDT3CaksRVtA,11
62
- datacosmos-0.0.20.dist-info/RECORD,,
58
+ datacosmos-0.0.21.dist-info/licenses/LICENSE.md,sha256=vpbRI-UUbZVQfr3VG_CXt9HpRnL1b5kt8uTVbirxeyI,1486
59
+ datacosmos-0.0.21.dist-info/METADATA,sha256=i7VRWZEkz9TBSQZUX3HZ7eN-028ZJDpR9gRGnFK0vaE,1000
60
+ datacosmos-0.0.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
61
+ datacosmos-0.0.21.dist-info/top_level.txt,sha256=ueobs5CNeyDbPMgXPcVV0d0yNdm8CvGtDT3CaksRVtA,11
62
+ datacosmos-0.0.21.dist-info/RECORD,,