eodag 3.0.0b3__py3-none-any.whl → 3.1.0b1__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 (77) hide show
  1. eodag/api/core.py +292 -198
  2. eodag/api/product/_assets.py +6 -6
  3. eodag/api/product/_product.py +18 -18
  4. eodag/api/product/metadata_mapping.py +51 -14
  5. eodag/api/search_result.py +29 -3
  6. eodag/cli.py +57 -20
  7. eodag/config.py +413 -117
  8. eodag/plugins/apis/base.py +10 -4
  9. eodag/plugins/apis/ecmwf.py +49 -16
  10. eodag/plugins/apis/usgs.py +30 -7
  11. eodag/plugins/authentication/aws_auth.py +14 -5
  12. eodag/plugins/authentication/base.py +10 -1
  13. eodag/plugins/authentication/generic.py +14 -3
  14. eodag/plugins/authentication/header.py +12 -4
  15. eodag/plugins/authentication/keycloak.py +41 -22
  16. eodag/plugins/authentication/oauth.py +11 -1
  17. eodag/plugins/authentication/openid_connect.py +178 -163
  18. eodag/plugins/authentication/qsauth.py +12 -4
  19. eodag/plugins/authentication/sas_auth.py +19 -2
  20. eodag/plugins/authentication/token.py +93 -15
  21. eodag/plugins/authentication/token_exchange.py +19 -19
  22. eodag/plugins/crunch/base.py +4 -1
  23. eodag/plugins/crunch/filter_date.py +5 -2
  24. eodag/plugins/crunch/filter_latest_intersect.py +5 -4
  25. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  26. eodag/plugins/crunch/filter_overlap.py +5 -7
  27. eodag/plugins/crunch/filter_property.py +6 -6
  28. eodag/plugins/download/aws.py +50 -34
  29. eodag/plugins/download/base.py +41 -50
  30. eodag/plugins/download/creodias_s3.py +40 -2
  31. eodag/plugins/download/http.py +221 -195
  32. eodag/plugins/download/s3rest.py +25 -25
  33. eodag/plugins/manager.py +168 -23
  34. eodag/plugins/search/base.py +106 -39
  35. eodag/plugins/search/build_search_result.py +1065 -324
  36. eodag/plugins/search/cop_marine.py +112 -29
  37. eodag/plugins/search/creodias_s3.py +45 -24
  38. eodag/plugins/search/csw.py +41 -1
  39. eodag/plugins/search/data_request_search.py +109 -9
  40. eodag/plugins/search/qssearch.py +549 -257
  41. eodag/plugins/search/static_stac_search.py +20 -21
  42. eodag/resources/ext_product_types.json +1 -1
  43. eodag/resources/product_types.yml +577 -87
  44. eodag/resources/providers.yml +1619 -2776
  45. eodag/resources/stac.yml +3 -163
  46. eodag/resources/user_conf_template.yml +112 -97
  47. eodag/rest/config.py +1 -2
  48. eodag/rest/constants.py +0 -1
  49. eodag/rest/core.py +138 -98
  50. eodag/rest/errors.py +181 -0
  51. eodag/rest/server.py +55 -329
  52. eodag/rest/stac.py +93 -544
  53. eodag/rest/types/eodag_search.py +19 -8
  54. eodag/rest/types/queryables.py +6 -8
  55. eodag/rest/types/stac_search.py +11 -2
  56. eodag/rest/utils/__init__.py +3 -0
  57. eodag/types/__init__.py +71 -18
  58. eodag/types/download_args.py +3 -3
  59. eodag/types/queryables.py +180 -73
  60. eodag/types/search_args.py +3 -3
  61. eodag/types/whoosh.py +126 -0
  62. eodag/utils/__init__.py +147 -66
  63. eodag/utils/exceptions.py +47 -26
  64. eodag/utils/logging.py +37 -77
  65. eodag/utils/repr.py +65 -6
  66. eodag/utils/requests.py +11 -13
  67. eodag/utils/stac_reader.py +1 -1
  68. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/METADATA +80 -81
  69. eodag-3.1.0b1.dist-info/RECORD +108 -0
  70. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/WHEEL +1 -1
  71. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/entry_points.txt +4 -2
  72. eodag/resources/constraints/climate-dt.json +0 -13
  73. eodag/resources/constraints/extremes-dt.json +0 -8
  74. eodag/utils/constraints.py +0 -244
  75. eodag-3.0.0b3.dist-info/RECORD +0 -110
  76. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/LICENSE +0 -0
  77. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/top_level.txt +0 -0
