eodag 3.0.0b3__py3-none-any.whl → 3.1.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 +347 -247
- eodag/api/product/_assets.py +44 -15
- eodag/api/product/_product.py +58 -47
- 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 +129 -93
- eodag/api/search_result.py +28 -12
- eodag/cli.py +61 -24
- eodag/config.py +457 -167
- eodag/plugins/apis/base.py +10 -4
- eodag/plugins/apis/ecmwf.py +53 -23
- eodag/plugins/apis/usgs.py +41 -17
- eodag/plugins/authentication/aws_auth.py +30 -18
- eodag/plugins/authentication/base.py +14 -3
- eodag/plugins/authentication/generic.py +14 -3
- eodag/plugins/authentication/header.py +14 -6
- eodag/plugins/authentication/keycloak.py +44 -25
- eodag/plugins/authentication/oauth.py +18 -4
- eodag/plugins/authentication/openid_connect.py +192 -171
- eodag/plugins/authentication/qsauth.py +12 -4
- eodag/plugins/authentication/sas_auth.py +22 -5
- eodag/plugins/authentication/token.py +95 -17
- eodag/plugins/authentication/token_exchange.py +19 -19
- eodag/plugins/base.py +4 -4
- eodag/plugins/crunch/base.py +8 -5
- eodag/plugins/crunch/filter_date.py +9 -6
- eodag/plugins/crunch/filter_latest_intersect.py +9 -8
- eodag/plugins/crunch/filter_latest_tpl_name.py +8 -8
- eodag/plugins/crunch/filter_overlap.py +9 -11
- eodag/plugins/crunch/filter_property.py +10 -10
- eodag/plugins/download/aws.py +181 -105
- eodag/plugins/download/base.py +49 -67
- eodag/plugins/download/creodias_s3.py +40 -2
- eodag/plugins/download/http.py +247 -223
- eodag/plugins/download/s3rest.py +29 -28
- eodag/plugins/manager.py +176 -41
- eodag/plugins/search/__init__.py +6 -5
- eodag/plugins/search/base.py +123 -60
- eodag/plugins/search/build_search_result.py +1046 -355
- eodag/plugins/search/cop_marine.py +132 -39
- eodag/plugins/search/creodias_s3.py +19 -68
- eodag/plugins/search/csw.py +48 -8
- eodag/plugins/search/data_request_search.py +124 -23
- eodag/plugins/search/qssearch.py +531 -310
- eodag/plugins/search/stac_list_assets.py +85 -0
- eodag/plugins/search/static_stac_search.py +23 -24
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +1295 -355
- eodag/resources/providers.yml +1819 -3010
- eodag/resources/stac.yml +3 -163
- eodag/resources/stac_api.yml +2 -2
- eodag/resources/user_conf_template.yml +115 -99
- eodag/rest/cache.py +2 -2
- eodag/rest/config.py +3 -4
- eodag/rest/constants.py +0 -1
- eodag/rest/core.py +157 -117
- eodag/rest/errors.py +181 -0
- eodag/rest/server.py +57 -339
- eodag/rest/stac.py +133 -581
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +41 -30
- eodag/rest/types/queryables.py +42 -32
- eodag/rest/types/stac_search.py +15 -16
- eodag/rest/utils/__init__.py +14 -21
- eodag/rest/utils/cql_evaluate.py +6 -6
- eodag/rest/utils/rfc3339.py +2 -2
- eodag/types/__init__.py +153 -32
- eodag/types/bbox.py +2 -2
- eodag/types/download_args.py +4 -4
- eodag/types/queryables.py +183 -73
- eodag/types/search_args.py +6 -6
- eodag/types/whoosh.py +127 -3
- eodag/utils/__init__.py +228 -106
- eodag/utils/exceptions.py +47 -26
- eodag/utils/import_system.py +2 -2
- eodag/utils/logging.py +37 -77
- eodag/utils/repr.py +65 -6
- eodag/utils/requests.py +13 -15
- eodag/utils/rest.py +2 -2
- eodag/utils/s3.py +231 -0
- eodag/utils/stac_reader.py +11 -11
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/METADATA +81 -81
- eodag-3.1.0.dist-info/RECORD +113 -0
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/WHEEL +1 -1
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/entry_points.txt +5 -2
- eodag/resources/constraints/climate-dt.json +0 -13
- eodag/resources/constraints/extremes-dt.json +0 -8
- eodag/utils/constraints.py +0 -244
- eodag-3.0.0b3.dist-info/RECORD +0 -110
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/LICENSE +0 -0
- {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/top_level.txt +0 -0
eodag/api/search_result.py
CHANGED
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
from collections import UserList
|
|
21
|
-
from typing import TYPE_CHECKING,
|
|
21
|
+
from typing import TYPE_CHECKING, Annotated, Any, Iterable, Optional, Union
|
|
22
22
|
|
|
23
23
|
from shapely.geometry import GeometryCollection, shape
|
|
24
|
+
from typing_extensions import Doc
|
|
24
25
|
|
|
25
26
|
from eodag.api.product import EOProduct
|
|
26
27
|
from eodag.plugins.crunch.filter_date import FilterDate
|
|
@@ -45,13 +46,21 @@ class SearchResult(UserList):
|
|
|
45
46
|
:ivar number_matched: Estimated total number of matching results
|
|
46
47
|
"""
|
|
47
48
|
|
|
48
|
-
data:
|
|
49
|
+
data: list[EOProduct]
|
|
50
|
+
|
|
51
|
+
errors: Annotated[
|
|
52
|
+
list[tuple[str, Exception]], Doc("Tuple of provider name, exception")
|
|
53
|
+
]
|
|
49
54
|
|
|
50
55
|
def __init__(
|
|
51
|
-
self,
|
|
56
|
+
self,
|
|
57
|
+
products: list[EOProduct],
|
|
58
|
+
number_matched: Optional[int] = None,
|
|
59
|
+
errors: list[tuple[str, Exception]] = [],
|
|
52
60
|
) -> None:
|
|
53
|
-
super(
|
|
61
|
+
super().__init__(products)
|
|
54
62
|
self.number_matched = number_matched
|
|
63
|
+
self.errors = errors
|
|
55
64
|
|
|
56
65
|
def crunch(self, cruncher: Crunch, **search_params: Any) -> SearchResult:
|
|
57
66
|
"""Do some crunching with the underlying EO products.
|
|
@@ -73,7 +82,7 @@ class SearchResult(UserList):
|
|
|
73
82
|
return self.crunch(FilterDate(dict(start=start, end=end)))
|
|
74
83
|
|
|
75
84
|
def filter_latest_intersect(
|
|
76
|
-
self, geometry: Union[
|
|
85
|
+
self, geometry: Union[dict[str, Any], BaseGeometry, Any]
|
|
77
86
|
) -> SearchResult:
|
|
78
87
|
"""
|
|
79
88
|
Apply :class:`~eodag.plugins.crunch.filter_latest_intersect.FilterLatestIntersect` crunch,
|
|
@@ -129,7 +138,7 @@ class SearchResult(UserList):
|
|
|
129
138
|
return self.filter_property(storageStatus="ONLINE")
|
|
130
139
|
|
|
131
140
|
@staticmethod
|
|
132
|
-
def from_geojson(feature_collection:
|
|
141
|
+
def from_geojson(feature_collection: dict[str, Any]) -> SearchResult:
|
|
133
142
|
"""Builds an :class:`~eodag.api.search_result.SearchResult` object from its representation as geojson
|
|
134
143
|
|
|
135
144
|
:param feature_collection: A collection representing a search result.
|
|
@@ -142,7 +151,7 @@ class SearchResult(UserList):
|
|
|
142
151
|
]
|
|
143
152
|
)
|
|
144
153
|
|
|
145
|
-
def as_geojson_object(self) ->
|
|
154
|
+
def as_geojson_object(self) -> dict[str, Any]:
|
|
146
155
|
"""GeoJSON representation of SearchResult"""
|
|
147
156
|
return {
|
|
148
157
|
"type": "FeatureCollection",
|
|
@@ -163,7 +172,7 @@ class SearchResult(UserList):
|
|
|
163
172
|
return self.as_shapely_geometry_object().wkt
|
|
164
173
|
|
|
165
174
|
@property
|
|
166
|
-
def __geo_interface__(self) ->
|
|
175
|
+
def __geo_interface__(self) -> dict[str, Any]:
|
|
167
176
|
"""Implements the geo-interface protocol.
|
|
168
177
|
|
|
169
178
|
See https://gist.github.com/sgillies/2217756
|
|
@@ -197,6 +206,13 @@ class SearchResult(UserList):
|
|
|
197
206
|
+ "</table>"
|
|
198
207
|
)
|
|
199
208
|
|
|
209
|
+
def extend(self, other: Iterable) -> None:
|
|
210
|
+
"""override extend method to include errors"""
|
|
211
|
+
if isinstance(other, SearchResult):
|
|
212
|
+
self.errors.extend(other.errors)
|
|
213
|
+
|
|
214
|
+
return super().extend(other)
|
|
215
|
+
|
|
200
216
|
|
|
201
217
|
class RawSearchResult(UserList):
|
|
202
218
|
"""An object representing a collection of raw/unparsed search results obtained from a provider.
|
|
@@ -204,9 +220,9 @@ class RawSearchResult(UserList):
|
|
|
204
220
|
:param results: A list of raw/unparsed search results
|
|
205
221
|
"""
|
|
206
222
|
|
|
207
|
-
data:
|
|
208
|
-
query_params:
|
|
209
|
-
product_type_def_params:
|
|
223
|
+
data: list[Any]
|
|
224
|
+
query_params: dict[str, Any]
|
|
225
|
+
product_type_def_params: dict[str, Any]
|
|
210
226
|
|
|
211
|
-
def __init__(self, results:
|
|
227
|
+
def __init__(self, results: list[Any]) -> None:
|
|
212
228
|
super(RawSearchResult, self).__init__(results)
|
eodag/cli.py
CHANGED
|
@@ -39,6 +39,7 @@ Commands:
|
|
|
39
39
|
|
|
40
40
|
noqa: D103
|
|
41
41
|
"""
|
|
42
|
+
|
|
42
43
|
from __future__ import annotations
|
|
43
44
|
|
|
44
45
|
import json
|
|
@@ -47,7 +48,7 @@ import shutil
|
|
|
47
48
|
import sys
|
|
48
49
|
import textwrap
|
|
49
50
|
from importlib.metadata import metadata
|
|
50
|
-
from typing import TYPE_CHECKING, Any,
|
|
51
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
|
51
52
|
|
|
52
53
|
import click
|
|
53
54
|
|
|
@@ -56,6 +57,11 @@ from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE, parse_qs
|
|
|
56
57
|
from eodag.utils.exceptions import NoMatchingProductType, UnsupportedProvider
|
|
57
58
|
from eodag.utils.logging import setup_logging
|
|
58
59
|
|
|
60
|
+
try:
|
|
61
|
+
from eodag.rest.utils import LIVENESS_PROBE_PATH
|
|
62
|
+
except ImportError:
|
|
63
|
+
pass
|
|
64
|
+
|
|
59
65
|
if TYPE_CHECKING:
|
|
60
66
|
from click import Context
|
|
61
67
|
|
|
@@ -69,6 +75,18 @@ CRUNCHERS = [
|
|
|
69
75
|
]
|
|
70
76
|
|
|
71
77
|
|
|
78
|
+
class LivenessFilter:
|
|
79
|
+
"""
|
|
80
|
+
Filter out requests to the liveness probe endpoint
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def filter(self, record):
|
|
84
|
+
"""
|
|
85
|
+
Filter method required by the Python logging API.
|
|
86
|
+
"""
|
|
87
|
+
return LIVENESS_PROBE_PATH not in record.getMessage()
|
|
88
|
+
|
|
89
|
+
|
|
72
90
|
class MutuallyExclusiveOption(click.Option):
|
|
73
91
|
"""Mutually Exclusive Options for Click
|
|
74
92
|
from https://gist.github.com/jacobtolar/fb80d5552a9a9dfc32b12a829fa21c0c
|
|
@@ -86,7 +104,7 @@ class MutuallyExclusiveOption(click.Option):
|
|
|
86
104
|
super(MutuallyExclusiveOption, self).__init__(*args, **kwargs)
|
|
87
105
|
|
|
88
106
|
def handle_parse_result(
|
|
89
|
-
self, ctx: Context, opts: Mapping[str, Any], args:
|
|
107
|
+
self, ctx: Context, opts: Mapping[str, Any], args: list[str]
|
|
90
108
|
):
|
|
91
109
|
"""Raise error or use parent handle_parse_result()"""
|
|
92
110
|
if self.mutually_exclusive.intersection(opts) and self.name in opts:
|
|
@@ -341,9 +359,9 @@ def search_crunch(ctx: Context, **kwargs: Any) -> None:
|
|
|
341
359
|
count = kwargs.pop("count")
|
|
342
360
|
|
|
343
361
|
# Process inputs for crunch
|
|
344
|
-
cruncher_names:
|
|
362
|
+
cruncher_names: set[Any] = set(kwargs.pop("cruncher") or [])
|
|
345
363
|
cruncher_args = kwargs.pop("cruncher_args")
|
|
346
|
-
cruncher_args_dict:
|
|
364
|
+
cruncher_args_dict: dict[str, dict[str, Any]] = {}
|
|
347
365
|
if cruncher_args:
|
|
348
366
|
for cruncher, argname, argval in cruncher_args:
|
|
349
367
|
cruncher_args_dict.setdefault(cruncher, {}).setdefault(argname, argval)
|
|
@@ -553,12 +571,13 @@ def download(ctx: Context, **kwargs: Any) -> None:
|
|
|
553
571
|
|
|
554
572
|
for idx, product in enumerate(search_results):
|
|
555
573
|
if product.downloader is None:
|
|
574
|
+
downloader = satim_api._plugins_manager.get_download_plugin(product)
|
|
556
575
|
auth = product.downloader_auth
|
|
557
576
|
if auth is None:
|
|
558
|
-
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
)
|
|
577
|
+
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
578
|
+
downloader, product
|
|
579
|
+
)
|
|
580
|
+
search_results[idx].register_downloader(downloader, auth)
|
|
562
581
|
|
|
563
582
|
downloaded_file = product.get_quicklook()
|
|
564
583
|
if not downloaded_file:
|
|
@@ -573,12 +592,13 @@ def download(ctx: Context, **kwargs: Any) -> None:
|
|
|
573
592
|
# register downloader
|
|
574
593
|
for idx, product in enumerate(search_results):
|
|
575
594
|
if product.downloader is None:
|
|
595
|
+
downloader = satim_api._plugins_manager.get_download_plugin(product)
|
|
576
596
|
auth = product.downloader_auth
|
|
577
597
|
if auth is None:
|
|
578
|
-
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
)
|
|
598
|
+
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
599
|
+
downloader, product
|
|
600
|
+
)
|
|
601
|
+
search_results[idx].register_downloader(downloader, auth)
|
|
582
602
|
|
|
583
603
|
downloaded_files = satim_api.download_all(search_results)
|
|
584
604
|
if downloaded_files and len(downloaded_files) > 0:
|
|
@@ -676,7 +696,9 @@ def serve_rest(
|
|
|
676
696
|
try:
|
|
677
697
|
pid = os.fork()
|
|
678
698
|
except OSError as e:
|
|
679
|
-
raise Exception(
|
|
699
|
+
raise Exception(
|
|
700
|
+
"%s [%d]" % (e.strerror, e.errno) if e.errno is not None else e.strerror
|
|
701
|
+
)
|
|
680
702
|
|
|
681
703
|
if pid == 0:
|
|
682
704
|
os.setsid()
|
|
@@ -684,19 +706,34 @@ def serve_rest(
|
|
|
684
706
|
else:
|
|
685
707
|
sys.exit(0)
|
|
686
708
|
else:
|
|
709
|
+
import logging
|
|
710
|
+
|
|
687
711
|
logging_config = uvicorn.config.LOGGING_CONFIG
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
712
|
+
uvicorn_fmt = "%(asctime)-15s %(name)-32s [%(levelname)-8s] %(message)s"
|
|
713
|
+
logging_config["filters"] = {"liveness": {"()": LivenessFilter}}
|
|
714
|
+
logging_config["formatters"]["access"]["fmt"] = uvicorn_fmt
|
|
715
|
+
logging_config["formatters"]["default"]["fmt"] = uvicorn_fmt
|
|
716
|
+
logging_config["loggers"]["uvicorn.access"]["filters"] = ["liveness"]
|
|
717
|
+
|
|
718
|
+
eodag_formatter = logging.Formatter(
|
|
719
|
+
"%(asctime)-15s %(name)-32s [%(levelname)-8s] (tid=%(thread)d) %(message)s"
|
|
720
|
+
)
|
|
721
|
+
logging.getLogger("eodag").handlers[0].setFormatter(eodag_formatter)
|
|
722
|
+
|
|
723
|
+
if ctx.obj["verbosity"] <= 1:
|
|
724
|
+
logging_config["handlers"]["null"] = {
|
|
725
|
+
"level": "DEBUG",
|
|
726
|
+
"class": "logging.NullHandler",
|
|
699
727
|
}
|
|
728
|
+
logging_config["loggers"]["uvicorn"]["handlers"] = ["null"]
|
|
729
|
+
logging_config["loggers"]["uvicorn.error"]["handlers"] = ["null"]
|
|
730
|
+
logging_config["loggers"]["uvicorn.access"]["handlers"] = ["null"]
|
|
731
|
+
else:
|
|
732
|
+
log_level = "INFO" if ctx.obj["verbosity"] == 2 else "DEBUG"
|
|
733
|
+
logging_config["loggers"]["uvicorn"]["level"] = log_level
|
|
734
|
+
logging_config["loggers"]["uvicorn.error"]["level"] = log_level
|
|
735
|
+
logging_config["loggers"]["uvicorn.access"]["level"] = log_level
|
|
736
|
+
|
|
700
737
|
uvicorn.run(
|
|
701
738
|
"eodag.rest.server:app",
|
|
702
739
|
host=bind_host,
|