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.
Files changed (93) hide show
  1. eodag/__init__.py +6 -8
  2. eodag/api/core.py +654 -538
  3. eodag/api/product/__init__.py +12 -2
  4. eodag/api/product/_assets.py +59 -16
  5. eodag/api/product/_product.py +100 -93
  6. eodag/api/product/drivers/__init__.py +7 -2
  7. eodag/api/product/drivers/base.py +0 -3
  8. eodag/api/product/metadata_mapping.py +192 -96
  9. eodag/api/search_result.py +69 -10
  10. eodag/cli.py +55 -25
  11. eodag/config.py +391 -116
  12. eodag/plugins/apis/base.py +11 -165
  13. eodag/plugins/apis/ecmwf.py +36 -25
  14. eodag/plugins/apis/usgs.py +80 -35
  15. eodag/plugins/authentication/aws_auth.py +13 -4
  16. eodag/plugins/authentication/base.py +10 -1
  17. eodag/plugins/authentication/generic.py +2 -2
  18. eodag/plugins/authentication/header.py +31 -6
  19. eodag/plugins/authentication/keycloak.py +17 -84
  20. eodag/plugins/authentication/oauth.py +3 -3
  21. eodag/plugins/authentication/openid_connect.py +268 -49
  22. eodag/plugins/authentication/qsauth.py +4 -1
  23. eodag/plugins/authentication/sas_auth.py +9 -2
  24. eodag/plugins/authentication/token.py +98 -47
  25. eodag/plugins/authentication/token_exchange.py +122 -0
  26. eodag/plugins/crunch/base.py +3 -1
  27. eodag/plugins/crunch/filter_date.py +3 -9
  28. eodag/plugins/crunch/filter_latest_intersect.py +0 -3
  29. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -4
  30. eodag/plugins/crunch/filter_overlap.py +4 -8
  31. eodag/plugins/crunch/filter_property.py +5 -11
  32. eodag/plugins/download/aws.py +149 -185
  33. eodag/plugins/download/base.py +88 -97
  34. eodag/plugins/download/creodias_s3.py +1 -1
  35. eodag/plugins/download/http.py +638 -310
  36. eodag/plugins/download/s3rest.py +47 -45
  37. eodag/plugins/manager.py +228 -88
  38. eodag/plugins/search/__init__.py +36 -0
  39. eodag/plugins/search/base.py +239 -30
  40. eodag/plugins/search/build_search_result.py +382 -37
  41. eodag/plugins/search/cop_marine.py +441 -0
  42. eodag/plugins/search/creodias_s3.py +25 -20
  43. eodag/plugins/search/csw.py +5 -7
  44. eodag/plugins/search/data_request_search.py +61 -30
  45. eodag/plugins/search/qssearch.py +713 -255
  46. eodag/plugins/search/static_stac_search.py +106 -40
  47. eodag/resources/ext_product_types.json +1 -1
  48. eodag/resources/product_types.yml +1921 -34
  49. eodag/resources/providers.yml +4091 -3655
  50. eodag/resources/stac.yml +50 -216
  51. eodag/resources/stac_api.yml +71 -25
  52. eodag/resources/stac_provider.yml +5 -0
  53. eodag/resources/user_conf_template.yml +89 -32
  54. eodag/rest/__init__.py +6 -0
  55. eodag/rest/cache.py +70 -0
  56. eodag/rest/config.py +68 -0
  57. eodag/rest/constants.py +26 -0
  58. eodag/rest/core.py +735 -0
  59. eodag/rest/errors.py +178 -0
  60. eodag/rest/server.py +264 -431
  61. eodag/rest/stac.py +442 -836
  62. eodag/rest/types/collections_search.py +44 -0
  63. eodag/rest/types/eodag_search.py +238 -47
  64. eodag/rest/types/queryables.py +164 -0
  65. eodag/rest/types/stac_search.py +273 -0
  66. eodag/rest/utils/__init__.py +216 -0
  67. eodag/rest/utils/cql_evaluate.py +119 -0
  68. eodag/rest/utils/rfc3339.py +64 -0
  69. eodag/types/__init__.py +106 -10
  70. eodag/types/bbox.py +15 -14
  71. eodag/types/download_args.py +40 -0
  72. eodag/types/search_args.py +57 -7
  73. eodag/types/whoosh.py +79 -0
  74. eodag/utils/__init__.py +110 -91
  75. eodag/utils/constraints.py +37 -45
  76. eodag/utils/exceptions.py +39 -22
  77. eodag/utils/import_system.py +0 -4
  78. eodag/utils/logging.py +37 -80
  79. eodag/utils/notebook.py +4 -4
  80. eodag/utils/repr.py +113 -0
  81. eodag/utils/requests.py +128 -0
  82. eodag/utils/rest.py +100 -0
  83. eodag/utils/stac_reader.py +93 -21
  84. {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/METADATA +88 -53
  85. eodag-3.0.0.dist-info/RECORD +109 -0
  86. {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/WHEEL +1 -1
  87. {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/entry_points.txt +7 -5
  88. eodag/plugins/apis/cds.py +0 -540
  89. eodag/rest/types/stac_queryables.py +0 -134
  90. eodag/rest/utils.py +0 -1133
  91. eodag-2.12.0.dist-info/RECORD +0 -94
  92. {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/LICENSE +0 -0
  93. {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/top_level.txt +0 -0
@@ -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.utils import DownloadedCallback
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 ``outputs_prefix`` folder defined in the plugin's
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 ``outputs_prefix``) whose name must be
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 ``outputs_prefix/.downloaded`` whose name
89
- is built on the MD5 hash of the product's ``remote_location`` attribute
90
- (``hashlib.md5(remote_location.encode("utf-8")).hexdigest()``) and whose content is
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[PluginConfig] = None,
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: Union[str, bool, Dict[str, Any]],
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
- :type product: :class:`~eodag.api.product._product.EOProduct`
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
- :type timeout: int
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[PluginConfig] = None,
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: Union[str, bool, Dict[str, Any]],
153
- ) -> Dict[str, Any]:
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
- :type product: :class:`~eodag.api.product._product.EOProduct`
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
- :type timeout: int
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
- :type kwargs: Union[str, bool, dict]
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: Union[str, bool, Dict[str, Any]],
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
- outputs_prefix = (
216
- kwargs.pop("outputs_prefix", None)
217
- or getattr(self.config, "outputs_prefix", tempfile.gettempdir())
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
- outputs_extension = kwargs.get("outputs_extension", None) or getattr(
221
- self.config, "outputs_extension", ".zip"
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(outputs_prefix)
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}{outputs_extension}",
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
- url_hash = hashlib.md5(url.encode("utf-8")).hexdigest()
250
- record_filename = os.path.join(download_records_dir, url_hash)
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: Any,
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
- outputs_extension = kwargs.pop("outputs_extension", ".zip")
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(outputs_extension)]
350
- if outputs_extension in fs_path
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
- outputs_prefix = (
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
- outputs_dir = os.path.join(outputs_prefix, product_path)
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(outputs_dir))
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
- shutil.move(extraction_dir, outputs_dir)
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
- shutil.move(extraction_dir, outputs_dir)
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[PluginConfig] = None,
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: Union[str, bool, Dict[str, Any]],
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
- :type products: :class:`~eodag.api.search_result.SearchResult`
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
- :type timeout: int
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: Any) -> T:
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.debug(not_available_info)
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.info(retry_info)
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.debug(not_available_info)
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.info(retry_info)
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, "base_uri", None)
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))