@@ -81,15 +81,15 @@ class Download(PluginTopic):
81
81
 
82
82
  - download data in the ``output_dir`` folder defined in the plugin's
83
83
  configuration or passed through kwargs
84
- - extract products from their archive (if relevant) if ``extract`` is set to True
85
- (True by default)
84
+ - extract products from their archive (if relevant) if ``extract`` is set to ``True``
85
+ (``True`` by default)
86
86
  - save a product in an archive/directory (in ``output_dir``) whose name must be
87
87
  the product's ``title`` property
88
88
  - update the product's ``location`` attribute once its data is downloaded (and
89
89
  eventually after it's extracted) to the product's location given as a file URI
90
- (e.g. 'file:///tmp/product_folder' on Linux or
91
- 'file:///C:/Users/username/AppData/LOcal/Temp' on Windows)
92
- - save a *record* file in the directory ``output_dir/.downloaded`` whose name
90
+ (e.g. ``file:///tmp/product_folder`` on Linux or
91
+ ``file:///C:/Users/username/AppData/Local/Temp`` on Windows)
92
+ - save a *record* file in the directory ``{output_dir}/.downloaded`` whose name
93
93
  is built on the MD5 hash of the product's ``product_type`` and ``properties['id']``
94
94
  attributes (``hashlib.md5((product.product_type+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
95
95
  and whose content is the product's ``remote_location`` attribute itself.
@@ -112,8 +112,8 @@ class Download(PluginTopic):
112
112
  product: EOProduct,
113
113
  auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
114
114
  progress_callback: Optional[ProgressCallback] = None,
115
- wait: int = DEFAULT_DOWNLOAD_WAIT,
116
- timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
115
+ wait: float = DEFAULT_DOWNLOAD_WAIT,
116
+ timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
117
117
  **kwargs: Unpack[DownloadConf],
118
118
  ) -> Optional[str]:
119
119
  r"""
@@ -130,8 +130,8 @@ class Download(PluginTopic):
130
130
  and will override any other values defined in a configuration
131
131
  file or with environment variables.
132
132
  :returns: The absolute path to the downloaded product in the local filesystem
133
- (e.g. '/tmp/product.zip' on Linux or
134
- 'C:\\Users\\username\\AppData\\Local\\Temp\\product.zip' on Windows)
133
+ (e.g. ``/tmp/product.zip`` on Linux or
134
+ ``C:\\Users\\username\\AppData\\Local\\Temp\\product.zip`` on Windows)
135
135
  """
136
136
  raise NotImplementedError(
137
137
  "A Download plugin must implement a method named download"
@@ -142,8 +142,8 @@ class Download(PluginTopic):
142
142
  product: EOProduct,
143
143
  auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
144
144
  progress_callback: Optional[ProgressCallback] = None,
145
- wait: int = DEFAULT_DOWNLOAD_WAIT,
146
- timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
145
+ wait: float = DEFAULT_DOWNLOAD_WAIT,
146
+ timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
147
147
  **kwargs: Unpack[DownloadConf],
148
148
  ) -> StreamResponse:
149
149
  r"""
@@ -155,11 +155,11 @@ class Download(PluginTopic):
155
155
  :param wait: (optional) If download fails, wait time in minutes between two download tries
156
156
  :param timeout: (optional) If download fails, maximum time in minutes before stop retrying
157
157
  to download
158
- :param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
159
- and `dl_url_params` (dict) can be provided as additional kwargs
158
+ :param kwargs: ``output_dir`` (str), ``extract`` (bool), ``delete_archive`` (bool)
159
+ and ``dl_url_params`` (dict) can be provided as additional kwargs
160
160
  and will override any other values defined in a configuration
161
161
  file or with environment variables.
162
- :returns: Dictionnary of :class:`~fastapi.responses.StreamingResponse` keyword-arguments
162
+ :returns: Dictionary of :class:`~fastapi.responses.StreamingResponse` keyword-arguments
163
163
  """
164
164
  raise NotImplementedError(
165
165
  "Download streaming must be implemented using a method named _stream_download_dict"
@@ -202,8 +202,8 @@ class Download(PluginTopic):
202
202
  or getattr(self.config, "output_dir", tempfile.gettempdir())
203
203
  or tempfile.gettempdir()
204
204
  )
205
- output_extension = kwargs.get("output_extension", None) or getattr(
206
- self.config, "output_extension", ".zip"
205
+ output_extension = kwargs.get("output_extension") or getattr(
206
+ self.config, "output_extension", ""
207
207
  )
208
208
 
209
209
  # Strong asumption made here: all products downloaded will be zip files
@@ -233,9 +233,13 @@ class Download(PluginTopic):
233
233
  logger.warning(
234
234
  f"Unable to create records directory. Got:\n{tb.format_exc()}",
235
235
  )
236
+ url_hash = hashlib.md5(url.encode("utf-8")).hexdigest()
237
+ old_record_filename = os.path.join(download_records_dir, url_hash)
236
238
  record_filename = os.path.join(
237
239
  download_records_dir, self.generate_record_hash(product)
238
240
  )
241
+ if os.path.isfile(old_record_filename):
242
+ os.rename(old_record_filename, record_filename)
239
243
  if os.path.isfile(record_filename) and os.path.isfile(fs_path):
240
244
  logger.info(
241
245
  f"Product already downloaded: {fs_path}",
@@ -339,13 +343,7 @@ class Download(PluginTopic):
339
343
  if delete_archive is not None
340
344
  else getattr(self.config, "delete_archive", True)
341
345
  )
342
- output_extension = kwargs.pop("output_extension", ".zip")
343
-
344
- product_path = (
345
- fs_path[: fs_path.index(output_extension)]
346
- if output_extension in fs_path
347
- else fs_path
348
- )
346
+ product_path, _ = os.path.splitext(fs_path)
349
347
  product_path_exists = os.path.exists(product_path)
350
348
  if product_path_exists and os.path.isfile(product_path):
351
349
  logger.info(
@@ -422,10 +420,10 @@ class Download(PluginTopic):
422
420
 
423
421
  tmp_dir.cleanup()
424
422
 
425
- if delete_archive:
423
+ if delete_archive and os.path.isfile(fs_path):
426
424
  logger.info(f"Deleting archive {os.path.basename(fs_path)}")
427
425
  os.unlink(fs_path)
428
- else:
426
+ elif os.path.isfile(fs_path):
429
427
  logger.info(
430
428
  f"Archive deletion is deactivated, keeping {os.path.basename(fs_path)}"
431
429
  )
@@ -444,8 +442,8 @@ class Download(PluginTopic):
444
442
  auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
445
443
  downloaded_callback: Optional[DownloadedCallback] = None,
446
444
  progress_callback: Optional[ProgressCallback] = None,
447
- wait: int = DEFAULT_DOWNLOAD_WAIT,
448
- timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
445
+ wait: float = DEFAULT_DOWNLOAD_WAIT,
446
+ timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
449
447
  **kwargs: Unpack[DownloadConf],
450
448
  ) -> List[str]:
451
449
  """
@@ -458,7 +456,7 @@ class Download(PluginTopic):
458
456
  :param auth: (optional) authenticated object
459
457
  :param downloaded_callback: (optional) A method or a callable object which takes
460
458
  as parameter the ``product``. You can use the base class
461
- :class:`~eodag.api.product.DownloadedCallback` and override
459
+ :class:`~eodag.utils.DownloadedCallback` and override
462
460
  its ``__call__`` method. Will be called each time a product
463
461
  finishes downloading
464
462
  :param progress_callback: (optional) A progress callback
@@ -541,7 +539,7 @@ class Download(PluginTopic):
541
539
  )
542
540
  raise
543
541
 
544
- except RuntimeError:
542
+ except (RuntimeError, Exception):
545
543
  import traceback as tb
546
544
 
547
545
  logger.error(
@@ -549,16 +547,9 @@ class Download(PluginTopic):
549
547
  "Skipping it"
550
548
  )
551
549
  logger.debug(f"\n{tb.format_exc()}")
552
- stop_time = datetime.now()
553
-
554
- except Exception:
555
- import traceback as tb
556
550
 
557
- logger.warning(
558
- f"A problem occurred during download of product: {product}. "
559
- "Skipping it",
560
- )
561
- logger.debug(f"\n{tb.format_exc()}")
551
+ # product skipped, to not retry it
552
+ products.remove(product)
562
553
 
563
554
  if (
564
555
  len(products) > 0
@@ -585,14 +576,14 @@ class Download(PluginTopic):
585
576
 
586
577
  return paths
587
578
 
588
- def _download_retry(
589
- self, product: EOProduct, wait: int, timeout: int
579
+ def _order_download_retry(
580
+ self, product: EOProduct, wait: float, timeout: float
590
581
  ) -> Callable[[Callable[..., T]], Callable[..., T]]:
591
582
  """
592
- Download retry decorator.
583
+ Order download retry decorator.
593
584
 
594
- Retries the wrapped download method after `wait` minutes if a NotAvailableError
595
- exception is thrown until `timeout` minutes.
585
+ Retries the wrapped order_download method after ``wait`` minutes if a
586
+ ``NotAvailableError`` exception is thrown until ``timeout`` minutes.
596
587
 
597
588
  :param product: The EO product to download
598
589
  :param wait: If download fails, wait time in minutes between two download tries
@@ -601,7 +592,7 @@ class Download(PluginTopic):
601
592
  :returns: decorator
602
593
  """
603
594
 
604
- def decorator(download: Callable[..., T]) -> Callable[..., T]:
595
+ def decorator(order_download: Callable[..., T]) -> Callable[..., T]:
605
596
  def download_and_retry(*args: Any, **kwargs: Unpack[DownloadConf]) -> T:
606
597
  # initiate retry loop
607
598
  start_time = datetime.now()
@@ -618,7 +609,7 @@ class Download(PluginTopic):
618
609
  if datetime_now >= product.next_try:
619
610
  product.next_try += timedelta(minutes=wait)
620
611
  try:
621
- return download(*args, **kwargs)
612
+ return order_download(*args, **kwargs)
622
613
 
623
614
  except NotAvailableError as e:
624
615
  if not getattr(self.config, "order_enabled", False):
@@ -634,7 +625,7 @@ class Download(PluginTopic):
634
625
  ).seconds
635
626
  retry_count += 1
636
627
  retry_info = (
637
- f"[Retry #{retry_count}] Waited {wait_seconds}s, trying again to download ordered product"
628
+ f"[Retry #{retry_count}] Waited {wait_seconds}s, checking order status again"
638
629
  f" (retry every {wait}' for {timeout}')"
639
630
  )
640
631
  logger.info(not_available_info)
@@ -656,8 +647,8 @@ class Download(PluginTopic):
656
647
  ).microseconds / 1e6
657
648
  retry_count += 1
658
649
  retry_info = (
659
- f"[Retry #{retry_count}] Waiting {wait_seconds}s until next download try"
660
- f" for ordered product (retry every {wait}' for {timeout}')"
650
+ f"[Retry #{retry_count}] Waiting {wait_seconds}s until next order status check"
651
+ f" (retry every {wait}' for {timeout}')"
661
652
  )
662
653
  logger.info(not_available_info)
663
654
  # Retry-After info from Response header
@@ -678,12 +669,12 @@ class Download(PluginTopic):
678
669
  logger.info(not_available_info)
679
670
  raise NotAvailableError(
680
671
  f"{product.properties['title']} is not available ({product.properties['storageStatus']})"
681
- f" and could not be downloaded, timeout reached"
672
+ f" and order was not successfull, timeout reached"
682
673
  )
683
674
  elif datetime_now >= stop_time:
684
675
  raise NotAvailableError(not_available_info)
685
676
 
686
- return download(*args, **kwargs)
677
+ return order_download(*args, **kwargs)
687
678
 
688
679
  return download_and_retry
689
680
 
@@ -15,17 +15,28 @@
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, Optional, Tuple
18
19
 
19
20
  import boto3
20
21
  from botocore.exceptions import ClientError
21
22
 
23
+ from eodag import EOProduct
22
24
  from eodag.plugins.download.aws import AwsDownload
23
25
  from eodag.utils.exceptions import MisconfiguredError
24
26
 
25
27
 
26
28
  class CreodiasS3Download(AwsDownload):
27
29
  """
28
- Download on creodias s3 from their VMs
30
+ Download on creodias s3 from their VMs (extension of :class:`~eodag.plugins.download.aws.AwsDownload`)
31
+
32
+ :param provider: provider name
33
+ :param config: Download plugin configuration:
34
+
35
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): CreodiasS3Download
36
+ * :attr:`~eodag.config.PluginConfig.base_uri` (``str``) (**mandatory**): s3 endpoint url
37
+ * :attr:`~eodag.config.PluginConfig.s3_bucket` (``str``) (**mandatory**): bucket where the products can be found
38
+ * :attr:`~eodag.config.PluginConfig.ssl_verify` (``bool``): if the ssl certificates should be
39
+ verified in requests; default: ``True``
29
40
  """
30
41
 
31
42
  def _get_authenticated_objects_unsigned(self, bucket_name, prefix, auth_dict):
@@ -50,9 +61,36 @@ class CreodiasS3Download(AwsDownload):
50
61
 
51
62
  s3_session = boto3.session.Session(**auth_dict)
52
63
  s3_resource = s3_session.resource(
53
- "s3", endpoint_url=getattr(self.config, "base_uri", None)
64
+ "s3", endpoint_url=getattr(self.config, "s3_endpoint", None)
54
65
  )
55
66
  objects = s3_resource.Bucket(bucket_name).objects.filter()
56
67
  list(objects.filter(Prefix=prefix).limit(1))
57
68
  self.s3_session = s3_session
58
69
  return objects
70
+
71
+ def _get_bucket_names_and_prefixes(
72
+ self,
73
+ product: EOProduct,
74
+ asset_filter: Optional[str] = None,
75
+ ignore_assets: Optional[bool] = False,
76
+ ) -> List[Tuple[str, Optional[str]]]:
77
+ """
78
+ Retrieves the bucket names and path prefixes for the assets
79
+
80
+ :param product: product for which the assets shall be downloaded
81
+ :param asset_filter: text for which the assets should be filtered
82
+ :param ignore_assets: if product instead of individual assets should be used
83
+ :return: tuples of bucket names and prefixes
84
+ """
85
+ # if assets are defined, use them instead of scanning product.location
86
+ if len(product.assets) > 0 and not ignore_assets:
87
+ bucket_names_and_prefixes = super()._get_bucket_names_and_prefixes(
88
+ product, asset_filter, ignore_assets
89
+ )
90
+ else:
91
+ # if no assets are given, use productIdentifier to get S3 path for download
92
+ s3_url = "s3:/" + product.properties["productIdentifier"]
93
+ bucket_names_and_prefixes = [
94
+ self.get_product_bucket_name_and_prefix(product, s3_url)
95
+ ]
96
+ return bucket_names_and_prefixes