eodag 3.1.0b1__py3-none-any.whl → 3.2.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 (85) hide show
  1. eodag/api/core.py +69 -63
  2. eodag/api/product/_assets.py +49 -13
  3. eodag/api/product/_product.py +41 -30
  4. eodag/api/product/drivers/__init__.py +81 -4
  5. eodag/api/product/drivers/base.py +65 -4
  6. eodag/api/product/drivers/generic.py +65 -0
  7. eodag/api/product/drivers/sentinel1.py +97 -0
  8. eodag/api/product/drivers/sentinel2.py +95 -0
  9. eodag/api/product/metadata_mapping.py +85 -79
  10. eodag/api/search_result.py +13 -23
  11. eodag/cli.py +4 -4
  12. eodag/config.py +77 -80
  13. eodag/plugins/apis/base.py +1 -1
  14. eodag/plugins/apis/ecmwf.py +12 -15
  15. eodag/plugins/apis/usgs.py +12 -11
  16. eodag/plugins/authentication/aws_auth.py +16 -13
  17. eodag/plugins/authentication/base.py +5 -3
  18. eodag/plugins/authentication/header.py +3 -3
  19. eodag/plugins/authentication/keycloak.py +4 -4
  20. eodag/plugins/authentication/oauth.py +7 -3
  21. eodag/plugins/authentication/openid_connect.py +20 -14
  22. eodag/plugins/authentication/sas_auth.py +4 -4
  23. eodag/plugins/authentication/token.py +7 -7
  24. eodag/plugins/authentication/token_exchange.py +1 -1
  25. eodag/plugins/base.py +4 -4
  26. eodag/plugins/crunch/base.py +4 -4
  27. eodag/plugins/crunch/filter_date.py +4 -4
  28. eodag/plugins/crunch/filter_latest_intersect.py +6 -6
  29. eodag/plugins/crunch/filter_latest_tpl_name.py +7 -7
  30. eodag/plugins/crunch/filter_overlap.py +4 -4
  31. eodag/plugins/crunch/filter_property.py +4 -4
  32. eodag/plugins/download/aws.py +137 -77
  33. eodag/plugins/download/base.py +8 -17
  34. eodag/plugins/download/creodias_s3.py +2 -2
  35. eodag/plugins/download/http.py +30 -32
  36. eodag/plugins/download/s3rest.py +5 -4
  37. eodag/plugins/manager.py +10 -20
  38. eodag/plugins/search/__init__.py +6 -5
  39. eodag/plugins/search/base.py +38 -42
  40. eodag/plugins/search/build_search_result.py +286 -336
  41. eodag/plugins/search/cop_marine.py +22 -12
  42. eodag/plugins/search/creodias_s3.py +8 -78
  43. eodag/plugins/search/csw.py +11 -11
  44. eodag/plugins/search/data_request_search.py +19 -18
  45. eodag/plugins/search/qssearch.py +84 -151
  46. eodag/plugins/search/stac_list_assets.py +85 -0
  47. eodag/plugins/search/static_stac_search.py +4 -4
  48. eodag/resources/ext_product_types.json +1 -1
  49. eodag/resources/product_types.yml +848 -398
  50. eodag/resources/providers.yml +1038 -1115
  51. eodag/resources/stac_api.yml +2 -2
  52. eodag/resources/user_conf_template.yml +10 -9
  53. eodag/rest/cache.py +2 -2
  54. eodag/rest/config.py +3 -3
  55. eodag/rest/core.py +24 -24
  56. eodag/rest/errors.py +5 -5
  57. eodag/rest/server.py +3 -11
  58. eodag/rest/stac.py +41 -38
  59. eodag/rest/types/collections_search.py +3 -3
  60. eodag/rest/types/eodag_search.py +23 -23
  61. eodag/rest/types/queryables.py +40 -28
  62. eodag/rest/types/stac_search.py +15 -25
  63. eodag/rest/utils/__init__.py +11 -21
  64. eodag/rest/utils/cql_evaluate.py +6 -6
  65. eodag/rest/utils/rfc3339.py +2 -2
  66. eodag/types/__init__.py +97 -29
  67. eodag/types/bbox.py +2 -2
  68. eodag/types/download_args.py +2 -2
  69. eodag/types/queryables.py +5 -2
  70. eodag/types/search_args.py +4 -4
  71. eodag/types/whoosh.py +1 -3
  72. eodag/utils/__init__.py +82 -41
  73. eodag/utils/exceptions.py +2 -2
  74. eodag/utils/import_system.py +2 -2
  75. eodag/utils/requests.py +2 -2
  76. eodag/utils/rest.py +2 -2
  77. eodag/utils/s3.py +231 -0
  78. eodag/utils/stac_reader.py +10 -10
  79. {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/METADATA +12 -10
  80. eodag-3.2.0.dist-info/RECORD +113 -0
  81. {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/WHEEL +1 -1
  82. {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/entry_points.txt +1 -0
  83. eodag-3.1.0b1.dist-info/RECORD +0 -108
  84. {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info/licenses}/LICENSE +0 -0
  85. {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/top_level.txt +0 -0
@@ -23,19 +23,7 @@ import logging
23
23
  import re
24
24
  from datetime import datetime, timedelta
25
25
  from string import Formatter
26
- from typing import (
27
- TYPE_CHECKING,
28
- Any,
29
- AnyStr,
30
- Callable,
31
- Dict,
32
- Iterator,
33
- List,
34
- Optional,
35
- Tuple,
36
- Union,
37
- cast,
38
- )
26
+ from typing import TYPE_CHECKING, Any, AnyStr, Callable, Iterator, Optional, Union, cast
39
27
 
40
28
  import geojson
41
29
  import orjson
@@ -61,6 +49,7 @@ from eodag.utils import (
61
49
  get_timestamp,
62
50
  items_recursive_apply,
63
51
  nested_pairs2dict,
52
+ sanitize,
64
53
  string_to_jsonpath,
65
54
  update_nested_dict,
66
55
  )
@@ -88,8 +77,8 @@ DEFAULT_GEOMETRY = "POLYGON((180 -90, 180 90, -180 90, -180 -90, 180 -90))"
88
77
 
89
78
 
90
79
  def get_metadata_path(
91
- map_value: Union[str, List[str]],
92
- ) -> Tuple[Union[List[str], None], str]:
80
+ map_value: Union[str, list[str]],
81
+ ) -> tuple[Union[list[str], None], str]:
93
82
  """Return the jsonpath or xpath to the value of a EO product metadata in a provider
94
83
  search result.
95
84
 
@@ -137,12 +126,12 @@ def get_metadata_path(
137
126
  return None, path
138
127
 
139
128
 
140
- def get_metadata_path_value(map_value: Union[str, List[str]]) -> str:
129
+ def get_metadata_path_value(map_value: Union[str, list[str]]) -> str:
141
130
  """Get raw metadata path without converter"""
142
131
  return map_value[1] if isinstance(map_value, list) else map_value
143
132
 
144
133
 
145
- def get_search_param(map_value: List[str]) -> str:
134
+ def get_search_param(map_value: list[str]) -> str:
146
135
  """See :func:`~eodag.api.product.metadata_mapping.get_metadata_path`
147
136
 
148
137
  :param map_value: The value originating from the definition of `metadata_mapping`
@@ -188,6 +177,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
188
177
  - ``split_corine_id``: get the product type by splitting the product id
189
178
  - ``to_datetime_dict``: convert a datetime string to a dictionary where values are either a string or a list
190
179
  - ``get_ecmwf_time``: get the time of a datetime string in the ECMWF format
180
+ - ``sanitize``: sanitize string
191
181
 
192
182
  :param search_param: The string to be formatted
193
183
  :param args: (optional) Additional arguments to use in the formatting process
@@ -335,7 +325,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
335
325
  return wkt_value
336
326
 
337
327
  @staticmethod
338
- def convert_to_bounds_lists(input_geom: BaseGeometry) -> List[List[float]]:
328
+ def convert_to_bounds_lists(input_geom: BaseGeometry) -> list[list[float]]:
339
329
  if isinstance(input_geom, MultiPolygon):
340
330
  geoms = [geom for geom in input_geom.geoms]
341
331
  # sort with larger one at first (stac-browser only plots first one)
@@ -345,7 +335,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
345
335
  return [list(input_geom.bounds[0:4])]
346
336
 
347
337
  @staticmethod
348
- def convert_to_bounds(input_geom_unformatted: Any) -> List[float]:
338
+ def convert_to_bounds(input_geom_unformatted: Any) -> list[float]:
349
339
  input_geom = get_geometry_from_various(geometry=input_geom_unformatted)
350
340
  if isinstance(input_geom, MultiPolygon):
351
341
  geoms = [geom for geom in input_geom.geoms]
@@ -356,16 +346,16 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
356
346
  max_lon = -180
357
347
  max_lat = -90
358
348
  for geom in geoms:
359
- min_lon = min(min_lon, geom.bound[0])
360
- min_lat = min(min_lat, geom.bound[1])
361
- max_lon = max(max_lon, geom.bound[2])
362
- max_lat = max(max_lat, geom.bound[3])
349
+ min_lon = min(min_lon, geom.bounds[0])
350
+ min_lat = min(min_lat, geom.bounds[1])
351
+ max_lon = max(max_lon, geom.bounds[2])
352
+ max_lat = max(max_lat, geom.bounds[3])
363
353
  return [min_lon, min_lat, max_lon, max_lat]
364
354
  else:
365
355
  return list(input_geom.bounds[0:4])
366
356
 
367
357
  @staticmethod
368
- def convert_to_nwse_bounds(input_geom: BaseGeometry) -> List[float]:
358
+ def convert_to_nwse_bounds(input_geom: BaseGeometry) -> list[float]:
369
359
  if isinstance(input_geom, str):
370
360
  input_geom = shapely.wkt.loads(input_geom)
371
361
  return list(input_geom.bounds[-1:] + input_geom.bounds[:-1])
@@ -379,8 +369,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
379
369
  )
380
370
 
381
371
  @staticmethod
382
- def convert_to_geojson(string: str) -> str:
383
- return geojson.dumps(string)
372
+ def convert_to_geojson(value: Any) -> str:
373
+ return geojson.dumps(value)
384
374
 
385
375
  @staticmethod
386
376
  def convert_from_ewkt(ewkt_string: str) -> Union[BaseGeometry, str]:
@@ -449,7 +439,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
449
439
  else:
450
440
  yield e
451
441
 
452
- polygons_list: List[Polygon] = []
442
+ polygons_list: list[Polygon] = []
453
443
  for elem in flatten_elements(georss[0]):
454
444
  coords_list = elem.text.split()
455
445
  polygon_args = [
@@ -474,7 +464,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
474
464
  @staticmethod
475
465
  def convert_to_longitude_latitude(
476
466
  input_geom_unformatted: Any,
477
- ) -> Dict[str, float]:
467
+ ) -> dict[str, float]:
478
468
  bounds = MetadataFormatter.convert_to_bounds(input_geom_unformatted)
479
469
  lon = (bounds[0] + bounds[2]) / 2
480
470
  lat = (bounds[1] + bounds[3]) / 2
@@ -508,14 +498,21 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
508
498
  return NOT_AVAILABLE
509
499
 
510
500
  @staticmethod
511
- def convert_replace_str(string: str, args: str) -> str:
501
+ def convert_replace_str(value: Any, args: str) -> str:
502
+ if isinstance(value, dict):
503
+ value = MetadataFormatter.convert_to_geojson(value)
504
+ elif not isinstance(value, str):
505
+ raise TypeError(
506
+ f"convert_replace_str expects a string or a dict (apply to_geojson). Got {type(value)}"
507
+ )
508
+
512
509
  old, new = ast.literal_eval(args)
513
- return re.sub(old, new, string)
510
+ return re.sub(old, new, value)
514
511
 
515
512
  @staticmethod
516
513
  def convert_recursive_sub_str(
517
- input_obj: Union[Dict[Any, Any], List[Any]], args: str
518
- ) -> Union[Dict[Any, Any], List[Any]]:
514
+ input_obj: Union[dict[Any, Any], list[Any]], args: str
515
+ ) -> Union[dict[Any, Any], list[Any]]:
519
516
  old, new = ast.literal_eval(args)
520
517
  return items_recursive_apply(
521
518
  input_obj,
@@ -525,8 +522,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
525
522
 
526
523
  @staticmethod
527
524
  def convert_dict_update(
528
- input_dict: Dict[Any, Any], args: str
529
- ) -> Dict[Any, Any]:
525
+ input_dict: dict[Any, Any], args: str
526
+ ) -> dict[Any, Any]:
530
527
  """Converts"""
531
528
  new_items_list = ast.literal_eval(args)
532
529
 
@@ -536,8 +533,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
536
533
 
537
534
  @staticmethod
538
535
  def convert_dict_filter(
539
- input_dict: Dict[Any, Any], jsonpath_filter_str: str
540
- ) -> Dict[Any, Any]:
536
+ input_dict: dict[Any, Any], jsonpath_filter_str: str
537
+ ) -> dict[Any, Any]:
541
538
  """Fitlers dict items using jsonpath"""
