eodag 2.12.0__py3-none-any.whl → 3.0.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/__init__.py +6 -8
- eodag/api/core.py +654 -538
- eodag/api/product/__init__.py +12 -2
- eodag/api/product/_assets.py +59 -16
- eodag/api/product/_product.py +100 -93
- eodag/api/product/drivers/__init__.py +7 -2
- eodag/api/product/drivers/base.py +0 -3
- eodag/api/product/metadata_mapping.py +192 -96
- eodag/api/search_result.py +69 -10
- eodag/cli.py +55 -25
- eodag/config.py +391 -116
- eodag/plugins/apis/base.py +11 -165
- eodag/plugins/apis/ecmwf.py +36 -25
- eodag/plugins/apis/usgs.py +80 -35
- eodag/plugins/authentication/aws_auth.py +13 -4
- eodag/plugins/authentication/base.py +10 -1
- eodag/plugins/authentication/generic.py +2 -2
- eodag/plugins/authentication/header.py +31 -6
- eodag/plugins/authentication/keycloak.py +17 -84
- eodag/plugins/authentication/oauth.py +3 -3
- eodag/plugins/authentication/openid_connect.py +268 -49
- eodag/plugins/authentication/qsauth.py +4 -1
- eodag/plugins/authentication/sas_auth.py +9 -2
- eodag/plugins/authentication/token.py +98 -47
- eodag/plugins/authentication/token_exchange.py +122 -0
- eodag/plugins/crunch/base.py +3 -1
- eodag/plugins/crunch/filter_date.py +3 -9
- eodag/plugins/crunch/filter_latest_intersect.py +0 -3
- eodag/plugins/crunch/filter_latest_tpl_name.py +1 -4
- eodag/plugins/crunch/filter_overlap.py +4 -8
- eodag/plugins/crunch/filter_property.py +5 -11
- eodag/plugins/download/aws.py +149 -185
- eodag/plugins/download/base.py +88 -97
- eodag/plugins/download/creodias_s3.py +1 -1
- eodag/plugins/download/http.py +638 -310
- eodag/plugins/download/s3rest.py +47 -45
- eodag/plugins/manager.py +228 -88
- eodag/plugins/search/__init__.py +36 -0
- eodag/plugins/search/base.py +239 -30
- eodag/plugins/search/build_search_result.py +382 -37
- eodag/plugins/search/cop_marine.py +441 -0
- eodag/plugins/search/creodias_s3.py +25 -20
- eodag/plugins/search/csw.py +5 -7
- eodag/plugins/search/data_request_search.py +61 -30
- eodag/plugins/search/qssearch.py +713 -255
- eodag/plugins/search/static_stac_search.py +106 -40
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +1921 -34
- eodag/resources/providers.yml +4091 -3655
- eodag/resources/stac.yml +50 -216
- eodag/resources/stac_api.yml +71 -25
- eodag/resources/stac_provider.yml +5 -0
- eodag/resources/user_conf_template.yml +89 -32
- eodag/rest/__init__.py +6 -0
- eodag/rest/cache.py +70 -0
- eodag/rest/config.py +68 -0
- eodag/rest/constants.py +26 -0
- eodag/rest/core.py +735 -0
- eodag/rest/errors.py +178 -0
- eodag/rest/server.py +264 -431
- eodag/rest/stac.py +442 -836
- eodag/rest/types/collections_search.py +44 -0
- eodag/rest/types/eodag_search.py +238 -47
- eodag/rest/types/queryables.py +164 -0
- eodag/rest/types/stac_search.py +273 -0
- eodag/rest/utils/__init__.py +216 -0
- eodag/rest/utils/cql_evaluate.py +119 -0
- eodag/rest/utils/rfc3339.py +64 -0
- eodag/types/__init__.py +106 -10
- eodag/types/bbox.py +15 -14
- eodag/types/download_args.py +40 -0
- eodag/types/search_args.py +57 -7
- eodag/types/whoosh.py +79 -0
- eodag/utils/__init__.py +110 -91
- eodag/utils/constraints.py +37 -45
- eodag/utils/exceptions.py +39 -22
- eodag/utils/import_system.py +0 -4
- eodag/utils/logging.py +37 -80
- eodag/utils/notebook.py +4 -4
- eodag/utils/repr.py +113 -0
- eodag/utils/requests.py +128 -0
- eodag/utils/rest.py +100 -0
- eodag/utils/stac_reader.py +93 -21
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/METADATA +88 -53
- eodag-3.0.0.dist-info/RECORD +109 -0
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/WHEEL +1 -1
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/entry_points.txt +7 -5
- eodag/plugins/apis/cds.py +0 -540
- eodag/rest/types/stac_queryables.py +0 -134
- eodag/rest/utils.py +0 -1133
- eodag-2.12.0.dist-info/RECORD +0 -94
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/LICENSE +0 -0
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/top_level.txt +0 -0
eodag/plugins/download/s3rest.py
CHANGED
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
import hashlib
|
|
21
20
|
import logging
|
|
22
21
|
import os
|
|
23
22
|
import os.path
|
|
24
|
-
from typing import TYPE_CHECKING,
|
|
23
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Union
|
|
25
24
|
from xml.dom import minidom
|
|
26
25
|
from xml.parsers.expat import ExpatError
|
|
27
26
|
|
|
28
27
|
import requests
|
|
29
28
|
from requests import RequestException
|
|
29
|
+
from requests.auth import AuthBase
|
|
30
30
|
|
|
31
31
|
from eodag.api.product.metadata_mapping import OFFLINE_STATUS, ONLINE_STATUS
|
|
32
32
|
from eodag.plugins.download.base import Download
|
|
@@ -46,6 +46,7 @@ from eodag.utils import (
|
|
|
46
46
|
from eodag.utils.exceptions import (
|
|
47
47
|
AuthenticationError,
|
|
48
48
|
DownloadError,
|
|
49
|
+
MisconfiguredError,
|
|
49
50
|
NotAvailableError,
|
|
50
51
|
RequestError,
|
|
51
52
|
)
|
|
@@ -53,6 +54,8 @@ from eodag.utils.exceptions import (
|
|
|
53
54
|
if TYPE_CHECKING:
|
|
54
55
|
from eodag.api.product import EOProduct
|
|
55
56
|
from eodag.config import PluginConfig
|
|
57
|
+
from eodag.types.download_args import DownloadConf
|
|
58
|
+
from eodag.utils import Unpack
|
|
56
59
|
|
|
57
60
|
logger = logging.getLogger("eodag.download.s3rest")
|
|
58
61
|
|
|
@@ -65,7 +68,6 @@ class S3RestDownload(Download):
|
|
|
65
68
|
Re-use AwsDownload bucket some handling methods
|
|
66
69
|
|
|
67
70
|
:param provider: provider name
|
|
68
|
-
:type provider: str
|
|
69
71
|
:param config: Download plugin configuration:
|
|
70
72
|
|
|
71
73
|
* ``config.base_uri`` (str) - default endpoint url
|
|
@@ -76,12 +78,7 @@ class S3RestDownload(Download):
|
|
|
76
78
|
* ``config.order_method`` (str) - (optional) HTTP request method, GET (default) or POST
|
|
77
79
|
* ``config.order_headers`` (dict) - (optional) order request headers
|
|
78
80
|
* ``config.order_on_response`` (dict) - (optional) edit or add new product properties
|
|
79
|
-
* ``config.
|
|
80
|
-
* ``config.order_status_percent`` (str) - (optional) progress percentage key in obtained status response
|
|
81
|
-
* ``config.order_status_success`` (dict) - (optional) key/value identifying an error success
|
|
82
|
-
* ``config.order_status_on_success`` (dict) - (optional) edit or add new product properties
|
|
83
|
-
|
|
84
|
-
:type config: :class:`~eodag.config.PluginConfig`
|
|
81
|
+
* ``config.order_status`` (:class:`~eodag.config.PluginConfig.OrderStatus`) - Order status handling
|
|
85
82
|
"""
|
|
86
83
|
|
|
87
84
|
def __init__(self, provider: str, config: PluginConfig) -> None:
|
|
@@ -91,32 +88,30 @@ class S3RestDownload(Download):
|
|
|
91
88
|
def download(
|
|
92
89
|
self,
|
|
93
90
|
product: EOProduct,
|
|
94
|
-
auth: Optional[
|
|
91
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
95
92
|
progress_callback: Optional[ProgressCallback] = None,
|
|
96
93
|
wait: int = DEFAULT_DOWNLOAD_WAIT,
|
|
97
94
|
timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
98
|
-
**kwargs:
|
|
95
|
+
**kwargs: Unpack[DownloadConf],
|
|
99
96
|
) -> Optional[str]:
|
|
100
97
|
"""Download method for S3 REST API.
|
|
101
98
|
|
|
102
99
|
:param product: The EO product to download
|
|
103
|
-
:
|
|
104
|
-
:param auth: (optional) The configuration of a plugin of type Authentication
|
|
105
|
-
:type auth: :class:`~eodag.config.PluginConfig`
|
|
100
|
+
:param auth: (optional) authenticated object
|
|
106
101
|
:param progress_callback: (optional) A method or a callable object
|
|
107
102
|
which takes a current size and a maximum
|
|
108
103
|
size as inputs and handle progress bar
|
|
109
104
|
creation and update to give the user a
|
|
110
105
|
feedback on the download progress
|
|
111
|
-
:
|
|
112
|
-
:param kwargs: `outputs_prefix` (str), `extract` (bool), `delete_archive` (bool)
|
|
106
|
+
:param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
|
|
113
107
|
and `dl_url_params` (dict) can be provided as additional kwargs
|
|
114
108
|
and will override any other values defined in a configuration
|
|
115
109
|
file or with environment variables.
|
|
116
|
-
:type kwargs: Union[str, bool, dict]
|
|
117
110
|
:returns: The absolute path to the downloaded product in the local filesystem
|
|
118
|
-
:rtype: str
|
|
119
111
|
"""
|
|
112
|
+
if auth is not None and not isinstance(auth, AuthBase):
|
|
113
|
+
raise MisconfiguredError(f"Incompatible auth plugin: {type(auth)}")
|
|
114
|
+
|
|
120
115
|
if progress_callback is None:
|
|
121
116
|
logger.info(
|
|
122
117
|
"Progress bar unavailable, please call product.download() instead of plugin.download()"
|
|
@@ -130,19 +125,19 @@ class S3RestDownload(Download):
|
|
|
130
125
|
and "storageStatus" in product.properties
|
|
131
126
|
and product.properties["storageStatus"] != ONLINE_STATUS
|
|
132
127
|
):
|
|
133
|
-
self.http_download_plugin.
|
|
128
|
+
self.http_download_plugin.order_download(product=product, auth=auth)
|
|
134
129
|
|
|
135
130
|
@self._download_retry(product, wait, timeout)
|
|
136
131
|
def download_request(
|
|
137
132
|
product: EOProduct,
|
|
138
|
-
auth:
|
|
133
|
+
auth: AuthBase,
|
|
139
134
|
progress_callback: ProgressCallback,
|
|
140
135
|
ordered_message: str,
|
|
141
|
-
**kwargs:
|
|
136
|
+
**kwargs: Unpack[DownloadConf],
|
|
142
137
|
):
|
|
143
138
|
# check order status
|
|
144
139
|
if product.properties.get("orderStatusLink", None):
|
|
145
|
-
self.http_download_plugin.
|
|
140
|
+
self.http_download_plugin.order_download_status(
|
|
146
141
|
product=product, auth=auth
|
|
147
142
|
)
|
|
148
143
|
|
|
@@ -150,6 +145,8 @@ class S3RestDownload(Download):
|
|
|
150
145
|
bucket_name, prefix = get_bucket_name_and_prefix(
|
|
151
146
|
url=product.location, bucket_path_level=self.config.bucket_path_level
|
|
152
147
|
)
|
|
148
|
+
if prefix is None:
|
|
149
|
+
raise DownloadError(f"Could not extract prefix from {product.location}")
|
|
153
150
|
|
|
154
151
|
if (
|
|
155
152
|
bucket_name is None
|
|
@@ -172,8 +169,16 @@ class S3RestDownload(Download):
|
|
|
172
169
|
|
|
173
170
|
# get nodes/files list contained in the bucket
|
|
174
171
|
logger.debug("Retrieving product content from %s", nodes_list_url)
|
|
172
|
+
|
|
173
|
+
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
174
|
+
timeout = getattr(self.config, "timeout", HTTP_REQ_TIMEOUT)
|
|
175
|
+
|
|
175
176
|
bucket_contents = requests.get(
|
|
176
|
-
nodes_list_url,
|
|
177
|
+
nodes_list_url,
|
|
178
|
+
auth=auth,
|
|
179
|
+
headers=USER_AGENT,
|
|
180
|
+
timeout=timeout,
|
|
181
|
+
verify=ssl_verify,
|
|
177
182
|
)
|
|
178
183
|
try:
|
|
179
184
|
bucket_contents.raise_for_status()
|
|
@@ -184,12 +189,9 @@ class S3RestDownload(Download):
|
|
|
184
189
|
auth_errors = [auth_errors]
|
|
185
190
|
if err.response and err.response.status_code in auth_errors:
|
|
186
191
|
raise AuthenticationError(
|
|
187
|
-
"
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
err.response.text.strip(),
|
|
191
|
-
self.provider,
|
|
192
|
-
)
|
|
192
|
+
f"Please check your credentials for {self.provider}.",
|
|
193
|
+
f"HTTP Error {err.response.status_code} returned.",
|
|
194
|
+
err.response.text.strip(),
|
|
193
195
|
)
|
|
194
196
|
# product not available
|
|
195
197
|
elif (
|
|
@@ -220,7 +222,7 @@ class S3RestDownload(Download):
|
|
|
220
222
|
self.__class__.__name__,
|
|
221
223
|
bucket_contents.text,
|
|
222
224
|
)
|
|
223
|
-
raise RequestError(
|
|
225
|
+
raise RequestError.from_error(err) from err
|
|
224
226
|
try:
|
|
225
227
|
xmldoc = minidom.parseString(bucket_contents.text)
|
|
226
228
|
except ExpatError as err:
|
|
@@ -232,14 +234,12 @@ class S3RestDownload(Download):
|
|
|
232
234
|
logger.warning("Could not load any content from %s", nodes_list_url)
|
|
233
235
|
|
|
234
236
|
# destination product path
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
)
|
|
238
|
-
abs_outputs_prefix = os.path.abspath(outputs_prefix)
|
|
239
|
-
product_local_path = os.path.join(abs_outputs_prefix, prefix.split("/")[-1])
|
|
237
|
+
output_dir = kwargs.pop("output_dir", None) or self.config.output_dir
|
|
238
|
+
abs_output_dir = os.path.abspath(output_dir)
|
|
239
|
+
product_local_path = os.path.join(abs_output_dir, prefix.split("/")[-1])
|
|
240
240
|
|
|
241
241
|
# .downloaded cache record directory
|
|
242
|
-
download_records_dir = os.path.join(
|
|
242
|
+
download_records_dir = os.path.join(abs_output_dir, ".downloaded")
|
|
243
243
|
try:
|
|
244
244
|
os.makedirs(download_records_dir)
|
|
245
245
|
except OSError as exc:
|
|
@@ -252,8 +252,9 @@ class S3RestDownload(Download):
|
|
|
252
252
|
"Unable to create records directory. Got:\n%s", tb.format_exc()
|
|
253
253
|
)
|
|
254
254
|
# check if product has already been downloaded
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
record_filename = os.path.join(
|
|
256
|
+
download_records_dir, self.generate_record_hash(product)
|
|
257
|
+
)
|
|
257
258
|
if os.path.isfile(record_filename) and os.path.exists(product_local_path):
|
|
258
259
|
product.location = path_to_uri(product_local_path)
|
|
259
260
|
return product_local_path
|
|
@@ -266,18 +267,18 @@ class S3RestDownload(Download):
|
|
|
266
267
|
os.remove(record_filename)
|
|
267
268
|
|
|
268
269
|
# total size for progress_callback
|
|
269
|
-
|
|
270
|
-
[
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
)
|
|
270
|
+
size_list: List[int] = [
|
|
271
|
+
int(node.firstChild.nodeValue) # type: ignore[attr-defined]
|
|
272
|
+
for node in xmldoc.getElementsByTagName("Size")
|
|
273
|
+
if node.firstChild is not None
|
|
274
|
+
]
|
|
275
|
+
total_size = sum(size_list)
|
|
275
276
|
progress_callback.reset(total=total_size)
|
|
276
277
|
|
|
277
278
|
# download each node key
|
|
278
279
|
for node_xml in nodes_xml_list:
|
|
279
280
|
node_key = unquote(
|
|
280
|
-
node_xml.getElementsByTagName("Key")[0].firstChild.nodeValue
|
|
281
|
+
node_xml.getElementsByTagName("Key")[0].firstChild.nodeValue # type: ignore[union-attr]
|
|
281
282
|
)
|
|
282
283
|
# As "Key", "Size" and "ETag" (md5 hash) can also be retrieved from node_xml
|
|
283
284
|
node_url = urljoin(bucket_url.strip("/") + "/", node_key.strip("/"))
|
|
@@ -303,6 +304,7 @@ class S3RestDownload(Download):
|
|
|
303
304
|
auth=auth,
|
|
304
305
|
headers=USER_AGENT,
|
|
305
306
|
timeout=DEFAULT_STREAM_REQUESTS_TIMEOUT,
|
|
307
|
+
verify=ssl_verify,
|
|
306
308
|
) as stream:
|
|
307
309
|
try:
|
|
308
310
|
stream.raise_for_status()
|