eodag 3.1.0b1__py3-none-any.whl → 3.2.0__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.
- eodag/api/core.py +69 -63
- eodag/api/product/_assets.py +49 -13
- eodag/api/product/_product.py +41 -30
- eodag/api/product/drivers/__init__.py +81 -4
- eodag/api/product/drivers/base.py +65 -4
- eodag/api/product/drivers/generic.py +65 -0
- eodag/api/product/drivers/sentinel1.py +97 -0
- eodag/api/product/drivers/sentinel2.py +95 -0
- eodag/api/product/metadata_mapping.py +85 -79
- eodag/api/search_result.py +13 -23
- eodag/cli.py +4 -4
- eodag/config.py +77 -80
- eodag/plugins/apis/base.py +1 -1
- eodag/plugins/apis/ecmwf.py +12 -15
- eodag/plugins/apis/usgs.py +12 -11
- eodag/plugins/authentication/aws_auth.py +16 -13
- eodag/plugins/authentication/base.py +5 -3
- eodag/plugins/authentication/header.py +3 -3
- eodag/plugins/authentication/keycloak.py +4 -4
- eodag/plugins/authentication/oauth.py +7 -3
- eodag/plugins/authentication/openid_connect.py +20 -14
- eodag/plugins/authentication/sas_auth.py +4 -4
- eodag/plugins/authentication/token.py +7 -7
- eodag/plugins/authentication/token_exchange.py +1 -1
- eodag/plugins/base.py +4 -4
- eodag/plugins/crunch/base.py +4 -4
- eodag/plugins/crunch/filter_date.py +4 -4
- eodag/plugins/crunch/filter_latest_intersect.py +6 -6
- eodag/plugins/crunch/filter_latest_tpl_name.py +7 -7
- eodag/plugins/crunch/filter_overlap.py +4 -4
- eodag/plugins/crunch/filter_property.py +4 -4
- eodag/plugins/download/aws.py +137 -77
- eodag/plugins/download/base.py +8 -17
- eodag/plugins/download/creodias_s3.py +2 -2
- eodag/plugins/download/http.py +30 -32
- eodag/plugins/download/s3rest.py +5 -4
- eodag/plugins/manager.py +10 -20
- eodag/plugins/search/__init__.py +6 -5
- eodag/plugins/search/base.py +38 -42
- eodag/plugins/search/build_search_result.py +286 -336
- eodag/plugins/search/cop_marine.py +22 -12
- eodag/plugins/search/creodias_s3.py +8 -78
- eodag/plugins/search/csw.py +11 -11
- eodag/plugins/search/data_request_search.py +19 -18
- eodag/plugins/search/qssearch.py +84 -151
- eodag/plugins/search/stac_list_assets.py +85 -0
- eodag/plugins/search/static_stac_search.py +4 -4
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +848 -398
- eodag/resources/providers.yml +1038 -1115
- eodag/resources/stac_api.yml +2 -2
- eodag/resources/user_conf_template.yml +10 -9
- eodag/rest/cache.py +2 -2
- eodag/rest/config.py +3 -3
- eodag/rest/core.py +24 -24
- eodag/rest/errors.py +5 -5
- eodag/rest/server.py +3 -11
- eodag/rest/stac.py +41 -38
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +23 -23
- eodag/rest/types/queryables.py +40 -28
- eodag/rest/types/stac_search.py +15 -25
- eodag/rest/utils/__init__.py +11 -21
- eodag/rest/utils/cql_evaluate.py +6 -6
- eodag/rest/utils/rfc3339.py +2 -2
- eodag/types/__init__.py +97 -29
- eodag/types/bbox.py +2 -2
- eodag/types/download_args.py +2 -2
- eodag/types/queryables.py +5 -2
- eodag/types/search_args.py +4 -4
- eodag/types/whoosh.py +1 -3
- eodag/utils/__init__.py +82 -41
- eodag/utils/exceptions.py +2 -2
- eodag/utils/import_system.py +2 -2
- eodag/utils/requests.py +2 -2
- eodag/utils/rest.py +2 -2
- eodag/utils/s3.py +231 -0
- eodag/utils/stac_reader.py +10 -10
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/METADATA +12 -10
- eodag-3.2.0.dist-info/RECORD +113 -0
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/WHEEL +1 -1
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/entry_points.txt +1 -0
- eodag-3.1.0b1.dist-info/RECORD +0 -108
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info/licenses}/LICENSE +0 -0
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/top_level.txt +0 -0
eodag/types/__init__.py
CHANGED
|
@@ -22,11 +22,9 @@ from __future__ import annotations
|
|
|
22
22
|
from typing import (
|
|
23
23
|
Annotated,
|
|
24
24
|
Any,
|
|
25
|
-
Dict,
|
|
26
|
-
List,
|
|
27
25
|
Literal,
|
|
28
26
|
Optional,
|
|
29
|
-
|
|
27
|
+
Type,
|
|
30
28
|
TypedDict,
|
|
31
29
|
Union,
|
|
32
30
|
get_args,
|
|
@@ -34,15 +32,18 @@ from typing import (
|
|
|
34
32
|
)
|
|
35
33
|
|
|
36
34
|
from annotated_types import Gt, Lt
|
|
37
|
-
from pydantic import BaseModel, Field, create_model
|
|
35
|
+
from pydantic import BaseModel, ConfigDict, Field, create_model
|
|
36
|
+
from pydantic.annotated_handlers import GetJsonSchemaHandler
|
|
38
37
|
from pydantic.fields import FieldInfo
|
|
38
|
+
from pydantic.json_schema import JsonSchemaValue
|
|
39
|
+
from pydantic_core import CoreSchema
|
|
39
40
|
|
|
40
41
|
from eodag.utils import copy_deepcopy
|
|
41
42
|
from eodag.utils.exceptions import ValidationError
|
|
42
43
|
|
|
43
44
|
# Types mapping from JSON Schema and OpenAPI 3.1.0 specifications to Python
|
|
44
45
|
# See https://spec.openapis.org/oas/v3.1.0#data-types
|
|
45
|
-
JSON_TYPES_MAPPING:
|
|
46
|
+
JSON_TYPES_MAPPING: dict[str, type] = {
|
|
46
47
|
"boolean": bool,
|
|
47
48
|
"integer": int,
|
|
48
49
|
"number": float,
|
|
@@ -53,7 +54,7 @@ JSON_TYPES_MAPPING: Dict[str, type] = {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
|
|
56
|
-
def json_type_to_python(json_type: Union[str,
|
|
57
|
+
def json_type_to_python(json_type: Union[str, list[str]]) -> type:
|
|
57
58
|
"""Get python type from json type https://spec.openapis.org/oas/v3.1.0#data-types
|
|
58
59
|
|
|
59
60
|
>>> json_type_to_python("number")
|
|
@@ -70,7 +71,7 @@ def json_type_to_python(json_type: Union[str, List[str]]) -> type:
|
|
|
70
71
|
return type(None)
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
def _get_min_or_max(type_info: Union[Lt, Gt, Any]) ->
|
|
74
|
+
def _get_min_or_max(type_info: Union[Lt, Gt, Any]) -> tuple[str, Any]:
|
|
74
75
|
"""Checks if the value from an Annotated object is a minimum or maximum
|
|
75
76
|
|
|
76
77
|
:param type_info: info from Annotated
|
|
@@ -85,7 +86,7 @@ def _get_min_or_max(type_info: Union[Lt, Gt, Any]) -> Tuple[str, Any]:
|
|
|
85
86
|
|
|
86
87
|
def _get_type_info_from_annotated(
|
|
87
88
|
annotated_type: Annotated[type, Any],
|
|
88
|
-
) ->
|
|
89
|
+
) -> dict[str, Any]:
|
|
89
90
|
"""Retrieves type information from an annotated object
|
|
90
91
|
|
|
91
92
|
:param annotated_type: annotated object
|
|
@@ -108,7 +109,7 @@ def _get_type_info_from_annotated(
|
|
|
108
109
|
|
|
109
110
|
def python_type_to_json(
|
|
110
111
|
python_type: type,
|
|
111
|
-
) -> Optional[Union[str,
|
|
112
|
+
) -> Optional[Union[str, list[dict[str, Any]]]]:
|
|
112
113
|
"""Get json type from python https://spec.openapis.org/oas/v3.1.0#data-types
|
|
113
114
|
|
|
114
115
|
>>> python_type_to_json(int)
|
|
@@ -149,7 +150,7 @@ def python_type_to_json(
|
|
|
149
150
|
|
|
150
151
|
|
|
151
152
|
def json_field_definition_to_python(
|
|
152
|
-
json_field_definition:
|
|
153
|
+
json_field_definition: dict[str, Any],
|
|
153
154
|
default_value: Optional[Any] = None,
|
|
154
155
|
required: Optional[bool] = False,
|
|
155
156
|
) -> Annotated[Any, FieldInfo]:
|
|
@@ -186,7 +187,7 @@ def json_field_definition_to_python(
|
|
|
186
187
|
if python_type in (list, set):
|
|
187
188
|
items = json_field_definition.get("items", None)
|
|
188
189
|
if isinstance(items, list):
|
|
189
|
-
python_type =
|
|
190
|
+
python_type = tuple[ # type: ignore
|
|
190
191
|
tuple(
|
|
191
192
|
json_field_definition_to_python(item, required=required)
|
|
192
193
|
for item in items
|
|
@@ -197,20 +198,28 @@ def json_field_definition_to_python(
|
|
|
197
198
|
|
|
198
199
|
if enum:
|
|
199
200
|
literal = Literal[tuple(sorted(enum))] # type: ignore
|
|
200
|
-
python_type =
|
|
201
|
+
python_type = list[literal] if python_type in (list, set) else literal # type: ignore
|
|
201
202
|
|
|
202
203
|
if "$ref" in json_field_definition:
|
|
203
204
|
field_type_kwargs["json_schema_extra"] = {"$ref": json_field_definition["$ref"]}
|
|
204
205
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
metadata = [
|
|
207
|
+
python_type,
|
|
208
|
+
Field(
|
|
209
|
+
default_value if not required or default_value is not None else ...,
|
|
210
|
+
**field_type_kwargs,
|
|
211
|
+
),
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
if required:
|
|
215
|
+
metadata.append("json_schema_required")
|
|
216
|
+
|
|
217
|
+
return Annotated[tuple(metadata)]
|
|
209
218
|
|
|
210
219
|
|
|
211
220
|
def python_field_definition_to_json(
|
|
212
221
|
python_field_definition: Annotated[Any, FieldInfo],
|
|
213
|
-
) ->
|
|
222
|
+
) -> dict[str, Any]:
|
|
214
223
|
"""Get json field definition from python `typing.Annotated`
|
|
215
224
|
|
|
216
225
|
>>> from pydantic import Field
|
|
@@ -231,7 +240,7 @@ def python_field_definition_to_json(
|
|
|
231
240
|
"%s must be an instance of Annotated" % python_field_definition
|
|
232
241
|
)
|
|
233
242
|
|
|
234
|
-
json_field_definition:
|
|
243
|
+
json_field_definition: dict[str, Any] = dict()
|
|
235
244
|
|
|
236
245
|
python_field_args = get_args(python_field_definition)
|
|
237
246
|
|
|
@@ -311,8 +320,8 @@ def python_field_definition_to_json(
|
|
|
311
320
|
|
|
312
321
|
|
|
313
322
|
def model_fields_to_annotated(
|
|
314
|
-
model_fields:
|
|
315
|
-
) ->
|
|
323
|
+
model_fields: dict[str, FieldInfo],
|
|
324
|
+
) -> dict[str, Annotated[Any, FieldInfo]]:
|
|
316
325
|
"""Convert BaseModel.model_fields from FieldInfo to Annotated
|
|
317
326
|
|
|
318
327
|
>>> from pydantic import create_model
|
|
@@ -326,7 +335,7 @@ def model_fields_to_annotated(
|
|
|
326
335
|
:param model_fields: BaseModel.model_fields to convert
|
|
327
336
|
:returns: Annotated tuple usable as create_model argument
|
|
328
337
|
"""
|
|
329
|
-
annotated_model_fields:
|
|
338
|
+
annotated_model_fields: dict[str, Annotated[Any, FieldInfo]] = dict()
|
|
330
339
|
for param, field_info in model_fields.items():
|
|
331
340
|
field_type = field_info.annotation or type(None)
|
|
332
341
|
new_field_info = copy_deepcopy(field_info)
|
|
@@ -335,26 +344,76 @@ def model_fields_to_annotated(
|
|
|
335
344
|
return annotated_model_fields
|
|
336
345
|
|
|
337
346
|
|
|
347
|
+
class BaseModelCustomJsonSchema(BaseModel):
|
|
348
|
+
"""Base class for generated models with custom json schema."""
|
|
349
|
+
|
|
350
|
+
@classmethod
|
|
351
|
+
def __get_pydantic_json_schema__(
|
|
352
|
+
cls: Type[BaseModel], core_schema: CoreSchema, handler: GetJsonSchemaHandler
|
|
353
|
+
) -> JsonSchemaValue:
|
|
354
|
+
"""
|
|
355
|
+
Custom get json schema method to handle required fields with default values.
|
|
356
|
+
This is not supported by Pydantic by default.
|
|
357
|
+
It requires the field to be marked with the key "json_schema_required" in the metadata dict.
|
|
358
|
+
Example: Annotated[str, "json_schema_required"]
|
|
359
|
+
"""
|
|
360
|
+
json_schema = handler.resolve_ref_schema(handler(core_schema))
|
|
361
|
+
|
|
362
|
+
json_schema["required"] = [
|
|
363
|
+
key
|
|
364
|
+
for key, field_info in cls.model_fields.items()
|
|
365
|
+
if "json_schema_required" in field_info.metadata
|
|
366
|
+
]
|
|
367
|
+
|
|
368
|
+
return json_schema
|
|
369
|
+
|
|
370
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
371
|
+
|
|
372
|
+
|
|
338
373
|
def annotated_dict_to_model(
|
|
339
|
-
model_name: str, annotated_fields:
|
|
374
|
+
model_name: str, annotated_fields: dict[str, Annotated[Any, FieldInfo]]
|
|
340
375
|
) -> BaseModel:
|
|
341
376
|
"""Convert a dictionary of Annotated values to a Pydantic BaseModel.
|
|
342
377
|
|
|
378
|
+
>>> from pydantic import Field
|
|
379
|
+
>>> annotated_fields = {
|
|
380
|
+
... "field1": Annotated[str, Field(description="A string field"), "json_schema_required"],
|
|
381
|
+
... "field2": Annotated[int, Field(default=42, description="An integer field")],
|
|
382
|
+
... }
|
|
383
|
+
>>> model = annotated_dict_to_model("TestModel", annotated_fields)
|
|
384
|
+
>>> json_schema = model.model_json_schema()
|
|
385
|
+
>>> json_schema["required"]
|
|
386
|
+
['field1']
|
|
387
|
+
>>> json_schema["properties"]["field1"]
|
|
388
|
+
{'description': 'A string field', 'title': 'Field1', 'type': 'string'}
|
|
389
|
+
>>> json_schema["properties"]["field2"]
|
|
390
|
+
{'default': 42, 'description': 'An integer field', 'title': 'Field2', 'type': 'integer'}
|
|
391
|
+
>>> json_schema["title"]
|
|
392
|
+
'TestModel'
|
|
393
|
+
>>> json_schema["type"]
|
|
394
|
+
'object'
|
|
395
|
+
|
|
343
396
|
:param model_name: name of the model to be created
|
|
344
397
|
:param annotated_fields: dict containing the parameters and annotated values that should become
|
|
345
398
|
the properties of the model
|
|
346
399
|
:returns: pydantic model
|
|
347
400
|
"""
|
|
348
|
-
fields = {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
401
|
+
fields = {}
|
|
402
|
+
for name, field in annotated_fields.items():
|
|
403
|
+
base_type, field_info, *metadata = get_args(field)
|
|
404
|
+
fields[name] = (
|
|
405
|
+
Annotated[tuple([base_type] + metadata)] if metadata else base_type,
|
|
406
|
+
field_info,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
custom_model = create_model(
|
|
353
410
|
model_name,
|
|
411
|
+
__base__=BaseModelCustomJsonSchema,
|
|
354
412
|
**fields, # type: ignore
|
|
355
|
-
__config__={"arbitrary_types_allowed": True},
|
|
356
413
|
)
|
|
357
414
|
|
|
415
|
+
return custom_model
|
|
416
|
+
|
|
358
417
|
|
|
359
418
|
class ProviderSortables(TypedDict):
|
|
360
419
|
"""A class representing sortable parameter(s) of a provider and the allowed
|
|
@@ -364,5 +423,14 @@ class ProviderSortables(TypedDict):
|
|
|
364
423
|
:param max_sort_params: (optional) The allowed maximum number of sortable(s) in a search request with the provider
|
|
365
424
|
"""
|
|
366
425
|
|
|
367
|
-
sortables:
|
|
426
|
+
sortables: list[str]
|
|
368
427
|
max_sort_params: Annotated[Optional[int], Gt(0)]
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class S3SessionKwargs(TypedDict, total=False):
|
|
431
|
+
"""A class representing available keyword arguments to pass to :class:`boto3.session.Session` for authentication"""
|
|
432
|
+
|
|
433
|
+
aws_access_key_id: Optional[str]
|
|
434
|
+
aws_secret_access_key: Optional[str]
|
|
435
|
+
aws_session_token: Optional[str]
|
|
436
|
+
profile_name: Optional[str]
|
eodag/types/bbox.py
CHANGED
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
16
|
# See the License for the specific language governing permissions and
|
|
17
17
|
# limitations under the License.
|
|
18
|
-
from typing import
|
|
18
|
+
from typing import Union
|
|
19
19
|
|
|
20
20
|
from pydantic import BaseModel, ValidationInfo, field_validator
|
|
21
21
|
from shapely.geometry.polygon import Polygon
|
|
22
22
|
|
|
23
23
|
NumType = Union[float, int]
|
|
24
24
|
BBoxArgs = Union[
|
|
25
|
-
|
|
25
|
+
list[NumType], tuple[NumType, NumType, NumType, NumType], dict[str, NumType]
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
|
eodag/types/download_args.py
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from typing import
|
|
20
|
+
from typing import Optional, TypedDict, Union
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class DownloadConf(TypedDict, total=False):
|
|
@@ -35,6 +35,6 @@ class DownloadConf(TypedDict, total=False):
|
|
|
35
35
|
output_dir: str
|
|
36
36
|
output_extension: Union[str, None]
|
|
37
37
|
extract: bool
|
|
38
|
-
dl_url_params:
|
|
38
|
+
dl_url_params: dict[str, str]
|
|
39
39
|
delete_archive: bool
|
|
40
40
|
asset: Optional[str]
|
eodag/types/queryables.py
CHANGED
|
@@ -148,7 +148,7 @@ class Queryables(CommonQueryables):
|
|
|
148
148
|
incidenceAngleVariation: Annotated[float, Field(None)]
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
class QueryablesDict(UserDict):
|
|
151
|
+
class QueryablesDict(UserDict[str, Any]):
|
|
152
152
|
"""Class inheriting from UserDict which contains queryables with their annotated type;
|
|
153
153
|
|
|
154
154
|
:param additional_properties: if additional properties (properties not given in EODAG config)
|
|
@@ -160,7 +160,10 @@ class QueryablesDict(UserDict):
|
|
|
160
160
|
additional_information: str = Field("")
|
|
161
161
|
|
|
162
162
|
def __init__(
|
|
163
|
-
self,
|
|
163
|
+
self,
|
|
164
|
+
additional_properties: bool = True,
|
|
165
|
+
additional_information: str = "",
|
|
166
|
+
**kwargs: Any,
|
|
164
167
|
):
|
|
165
168
|
self.additional_properties = additional_properties
|
|
166
169
|
self.additional_information = additional_information
|
eodag/types/search_args.py
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
import re
|
|
19
19
|
from datetime import datetime
|
|
20
|
-
from typing import Annotated, Any,
|
|
20
|
+
from typing import Annotated, Any, Optional, Union, cast
|
|
21
21
|
|
|
22
22
|
from annotated_types import MinLen
|
|
23
23
|
from pydantic import BaseModel, ConfigDict, Field, conint, field_validator
|
|
@@ -31,10 +31,10 @@ from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE
|
|
|
31
31
|
from eodag.utils.exceptions import ValidationError
|
|
32
32
|
|
|
33
33
|
NumType = Union[float, int]
|
|
34
|
-
GeomArgs = Union[
|
|
34
|
+
GeomArgs = Union[list[NumType], tuple[NumType], dict[str, NumType], str, BaseGeometry]
|
|
35
35
|
|
|
36
36
|
PositiveInt = conint(gt=0)
|
|
37
|
-
SortByList = Annotated[
|
|
37
|
+
SortByList = Annotated[list[tuple[str, str]], MinLen(1)]
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class SearchArgs(BaseModel):
|
|
@@ -48,7 +48,7 @@ class SearchArgs(BaseModel):
|
|
|
48
48
|
start: Optional[str] = Field(None)
|
|
49
49
|
end: Optional[str] = Field(None)
|
|
50
50
|
geom: Optional[BaseGeometry] = Field(None)
|
|
51
|
-
locations: Optional[
|
|
51
|
+
locations: Optional[dict[str, str]] = Field(None)
|
|
52
52
|
page: Optional[int] = Field(DEFAULT_PAGE, gt=0) # type: ignore
|
|
53
53
|
items_per_page: Optional[PositiveInt] = Field(DEFAULT_ITEMS_PER_PAGE) # type: ignore
|
|
54
54
|
sort_by: Optional[SortByList] = Field(None) # type: ignore
|
eodag/types/whoosh.py
CHANGED
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
16
|
# See the License for the specific language governing permissions and
|
|
17
17
|
# limitations under the License.
|
|
18
|
-
from typing import List
|
|
19
|
-
|
|
20
18
|
from whoosh.fields import Schema
|
|
21
19
|
from whoosh.index import _DEF_INDEX_NAME, FileIndex
|
|
22
20
|
from whoosh.matching import NullMatcher
|
|
@@ -52,7 +50,7 @@ class EODAGQueryParser(QueryParser):
|
|
|
52
50
|
|
|
53
51
|
def __init__(
|
|
54
52
|
self,
|
|
55
|
-
filters:
|
|
53
|
+
filters: list[str],
|
|
56
54
|
schema: Schema,
|
|
57
55
|
):
|
|
58
56
|
"""
|