eodag 2.12.1__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 -168
- 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.1.dist-info → eodag-3.0.0.dist-info}/METADATA +88 -53
- eodag-3.0.0.dist-info/RECORD +109 -0
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/WHEEL +1 -1
- {eodag-2.12.1.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.1.dist-info/RECORD +0 -94
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/LICENSE +0 -0
- {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/top_level.txt +0 -0
eodag/plugins/download/base.py
CHANGED
|
@@ -43,6 +43,7 @@ from eodag.utils import (
|
|
|
43
43
|
DEFAULT_DOWNLOAD_TIMEOUT,
|
|
44
44
|
DEFAULT_DOWNLOAD_WAIT,
|
|
45
45
|
ProgressCallback,
|
|
46
|
+
StreamResponse,
|
|
46
47
|
sanitize,
|
|
47
48
|
uri_to_path,
|
|
48
49
|
)
|
|
@@ -54,10 +55,13 @@ from eodag.utils.exceptions import (
|
|
|
54
55
|
from eodag.utils.notebook import NotebookWidgets
|
|
55
56
|
|
|
56
57
|
if TYPE_CHECKING:
|
|
58
|
+
from requests.auth import AuthBase
|
|
59
|
+
|
|
57
60
|
from eodag.api.product import EOProduct
|
|
58
61
|
from eodag.api.search_result import SearchResult
|
|
59
62
|
from eodag.config import PluginConfig
|
|
60
|
-
from eodag.
|
|
63
|
+
from eodag.types.download_args import DownloadConf
|
|
64
|
+
from eodag.utils import DownloadedCallback, Unpack
|
|
61
65
|
|
|
62
66
|
|
|
63
67
|
logger = logging.getLogger("eodag.download.base")
|
|
@@ -75,20 +79,20 @@ class Download(PluginTopic):
|
|
|
75
79
|
|
|
76
80
|
They must:
|
|
77
81
|
|
|
78
|
-
- download data in the ``
|
|
82
|
+
- download data in the ``output_dir`` folder defined in the plugin's
|
|
79
83
|
configuration or passed through kwargs
|
|
80
84
|
- extract products from their archive (if relevant) if ``extract`` is set to True
|
|
81
85
|
(True by default)
|
|
82
|
-
- save a product in an archive/directory (in ``
|
|
86
|
+
- save a product in an archive/directory (in ``output_dir``) whose name must be
|
|
83
87
|
the product's ``title`` property
|
|
84
88
|
- update the product's ``location`` attribute once its data is downloaded (and
|
|
85
89
|
eventually after it's extracted) to the product's location given as a file URI
|
|
86
90
|
(e.g. 'file:///tmp/product_folder' on Linux or
|
|
87
91
|
'file:///C:/Users/username/AppData/LOcal/Temp' on Windows)
|
|
88
|
-
- save a *record* file in the directory ``
|
|
89
|
-
is built on the MD5 hash of the product's ``
|
|
90
|
-
(``hashlib.md5(
|
|
91
|
-
the product's ``remote_location`` attribute itself.
|
|
92
|
+
- save a *record* file in the directory ``output_dir/.downloaded`` whose name
|
|
93
|
+
is built on the MD5 hash of the product's ``product_type`` and ``properties['id']``
|
|
94
|
+
attributes (``hashlib.md5((product.product_type+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
|
|
95
|
+
and whose content is the product's ``remote_location`` attribute itself.
|
|
92
96
|
- not try to download a product whose ``location`` attribute already points to an
|
|
93
97
|
existing file/directory
|
|
94
98
|
- not try to download a product if its *record* file exists as long as the expected
|
|
@@ -96,9 +100,7 @@ class Download(PluginTopic):
|
|
|
96
100
|
(it certainly indicates that the download didn't complete)
|
|
97
101
|
|
|
98
102
|
:param provider: An eodag providers configuration dictionary
|
|
99
|
-
:type provider: dict
|
|
100
103
|
:param config: Path to the user configuration file
|
|
101
|
-
:type config: str
|
|
102
104
|
"""
|
|
103
105
|
|
|
104
106
|
def __init__(self, provider: str, config: PluginConfig) -> None:
|
|
@@ -108,35 +110,28 @@ class Download(PluginTopic):
|
|
|
108
110
|
def download(
|
|
109
111
|
self,
|
|
110
112
|
product: EOProduct,
|
|
111
|
-
auth: Optional[
|
|
113
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
112
114
|
progress_callback: Optional[ProgressCallback] = None,
|
|
113
115
|
wait: int = DEFAULT_DOWNLOAD_WAIT,
|
|
114
116
|
timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
115
|
-
**kwargs:
|
|
117
|
+
**kwargs: Unpack[DownloadConf],
|
|
116
118
|
) -> Optional[str]:
|
|
117
119
|
r"""
|
|
118
120
|
Base download method. Not available, it must be defined for each plugin.
|
|
119
121
|
|
|
120
122
|
:param product: The EO product to download
|
|
121
|
-
:
|
|
122
|
-
:param auth: (optional) The configuration of a plugin of type Authentication
|
|
123
|
-
:type auth: :class:`~eodag.config.PluginConfig`
|
|
123
|
+
:param auth: (optional) authenticated object
|
|
124
124
|
:param progress_callback: (optional) A progress callback
|
|
125
|
-
:type progress_callback: :class:`~eodag.utils.ProgressCallback`
|
|
126
125
|
:param wait: (optional) If download fails, wait time in minutes between two download tries
|
|
127
|
-
:type wait: int
|
|
128
126
|
:param timeout: (optional) If download fails, maximum time in minutes before stop retrying
|
|
129
127
|
to download
|
|
130
|
-
:
|
|
131
|
-
:param kwargs: `outputs_prefix` (str), `extract` (bool), `delete_archive` (bool)
|
|
128
|
+
:param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
|
|
132
129
|
and `dl_url_params` (dict) can be provided as additional kwargs
|
|
133
130
|
and will override any other values defined in a configuration
|
|
134
131
|
file or with environment variables.
|
|
135
|
-
:type kwargs: Union[str, bool, dict]
|
|
136
132
|
:returns: The absolute path to the downloaded product in the local filesystem
|
|
137
133
|
(e.g. '/tmp/product.zip' on Linux or
|
|
138
134
|
'C:\\Users\\username\\AppData\\Local\\Temp\\product.zip' on Windows)
|
|
139
|
-
:rtype: str
|
|
140
135
|
"""
|
|
141
136
|
raise NotImplementedError(
|
|
142
137
|
"A Download plugin must implement a method named download"
|
|
@@ -145,33 +140,26 @@ class Download(PluginTopic):
|
|
|
145
140
|
def _stream_download_dict(
|
|
146
141
|
self,
|
|
147
142
|
product: EOProduct,
|
|
148
|
-
auth: Optional[
|
|
143
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
149
144
|
progress_callback: Optional[ProgressCallback] = None,
|
|
150
145
|
wait: int = DEFAULT_DOWNLOAD_WAIT,
|
|
151
146
|
timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
152
|
-
**kwargs:
|
|
153
|
-
) ->
|
|
147
|
+
**kwargs: Unpack[DownloadConf],
|
|
148
|
+
) -> StreamResponse:
|
|
154
149
|
r"""
|
|
155
150
|
Base _stream_download_dict method. Not available, it must be defined for each plugin.
|
|
156
151
|
|
|
157
152
|
:param product: The EO product to download
|
|
158
|
-
:
|
|
159
|
-
:param auth: (optional) The configuration of a plugin of type Authentication
|
|
160
|
-
:type auth: :class:`~eodag.config.PluginConfig`
|
|
153
|
+
:param auth: (optional) authenticated object
|
|
161
154
|
:param progress_callback: (optional) A progress callback
|
|
162
|
-
:type progress_callback: :class:`~eodag.utils.ProgressCallback`
|
|
163
155
|
:param wait: (optional) If download fails, wait time in minutes between two download tries
|
|
164
|
-
:type wait: int
|
|
165
156
|
:param timeout: (optional) If download fails, maximum time in minutes before stop retrying
|
|
166
157
|
to download
|
|
167
|
-
:
|
|
168
|
-
:param kwargs: `outputs_prefix` (str), `extract` (bool), `delete_archive` (bool)
|
|
158
|
+
:param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
|
|
169
159
|
and `dl_url_params` (dict) can be provided as additional kwargs
|
|
170
160
|
and will override any other values defined in a configuration
|
|
171
161
|
file or with environment variables.
|
|
172
|
-
:
|
|
173
|
-
:returns: Dictionnary of :class:`~fastapi.responses.StreamingResponse` keyword-arguments
|
|
174
|
-
:rtype: dict
|
|
162
|
+
:returns: Dictionary of :class:`~fastapi.responses.StreamingResponse` keyword-arguments
|
|
175
163
|
"""
|
|
176
164
|
raise NotImplementedError(
|
|
177
165
|
"Download streaming must be implemented using a method named _stream_download_dict"
|
|
@@ -181,16 +169,13 @@ class Download(PluginTopic):
|
|
|
181
169
|
self,
|
|
182
170
|
product: EOProduct,
|
|
183
171
|
progress_callback: Optional[ProgressCallback] = None,
|
|
184
|
-
**kwargs:
|
|
172
|
+
**kwargs: Unpack[DownloadConf],
|
|
185
173
|
) -> Tuple[Optional[str], Optional[str]]:
|
|
186
174
|
"""Check if file has already been downloaded, and prepare product download
|
|
187
175
|
|
|
188
176
|
:param product: The EO product to download
|
|
189
|
-
:type product: :class:`~eodag.api.product._product.EOProduct`
|
|
190
177
|
:param progress_callback: (optional) A progress callback
|
|
191
|
-
:type progress_callback: :class:`~eodag.utils.ProgressCallback` or None
|
|
192
178
|
:returns: fs_path, record_filename
|
|
193
|
-
:rtype: Tuple[Optional[str], Optional[str]]
|
|
194
179
|
"""
|
|
195
180
|
if product.location != product.remote_location:
|
|
196
181
|
fs_path = uri_to_path(product.location)
|
|
@@ -212,18 +197,18 @@ class Download(PluginTopic):
|
|
|
212
197
|
f"Download url: {url}",
|
|
213
198
|
)
|
|
214
199
|
|
|
215
|
-
|
|
216
|
-
kwargs.pop("
|
|
217
|
-
or getattr(self.config, "
|
|
200
|
+
output_dir = (
|
|
201
|
+
kwargs.pop("output_dir", None)
|
|
202
|
+
or getattr(self.config, "output_dir", tempfile.gettempdir())
|
|
218
203
|
or tempfile.gettempdir()
|
|
219
204
|
)
|
|
220
|
-
|
|
221
|
-
self.config, "
|
|
205
|
+
output_extension = kwargs.get("output_extension", None) or getattr(
|
|
206
|
+
self.config, "output_extension", ".zip"
|
|
222
207
|
)
|
|
223
208
|
|
|
224
209
|
# Strong asumption made here: all products downloaded will be zip files
|
|
225
210
|
# If they are not, the '.zip' extension will be removed when they are downloaded and returned as is
|
|
226
|
-
prefix = os.path.abspath(
|
|
211
|
+
prefix = os.path.abspath(output_dir)
|
|
227
212
|
sanitized_title = sanitize(product.properties["title"])
|
|
228
213
|
if sanitized_title == product.properties["title"]:
|
|
229
214
|
collision_avoidance_suffix = ""
|
|
@@ -231,9 +216,11 @@ class Download(PluginTopic):
|
|
|
231
216
|
collision_avoidance_suffix = "-" + sanitize(product.properties["id"])
|
|
232
217
|
fs_path = os.path.join(
|
|
233
218
|
prefix,
|
|
234
|
-
f"{sanitize(product.properties['title'])}{collision_avoidance_suffix}{
|
|
219
|
+
f"{sanitize(product.properties['title'])}{collision_avoidance_suffix}{output_extension}",
|
|
220
|
+
)
|
|
221
|
+
fs_dir_path = (
|
|
222
|
+
fs_path.replace(output_extension, "") if output_extension else fs_path
|
|
235
223
|
)
|
|
236
|
-
fs_dir_path = fs_path.replace(outputs_extension, "")
|
|
237
224
|
download_records_dir = os.path.join(prefix, ".downloaded")
|
|
238
225
|
try:
|
|
239
226
|
os.makedirs(download_records_dir)
|
|
@@ -246,8 +233,9 @@ class Download(PluginTopic):
|
|
|
246
233
|
logger.warning(
|
|
247
234
|
f"Unable to create records directory. Got:\n{tb.format_exc()}",
|
|
248
235
|
)
|
|
249
|
-
|
|
250
|
-
|
|
236
|
+
record_filename = os.path.join(
|
|
237
|
+
download_records_dir, self.generate_record_hash(product)
|
|
238
|
+
)
|
|
251
239
|
if os.path.isfile(record_filename) and os.path.isfile(fs_path):
|
|
252
240
|
logger.info(
|
|
253
241
|
f"Product already downloaded: {fs_path}",
|
|
@@ -278,6 +266,19 @@ class Download(PluginTopic):
|
|
|
278
266
|
|
|
279
267
|
return fs_path, record_filename
|
|
280
268
|
|
|
269
|
+
def generate_record_hash(self, product: EOProduct) -> str:
|
|
270
|
+
"""Generate the record hash of the given product.
|
|
271
|
+
|
|
272
|
+
The MD5 hash is built from the product's ``product_type`` and ``properties['id']`` attributes
|
|
273
|
+
(``hashlib.md5((product.product_type+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
|
|
274
|
+
|
|
275
|
+
:param product: The product to calculate the record hash
|
|
276
|
+
:returns: The MD5 hash
|
|
277
|
+
"""
|
|
278
|
+
# In some unit tests, `product.product_type` is `None` and `product.properties["id"]` is `ìnt`
|
|
279
|
+
product_hash = str(product.product_type) + "-" + str(product.properties["id"])
|
|
280
|
+
return hashlib.md5(product_hash.encode("utf-8")).hexdigest()
|
|
281
|
+
|
|
281
282
|
def _resolve_archive_depth(self, product_path: str) -> str:
|
|
282
283
|
"""Update product_path using archive_depth from provider configuration.
|
|
283
284
|
|
|
@@ -287,9 +288,7 @@ class Download(PluginTopic):
|
|
|
287
288
|
WARNING: A strong assumption is made here: there is only one subdirectory per level
|
|
288
289
|
|
|
289
290
|
:param product_path: The path to the extracted product
|
|
290
|
-
:type product_path: str
|
|
291
291
|
:returns: The path to the extracted product with the right depth
|
|
292
|
-
:rtype: str
|
|
293
292
|
"""
|
|
294
293
|
archive_depth = getattr(self.config, "archive_depth", 1)
|
|
295
294
|
count = 1
|
|
@@ -302,16 +301,13 @@ class Download(PluginTopic):
|
|
|
302
301
|
self,
|
|
303
302
|
fs_path: str,
|
|
304
303
|
progress_callback: Optional[ProgressCallback] = None,
|
|
305
|
-
**kwargs:
|
|
304
|
+
**kwargs: Unpack[DownloadConf],
|
|
306
305
|
) -> str:
|
|
307
306
|
"""Finalize the download process.
|
|
308
307
|
|
|
309
308
|
:param fs_path: The path to the local zip archive downloaded or already present
|
|
310
|
-
:type fs_path: str
|
|
311
309
|
:param progress_callback: (optional) A progress callback
|
|
312
|
-
:type progress_callback: :class:`~eodag.utils.ProgressCallback` or None
|
|
313
310
|
:returns: The absolute path to the product
|
|
314
|
-
:rtype: str
|
|
315
311
|
"""
|
|
316
312
|
# progress bar init
|
|
317
313
|
if progress_callback is None:
|
|
@@ -332,22 +328,22 @@ class Download(PluginTopic):
|
|
|
332
328
|
extract = (
|
|
333
329
|
extract if extract is not None else getattr(self.config, "extract", True)
|
|
334
330
|
)
|
|
331
|
+
if not extract:
|
|
332
|
+
logger.info("Extraction not activated. The product is available as is.")
|
|
333
|
+
progress_callback(1, total=1)
|
|
334
|
+
return fs_path
|
|
335
|
+
|
|
335
336
|
delete_archive = kwargs.pop("delete_archive", None)
|
|
336
337
|
delete_archive = (
|
|
337
338
|
delete_archive
|
|
338
339
|
if delete_archive is not None
|
|
339
340
|
else getattr(self.config, "delete_archive", True)
|
|
340
341
|
)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if not extract:
|
|
344
|
-
logger.info("Extraction not activated. The product is available as is.")
|
|
345
|
-
progress_callback(1, total=1)
|
|
346
|
-
return fs_path
|
|
342
|
+
output_extension = kwargs.pop("output_extension", ".zip")
|
|
347
343
|
|
|
348
344
|
product_path = (
|
|
349
|
-
fs_path[: fs_path.index(
|
|
350
|
-
if
|
|
345
|
+
fs_path[: fs_path.index(output_extension)]
|
|
346
|
+
if output_extension in fs_path
|
|
351
347
|
else fs_path
|
|
352
348
|
)
|
|
353
349
|
product_path_exists = os.path.exists(product_path)
|
|
@@ -373,11 +369,8 @@ class Download(PluginTopic):
|
|
|
373
369
|
f"Extraction cancelled, destination directory already exists and is not empty: {product_path}"
|
|
374
370
|
)
|
|
375
371
|
progress_callback(1, total=1)
|
|
376
|
-
product_path = self._resolve_archive_depth(product_path)
|
|
377
372
|
return product_path
|
|
378
|
-
|
|
379
|
-
kwargs.pop("outputs_prefix", None) or self.config.outputs_prefix
|
|
380
|
-
)
|
|
373
|
+
output_dir = kwargs.pop("output_dir", None) or self.config.output_dir
|
|
381
374
|
|
|
382
375
|
if not os.path.exists(product_path):
|
|
383
376
|
logger.info("Extraction activated")
|
|
@@ -386,9 +379,9 @@ class Download(PluginTopic):
|
|
|
386
379
|
)
|
|
387
380
|
progress_callback.refresh()
|
|
388
381
|
|
|
389
|
-
|
|
382
|
+
product_dir = os.path.join(output_dir, product_path)
|
|
390
383
|
tmp_dir = tempfile.TemporaryDirectory()
|
|
391
|
-
extraction_dir = os.path.join(tmp_dir.name, os.path.basename(
|
|
384
|
+
extraction_dir = os.path.join(tmp_dir.name, os.path.basename(product_dir))
|
|
392
385
|
|
|
393
386
|
if fs_path.endswith(".zip"):
|
|
394
387
|
with zipfile.ZipFile(fs_path, "r") as zfile:
|
|
@@ -402,14 +395,28 @@ class Download(PluginTopic):
|
|
|
402
395
|
path=extraction_dir,
|
|
403
396
|
)
|
|
404
397
|
progress_callback(1)
|
|
405
|
-
|
|
398
|
+
# in some cases, only a lone file is extracted without being in a directory
|
|
399
|
+
# then, we create a directory in which we place this file
|
|
400
|
+
product_extraction_path = self._resolve_archive_depth(extraction_dir)
|
|
401
|
+
if os.path.isfile(product_extraction_path) and not os.path.isdir(
|
|
402
|
+
product_dir
|
|
403
|
+
):
|
|
404
|
+
os.makedirs(product_dir)
|
|
405
|
+
shutil.move(product_extraction_path, product_dir)
|
|
406
406
|
|
|
407
|
-
elif fs_path.endswith(".tar.gz"):
|
|
407
|
+
elif fs_path.endswith(".tar") or fs_path.endswith(".tar.gz"):
|
|
408
408
|
with tarfile.open(fs_path, "r") as zfile:
|
|
409
409
|
progress_callback.reset(total=1)
|
|
410
410
|
zfile.extractall(path=extraction_dir)
|
|
411
411
|
progress_callback(1)
|
|
412
|
-
|
|
412
|
+
# in some cases, only a lone file is extracted without being in a directory
|
|
413
|
+
# then, we create a directory in which we place this file
|
|
414
|
+
product_extraction_path = self._resolve_archive_depth(extraction_dir)
|
|
415
|
+
if os.path.isfile(product_extraction_path) and not os.path.isdir(
|
|
416
|
+
product_dir
|
|
417
|
+
):
|
|
418
|
+
os.makedirs(product_dir)
|
|
419
|
+
shutil.move(product_extraction_path, product_dir)
|
|
413
420
|
else:
|
|
414
421
|
progress_callback(1, total=1)
|
|
415
422
|
|
|
@@ -429,19 +436,17 @@ class Download(PluginTopic):
|
|
|
429
436
|
if close_progress_callback:
|
|
430
437
|
progress_callback.close()
|
|
431
438
|
|
|
432
|
-
product_path = self._resolve_archive_depth(product_path)
|
|
433
|
-
|
|
434
439
|
return product_path
|
|
435
440
|
|
|
436
441
|
def download_all(
|
|
437
442
|
self,
|
|
438
443
|
products: SearchResult,
|
|
439
|
-
auth: Optional[
|
|
444
|
+
auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
|
|
440
445
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
441
446
|
progress_callback: Optional[ProgressCallback] = None,
|
|
442
447
|
wait: int = DEFAULT_DOWNLOAD_WAIT,
|
|
443
448
|
timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
444
|
-
**kwargs:
|
|
449
|
+
**kwargs: Unpack[DownloadConf],
|
|
445
450
|
) -> List[str]:
|
|
446
451
|
"""
|
|
447
452
|
Base download_all method.
|
|
@@ -450,32 +455,23 @@ class Download(PluginTopic):
|
|
|
450
455
|
implemented by the plugin to **sequentially** attempt to download products.
|
|
451
456
|
|
|
452
457
|
:param products: Products to download
|
|
453
|
-
:
|
|
454
|
-
:param auth: (optional) The configuration of a plugin of type Authentication
|
|
455
|
-
:type auth: :class:`~eodag.config.PluginConfig`
|
|
458
|
+
:param auth: (optional) authenticated object
|
|
456
459
|
:param downloaded_callback: (optional) A method or a callable object which takes
|
|
457
460
|
as parameter the ``product``. You can use the base class
|
|
458
461
|
:class:`~eodag.api.product.DownloadedCallback` and override
|
|
459
462
|
its ``__call__`` method. Will be called each time a product
|
|
460
463
|
finishes downloading
|
|
461
|
-
:type downloaded_callback: Callable[[:class:`~eodag.api.product._product.EOProduct`], None]
|
|
462
|
-
or None
|
|
463
464
|
:param progress_callback: (optional) A progress callback
|
|
464
|
-
:type progress_callback: :class:`~eodag.utils.ProgressCallback`
|
|
465
465
|
:param wait: (optional) If download fails, wait time in minutes between two download tries
|
|
466
|
-
:type wait: int
|
|
467
466
|
:param timeout: (optional) If download fails, maximum time in minutes before stop retrying
|
|
468
467
|
to download
|
|
469
|
-
:
|
|
470
|
-
:param kwargs: `outputs_prefix` (str), `extract` (bool), `delete_archive` (bool)
|
|
468
|
+
:param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
|
|
471
469
|
and `dl_url_params` (dict) can be provided as additional kwargs
|
|
472
470
|
and will override any other values defined in a configuration
|
|
473
471
|
file or with environment variables.
|
|
474
|
-
:type kwargs: Union[str, bool, dict]
|
|
475
472
|
:returns: List of absolute paths to the downloaded products in the local
|
|
476
473
|
filesystem (e.g. ``['/tmp/product.zip']`` on Linux or
|
|
477
474
|
``['C:\\Users\\username\\AppData\\Local\\Temp\\product.zip']`` on Windows)
|
|
478
|
-
:rtype: list
|
|
479
475
|
"""
|
|
480
476
|
# Products are going to be removed one by one from this sequence once
|
|
481
477
|
# downloaded.
|
|
@@ -599,18 +595,14 @@ class Download(PluginTopic):
|
|
|
599
595
|
exception is thrown until `timeout` minutes.
|
|
600
596
|
|
|
601
597
|
:param product: The EO product to download
|
|
602
|
-
:type product: :class:`~eodag.api.product._product.EOProduct`
|
|
603
598
|
:param wait: If download fails, wait time in minutes between two download tries
|
|
604
|
-
:type wait: int
|
|
605
599
|
:param timeout: If download fails, maximum time in minutes before stop retrying
|
|
606
600
|
to download
|
|
607
|
-
:type timeout: int
|
|
608
601
|
:returns: decorator
|
|
609
|
-
:rtype: Callable[[Callable[..., T]], Callable[..., T]]
|
|
610
602
|
"""
|
|
611
603
|
|
|
612
604
|
def decorator(download: Callable[..., T]) -> Callable[..., T]:
|
|
613
|
-
def download_and_retry(*args: Any, **kwargs:
|
|
605
|
+
def download_and_retry(*args: Any, **kwargs: Unpack[DownloadConf]) -> T:
|
|
614
606
|
# initiate retry loop
|
|
615
607
|
start_time = datetime.now()
|
|
616
608
|
stop_time = start_time + timedelta(minutes=timeout)
|
|
@@ -634,11 +626,10 @@ class Download(PluginTopic):
|
|
|
634
626
|
f"Product is not available for download and order is not supported for"
|
|
635
627
|
f" {self.provider}, {e}"
|
|
636
628
|
)
|
|
637
|
-
not_available_info = e
|
|
638
|
-
pass
|
|
629
|
+
not_available_info = str(e)
|
|
639
630
|
|
|
640
631
|
if datetime_now >= product.next_try and datetime_now < stop_time:
|
|
641
|
-
wait_seconds = (
|
|
632
|
+
wait_seconds: Union[float, int] = (
|
|
642
633
|
datetime_now - product.next_try + timedelta(minutes=wait)
|
|
643
634
|
).seconds
|
|
644
635
|
retry_count += 1
|
|
@@ -646,7 +637,7 @@ class Download(PluginTopic):
|
|
|
646
637
|
f"[Retry #{retry_count}] Waited {wait_seconds}s, trying again to download ordered product"
|
|
647
638
|
f" (retry every {wait}' for {timeout}')"
|
|
648
639
|
)
|
|
649
|
-
logger.
|
|
640
|
+
logger.info(not_available_info)
|
|
650
641
|
# Retry-After info from Response header
|
|
651
642
|
if hasattr(self, "stream"):
|
|
652
643
|
retry_server_info = self.stream.headers.get(
|
|
@@ -656,7 +647,7 @@ class Download(PluginTopic):
|
|
|
656
647
|
logger.debug(
|
|
657
648
|
f"[{self.provider} response] Retry-After: {retry_server_info}"
|
|
658
649
|
)
|
|
659
|
-
logger.
|
|
650
|
+
logger.debug(retry_info)
|
|
660
651
|
nb_info.display_html(retry_info)
|
|
661
652
|
product.next_try = datetime_now
|
|
662
653
|
elif datetime_now < product.next_try and datetime_now < stop_time:
|
|
@@ -668,7 +659,7 @@ class Download(PluginTopic):
|
|
|
668
659
|
f"[Retry #{retry_count}] Waiting {wait_seconds}s until next download try"
|
|
669
660
|
f" for ordered product (retry every {wait}' for {timeout}')"
|
|
670
661
|
)
|
|
671
|
-
logger.
|
|
662
|
+
logger.info(not_available_info)
|
|
672
663
|
# Retry-After info from Response header
|
|
673
664
|
if hasattr(self, "stream"):
|
|
674
665
|
retry_server_info = self.stream.headers.get(
|
|
@@ -678,7 +669,7 @@ class Download(PluginTopic):
|
|
|
678
669
|
logger.debug(
|
|
679
670
|
f"[{self.provider} response] Retry-After: {retry_server_info}"
|
|
680
671
|
)
|
|
681
|
-
logger.
|
|
672
|
+
logger.debug(retry_info)
|
|
682
673
|
nb_info.display_html(retry_info)
|
|
683
674
|
sleep(wait_seconds)
|
|
684
675
|
elif datetime_now >= stop_time and timeout > 0:
|
|
@@ -50,7 +50,7 @@ class CreodiasS3Download(AwsDownload):
|
|
|
50
50
|
|
|
51
51
|
s3_session = boto3.session.Session(**auth_dict)
|
|
52
52
|
s3_resource = s3_session.resource(
|
|
53
|
-
"s3", endpoint_url=getattr(self.config, "
|
|
53
|
+
"s3", endpoint_url=getattr(self.config, "s3_endpoint", None)
|
|
54
54
|
)
|
|
55
55
|
objects = s3_resource.Bucket(bucket_name).objects.filter()
|
|
56
56
|
list(objects.filter(Prefix=prefix).limit(1))
|