542
539
 
543
540
  jsonpath_filter = string_to_jsonpath(jsonpath_filter_str, force=True)
@@ -616,8 +613,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
616
613
  return NOT_AVAILABLE
617
614
 
618
615
  @staticmethod
619
- def convert_split_id_into_s1_params(product_id: str) -> Dict[str, str]:
620
- parts: List[str] = re.split(r"_(?!_)", product_id)
616
+ def convert_split_id_into_s1_params(product_id: str) -> dict[str, str]:
617
+ parts: list[str] = re.split(r"_(?!_)", product_id)
621
618
  if len(parts) < 9:
622
619
  logger.error(
623
620
  "id %s does not match expected Sentinel-1 id format", product_id
@@ -651,8 +648,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
651
648
  return params
652
649
 
653
650
  @staticmethod
654
- def convert_split_id_into_s3_params(product_id: str) -> Dict[str, str]:
655
- parts: List[str] = re.split(r"_(?!_)", product_id)
651
+ def convert_split_id_into_s3_params(product_id: str) -> dict[str, str]:
652
+ parts: list[str] = re.split(r"_(?!_)", product_id)
656
653
  params = {"productType": product_id[4:15]}
657
654
  dates = re.findall("[0-9]{8}T[0-9]{6}", product_id)
658
655
  start_date = datetime.strptime(dates[0], "%Y%m%dT%H%M%S") - timedelta(
@@ -668,8 +665,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
668
665
  return params
669
666
 
670
667
  @staticmethod
671
- def convert_split_id_into_s5p_params(product_id: str) -> Dict[str, str]:
672
- parts: List[str] = re.split(r"_(?!_)", product_id)
668
+ def convert_split_id_into_s5p_params(product_id: str) -> dict[str, str]:
669
+ parts: list[str] = re.split(r"_(?!_)", product_id)
673
670
  params = {
674
671
  "productType": product_id[9:19],
675
672
  "processingMode": parts[1],
@@ -686,7 +683,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
686
683
  return params
687
684
 
688
685
  @staticmethod
689
- def convert_split_cop_dem_id(product_id: str) -> List[int]:
686
+ def convert_split_cop_dem_id(product_id: str) -> list[int]:
690
687
  parts = product_id.split("_")
691
688
  lattitude = parts[3]
692
689
  longitude = parts[5]
@@ -725,7 +722,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
725
722
  @staticmethod
726
723
  def convert_to_datetime_dict(
727
724
  date: str, format: str
728
- ) -> Dict[str, Union[List[str], str]]:
725
+ ) -> dict[str, Union[list[str], str]]:
729
726
  """Convert a date (str) to a dictionary where values are in the format given in argument
730
727
 
731
728
  date == "2021-04-21T18:27:19.123Z" and format == "list" => {
@@ -777,7 +774,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
777
774
  @staticmethod
778
775
  def convert_interval_to_datetime_dict(
779
776
  date: str, separator: str = "/"
780
- ) -> Dict[str, List[str]]:
777
+ ) -> dict[str, list[str]]:
781
778
  """Convert a date interval ('/' separated str) to a dictionary where values are lists
782
779
 
783
780
  date == "2021-04-21/2021-04-22" => {
@@ -817,7 +814,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
817
814
  }
818
815
 
819
816
  @staticmethod
820
- def convert_get_ecmwf_time(date: str) -> List[str]:
817
+ def convert_get_ecmwf_time(date: str) -> list[str]:
821
818
  """Get the time of a date (str) in the ECMWF format (["HH:00"])
822
819
 
823
820
  "2021-04-21T18:27:19.123Z" => ["18:00"]
@@ -828,6 +825,11 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
828
825
  + ":00"
829
826
  ]
830
827
 
828
+ @staticmethod
829
+ def convert_sanitize(text: str) -> str:
830
+ """Sanitize string"""
831
+ return sanitize(text)
832
+
831
833
  @staticmethod
832
834
  def convert_get_dates_from_string(text: str, split_param="-"):
833
835
  reg = "[0-9]{8}" + split_param + "[0-9]{8}"
@@ -861,8 +863,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
861
863
 
862
864
  @staticmethod
863
865
  def convert_assets_list_to_dict(
864
- assets_list: List[Dict[str, str]], asset_name_key: str = "title"
865
- ) -> Dict[str, Dict[str, str]]:
866
+ assets_list: list[dict[str, str]], asset_name_key: str = "title"
867
+ ) -> dict[str, dict[str, str]]:
866
868
  """Convert a list of assets to a dictionary where keys represent
867
869
  name of assets and are found among values of asset dictionaries.
868
870
 
@@ -889,8 +891,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
889
891
  "asset3": {"href": "qux", "title": "qux-title", "name": "asset3"},
890
892
  }
891
893
  """
892
- asset_names: List[str] = []
893
- assets_dict: Dict[str, Dict[str, str]] = {}
894
+ asset_names: list[str] = []
895
+ assets_dict: dict[str, dict[str, str]] = {}
894
896
 
895
897
  for asset in assets_list:
896
898
  asset_name = asset[asset_name_key]
@@ -899,7 +901,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
899
901
 
900
902
  # we only keep the equivalent of the path basename in the case where the
901
903
  # asset name has a path pattern and this basename is only found once
902
- immutable_asset_indexes: List[int] = []
904
+ immutable_asset_indexes: list[int] = []
903
905
  for i, asset_name in enumerate(asset_names):
904
906
  if i in immutable_asset_indexes:
905
907
  continue
@@ -925,10 +927,10 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
925
927
 
926
928
 
927
929
  def properties_from_json(
928
- json: Dict[str, Any],
929
- mapping: Dict[str, Any],
930
- discovery_config: Optional[Dict[str, Any]] = None,
931
- ) -> Dict[str, Any]:
930
+ json: dict[str, Any],
931
+ mapping: dict[str, Any],
932
+ discovery_config: Optional[dict[str, Any]] = None,
933
+ ) -> dict[str, Any]:
932
934
  """Extract properties from a provider json result.
933
935
 
934
936
  :param json: The representation of a provider result as a json object
@@ -941,7 +943,7 @@ def properties_from_json(
941
943
  `discovery_path` (String representation of jsonpath)
942
944
  :returns: The metadata of the :class:`~eodag.api.product._product.EOProduct`
943
945
  """
944
- properties: Dict[str, Any] = {}
946
+ properties: dict[str, Any] = {}
945
947
  templates = {}
946
948
  used_jsonpaths = []
947
949
  for metadata, value in mapping.items():
@@ -1085,8 +1087,8 @@ def properties_from_xml(
1085
1087
  xml_as_text: AnyStr,
1086
1088
  mapping: Any,
1087
1089
  empty_ns_prefix: str = "ns",
1088
- discovery_config: Optional[Dict[str, Any]] = None,
1089
- ) -> Dict[str, Any]:
1090
+ discovery_config: Optional[dict[str, Any]] = None,
1091
+ ) -> dict[str, Any]:
1090
1092
  """Extract properties from a provider xml result.
1091
1093
 
1092
1094
  :param xml_as_text: The representation of a provider result as xml
@@ -1104,7 +1106,7 @@ def properties_from_xml(
1104
1106
  `discovery_path` (String representation of xpath)
1105
1107
  :returns: the metadata of the :class:`~eodag.api.product._product.EOProduct`
1106
1108
  """
1107
- properties: Dict[str, Any] = {}
1109
+ properties: dict[str, Any] = {}
1108
1110
  templates = {}
1109
1111
  used_xpaths = []
1110
1112
  root = etree.XML(xml_as_text)
@@ -1232,10 +1234,10 @@ def properties_from_xml(
1232
1234
 
1233
1235
 
1234
1236
  def mtd_cfg_as_conversion_and_querypath(
1235
- src_dict: Dict[str, Any],
1236
- dest_dict: Dict[str, Any] = {},
1237
+ src_dict: dict[str, Any],
1238
+ dest_dict: dict[str, Any] = {},
1237
1239
  result_type: str = "json",
1238
- ) -> Dict[str, Any]:
1240
+ ) -> dict[str, Any]:
1239
1241
  """Metadata configuration dictionary to querypath with conversion dictionary
1240
1242
  Transform every src_dict value from jsonpath_str to tuple `(conversion, jsonpath_object)`
1241
1243
  or from xpath_str to tuple `(conversion, xpath_str)`
@@ -1283,8 +1285,8 @@ def mtd_cfg_as_conversion_and_querypath(
1283
1285
 
1284
1286
 
1285
1287
  def format_query_params(
1286
- product_type: str, config: PluginConfig, query_dict: Dict[str, Any]
1287
- ) -> Dict[str, Any]:
1288
+ product_type: str, config: PluginConfig, query_dict: dict[str, Any]
1289
+ ) -> dict[str, Any]:
1288
1290
  """format the search parameters to query parameters"""
1289
1291
  if "raise_errors" in query_dict.keys():
1290
1292
  del query_dict["raise_errors"]
@@ -1296,7 +1298,7 @@ def format_query_params(
1296
1298
  **config.products.get(product_type, {}).get("metadata_mapping", {}),
1297
1299
  )
1298
1300
 
1299
- query_params: Dict[str, Any] = {}
1301
+ query_params: dict[str, Any] = {}
1300
1302
  # Get all the search parameters that are recognised as queryables by the
1301
1303
  # provider (they appear in the queryables dictionary)
1302
1304
  queryables = _get_queryables(query_dict, config, product_type_metadata_mapping)
@@ -1326,8 +1328,8 @@ def format_query_params(
1326
1328
  query_params[eodag_search_key] = formatted_query_param
1327
1329
  else:
1328
1330
  provider_search_key, provider_value = parts
1329
- query_params.setdefault(provider_search_key, []).append(
1330
- format_metadata(provider_value, product_type, **query_dict)
1331
+ query_params[provider_search_key] = format_metadata(
1332
+ provider_value, product_type, **query_dict
1331
1333
  )
1332
1334
  else:
1333
1335
  query_params[provider_search_key] = user_input
@@ -1386,10 +1388,10 @@ def _resolve_hashes(formatted_query_param: str) -> str:
1386
1388
 
1387
1389
 
1388
1390
  def _format_free_text_search(
1389
- config: PluginConfig, metadata_mapping: Dict[str, Any], **kwargs: Any
1390
- ) -> Dict[str, Any]:
1391
+ config: PluginConfig, metadata_mapping: dict[str, Any], **kwargs: Any
1392
+ ) -> dict[str, Any]:
1391
1393
  """Build the free text search parameter using the search parameters"""
1392
- query_params: Dict[str, Any] = {}
1394
+ query_params: dict[str, Any] = {}
1393
1395
  if not getattr(config, "free_text_search_operations", None):
1394
1396
  return query_params
1395
1397
  for param, operations_config in config.free_text_search_operations.items():
@@ -1428,13 +1430,13 @@ def _format_free_text_search(
1428
1430
 
1429
1431
 
1430
1432
  def _get_queryables(
1431
- search_params: Dict[str, Any],
1433
+ search_params: dict[str, Any],
1432
1434
  config: PluginConfig,
1433
- metadata_mapping: Dict[str, Any],
1434
- ) -> Dict[str, Any]:
1435
+ metadata_mapping: dict[str, Any],
1436
+ ) -> dict[str, Any]:
1435
1437
  """Retrieve the metadata mappings that are query-able"""
1436
1438
  logger.debug("Retrieving queryable metadata from metadata_mapping")
1437
- queryables: Dict[str, Any] = {}
1439
+ queryables: dict[str, Any] = {}
1438
1440
  for eodag_search_key, user_input in search_params.items():
1439
1441
  if user_input is not None:
1440
1442
  md_mapping = metadata_mapping.get(eodag_search_key, (None, NOT_MAPPED))
@@ -1481,7 +1483,7 @@ def _get_queryables(
1481
1483
 
1482
1484
 
1483
1485
  def get_queryable_from_provider(
1484
- provider_queryable: str, metadata_mapping: Dict[str, Union[str, List[str]]]
1486
+ provider_queryable: str, metadata_mapping: dict[str, Union[str, list[str]]]
1485
1487
  ) -> Optional[str]:
1486
1488
  """Get EODAG configured queryable parameter from provider queryable parameter
1487
1489
 
@@ -1499,13 +1501,17 @@ def get_queryable_from_provider(
1499
1501
  ind = mapping_values.index(provider_queryable)
1500
1502
  return Queryables.get_queryable_from_alias(list(metadata_mapping.keys())[ind])
1501
1503
  for param, param_conf in metadata_mapping.items():
1502
- if isinstance(param_conf, list) and re.search(pattern, param_conf[0]):
1504
+ if (
1505
+ isinstance(param_conf, list)
1506
+ and param_conf[0]
1507
+ and re.search(pattern, param_conf[0])
1508
+ ):
1503
1509
  return Queryables.get_queryable_from_alias(param)
1504
1510
  return None
1505
1511
 
1506
1512
 
1507
1513
  def get_provider_queryable_path(
1508
- queryable: str, metadata_mapping: Dict[str, Union[str, List[str]]]
1514
+ queryable: str, metadata_mapping: dict[str, Union[str, list[str]]]
1509
1515
  ) -> Optional[str]:
1510
1516
  """Get EODAG configured queryable path from its parameter
1511
1517
 
@@ -1522,8 +1528,8 @@ def get_provider_queryable_path(
1522
1528
 
1523
1529
  def get_provider_queryable_key(
1524
1530
  eodag_key: str,
1525
- provider_queryables: Dict[str, Any],
1526
- metadata_mapping: Dict[str, Union[List[Any], str]],
1531
+ provider_queryables: dict[str, Any],
1532
+ metadata_mapping: dict[str, Union[list[Any], str]],
1527
1533
  ) -> str:
1528
1534
  """Finds the provider queryable corresponding to the given eodag key based on the metadata mapping
1529
1535
 
@@ -18,17 +18,7 @@
18
18
  from __future__ import annotations
19
19
 
20
20
  from collections import UserList
21
- from typing import (
22
- TYPE_CHECKING,
23
- Annotated,
24
- Any,
25
- Dict,
26
- Iterable,
27
- List,
28
- Optional,
29
- Tuple,
30
- Union,
31
- )
21
+ from typing import TYPE_CHECKING, Annotated, Any, Iterable, Optional, Union
32
22
 
33
23
  from shapely.geometry import GeometryCollection, shape
34
24
  from typing_extensions import Doc
@@ -56,17 +46,17 @@ class SearchResult(UserList):
56
46
  :ivar number_matched: Estimated total number of matching results
57
47
  """
58
48
 
59
- data: List[EOProduct]
49
+ data: list[EOProduct]
60
50
 
61
51
  errors: Annotated[
62
- List[Tuple[str, Exception]], Doc("Tuple of provider name, exception")
52
+ list[tuple[str, Exception]], Doc("Tuple of provider name, exception")
63
53
  ]
64
54
 
65
55
  def __init__(
66
56
  self,
67
- products: List[EOProduct],
57
+ products: list[EOProduct],
68
58
  number_matched: Optional[int] = None,
69
- errors: List[Tuple[str, Exception]] = [],
59
+ errors: list[tuple[str, Exception]] = [],
70
60
  ) -> None:
71
61
  super().__init__(products)
72
62
  self.number_matched = number_matched
@@ -92,7 +82,7 @@ class SearchResult(UserList):
92
82
  return self.crunch(FilterDate(dict(start=start, end=end)))
93
83
 
94
84
  def filter_latest_intersect(
95
- self, geometry: Union[Dict[str, Any], BaseGeometry, Any]
85
+ self, geometry: Union[dict[str, Any], BaseGeometry, Any]
96
86
  ) -> SearchResult:
97
87
  """
98
88
  Apply :class:`~eodag.plugins.crunch.filter_latest_intersect.FilterLatestIntersect` crunch,
@@ -148,7 +138,7 @@ class SearchResult(UserList):
148
138
  return self.filter_property(storageStatus="ONLINE")
149
139
 
150
140
  @staticmethod
151
- def from_geojson(feature_collection: Dict[str, Any]) -> SearchResult:
141
+ def from_geojson(feature_collection: dict[str, Any]) -> SearchResult:
152
142
  """Builds an :class:`~eodag.api.search_result.SearchResult` object from its representation as geojson
153
143
 
154
144
  :param feature_collection: A collection representing a search result.
@@ -161,7 +151,7 @@ class SearchResult(UserList):
161
151
  ]
162
152
  )
163
153
 
164
- def as_geojson_object(self) -> Dict[str, Any]:
154
+ def as_geojson_object(self) -> dict[str, Any]:
165
155
  """GeoJSON representation of SearchResult"""
166
156
  return {
167
157
  "type": "FeatureCollection",
@@ -182,7 +172,7 @@ class SearchResult(UserList):
182
172
  return self.as_shapely_geometry_object().wkt
183
173
 
184
174
  @property
185
- def __geo_interface__(self) -> Dict[str, Any]:
175
+ def __geo_interface__(self) -> dict[str, Any]:
186
176
  """Implements the geo-interface protocol.
187
177
 
188
178
  See https://gist.github.com/sgillies/2217756
@@ -230,9 +220,9 @@ class RawSearchResult(UserList):
230
220
  :param results: A list of raw/unparsed search results
231
221
  """
232
222
 
233
- data: List[Any]
234
- query_params: Dict[str, Any]
235
- product_type_def_params: Dict[str, Any]
223
+ data: list[Any]
224
+ query_params: dict[str, Any]
225
+ product_type_def_params: dict[str, Any]
236
226
 
237
- def __init__(self, results: List[Any]) -> None:
227
+ def __init__(self, results: list[Any]) -> None:
238
228
  super(RawSearchResult, self).__init__(results)
eodag/cli.py CHANGED
@@ -48,7 +48,7 @@ import shutil
48
48
  import sys
49
49
  import textwrap
50
50
  from importlib.metadata import metadata
51
- from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Set
51
+ from typing import TYPE_CHECKING, Any, Mapping
52
52
 
53
53
  import click
54
54
 
@@ -104,7 +104,7 @@ class MutuallyExclusiveOption(click.Option):
104
104
  super(MutuallyExclusiveOption, self).__init__(*args, **kwargs)
105
105
 
106
106
  def handle_parse_result(
107
- self, ctx: Context, opts: Mapping[str, Any], args: List[str]
107
+ self, ctx: Context, opts: Mapping[str, Any], args: list[str]
108
108
  ):
109
109
  """Raise error or use parent handle_parse_result()"""
110
110
  if self.mutually_exclusive.intersection(opts) and self.name in opts:
@@ -359,9 +359,9 @@ def search_crunch(ctx: Context, **kwargs: Any) -> None:
359
359
  count = kwargs.pop("count")
360
360
 
361
361
  # Process inputs for crunch
362
- cruncher_names: Set[Any] = set(kwargs.pop("cruncher") or [])
362
+ cruncher_names: set[Any] = set(kwargs.pop("cruncher") or [])
363
363
  cruncher_args = kwargs.pop("cruncher_args")
364
- cruncher_args_dict: Dict[str, Dict[str, Any]] = {}
364
+ cruncher_args_dict: dict[str, dict[str, Any]] = {}
365
365
  if cruncher_args:
366
366
  for cruncher, argname, argval in cruncher_args:
367
367
  cruncher_args_dict.setdefault(cruncher, {}).setdefault(argname, argval)