eodag 3.0.0b2__py3-none-any.whl → 3.0.1__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 (84) hide show
  1. eodag/__init__.py +6 -8
  2. eodag/api/core.py +295 -287
  3. eodag/api/product/__init__.py +10 -4
  4. eodag/api/product/_assets.py +2 -14
  5. eodag/api/product/_product.py +16 -30
  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 +12 -31
  9. eodag/api/search_result.py +33 -12
  10. eodag/cli.py +35 -19
  11. eodag/config.py +455 -155
  12. eodag/plugins/apis/base.py +13 -7
  13. eodag/plugins/apis/ecmwf.py +16 -7
  14. eodag/plugins/apis/usgs.py +68 -16
  15. eodag/plugins/authentication/aws_auth.py +25 -7
  16. eodag/plugins/authentication/base.py +10 -1
  17. eodag/plugins/authentication/generic.py +14 -3
  18. eodag/plugins/authentication/header.py +12 -4
  19. eodag/plugins/authentication/keycloak.py +41 -22
  20. eodag/plugins/authentication/oauth.py +11 -1
  21. eodag/plugins/authentication/openid_connect.py +183 -167
  22. eodag/plugins/authentication/qsauth.py +12 -4
  23. eodag/plugins/authentication/sas_auth.py +19 -2
  24. eodag/plugins/authentication/token.py +59 -11
  25. eodag/plugins/authentication/token_exchange.py +19 -19
  26. eodag/plugins/crunch/base.py +7 -2
  27. eodag/plugins/crunch/filter_date.py +8 -11
  28. eodag/plugins/crunch/filter_latest_intersect.py +5 -7
  29. eodag/plugins/crunch/filter_latest_tpl_name.py +2 -5
  30. eodag/plugins/crunch/filter_overlap.py +9 -15
  31. eodag/plugins/crunch/filter_property.py +9 -14
  32. eodag/plugins/download/aws.py +84 -99
  33. eodag/plugins/download/base.py +36 -77
  34. eodag/plugins/download/creodias_s3.py +11 -2
  35. eodag/plugins/download/http.py +134 -109
  36. eodag/plugins/download/s3rest.py +37 -43
  37. eodag/plugins/manager.py +173 -41
  38. eodag/plugins/search/__init__.py +9 -9
  39. eodag/plugins/search/base.py +35 -35
  40. eodag/plugins/search/build_search_result.py +55 -64
  41. eodag/plugins/search/cop_marine.py +113 -32
  42. eodag/plugins/search/creodias_s3.py +20 -8
  43. eodag/plugins/search/csw.py +41 -1
  44. eodag/plugins/search/data_request_search.py +119 -14
  45. eodag/plugins/search/qssearch.py +619 -197
  46. eodag/plugins/search/static_stac_search.py +25 -23
  47. eodag/resources/ext_product_types.json +1 -1
  48. eodag/resources/product_types.yml +211 -56
  49. eodag/resources/providers.yml +1762 -1809
  50. eodag/resources/stac.yml +3 -163
  51. eodag/resources/user_conf_template.yml +134 -119
  52. eodag/rest/config.py +1 -2
  53. eodag/rest/constants.py +0 -1
  54. eodag/rest/core.py +70 -92
  55. eodag/rest/errors.py +181 -0
  56. eodag/rest/server.py +24 -330
  57. eodag/rest/stac.py +105 -630
  58. eodag/rest/types/eodag_search.py +17 -15
  59. eodag/rest/types/queryables.py +5 -14
  60. eodag/rest/types/stac_search.py +18 -13
  61. eodag/rest/utils/rfc3339.py +0 -1
  62. eodag/types/__init__.py +24 -6
  63. eodag/types/download_args.py +14 -5
  64. eodag/types/queryables.py +1 -2
  65. eodag/types/search_args.py +10 -11
  66. eodag/types/whoosh.py +0 -2
  67. eodag/utils/__init__.py +97 -136
  68. eodag/utils/constraints.py +0 -8
  69. eodag/utils/exceptions.py +23 -9
  70. eodag/utils/import_system.py +0 -4
  71. eodag/utils/logging.py +37 -80
  72. eodag/utils/notebook.py +4 -4
  73. eodag/utils/requests.py +13 -23
  74. eodag/utils/rest.py +0 -4
  75. eodag/utils/stac_reader.py +3 -15
  76. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/METADATA +41 -24
  77. eodag-3.0.1.dist-info/RECORD +109 -0
  78. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/WHEEL +1 -1
  79. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/entry_points.txt +1 -0
  80. eodag/resources/constraints/climate-dt.json +0 -13
  81. eodag/resources/constraints/extremes-dt.json +0 -8
  82. eodag-3.0.0b2.dist-info/RECORD +0 -110
  83. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/LICENSE +0 -0
  84. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/top_level.txt +0 -0
@@ -49,10 +49,7 @@ from eodag.rest.utils.cql_evaluate import EodagEvaluator
49
49
  from eodag.utils import DEFAULT_ITEMS_PER_PAGE
50
50
 
51
51
  if TYPE_CHECKING:
52
- try:
53
- from typing import Self
54
- except ImportError:
55
- from _typeshed import Self
52
+ from typing_extensions import Self
56
53
 
57
54
  Geometry = Union[
58
55
  Dict[str, Any],
@@ -113,18 +110,11 @@ class EODAGSearch(BaseModel):
113
110
  illuminationAzimuthAngle: Optional[float] = Field(None, alias="view:sun_azimuth")
114
111
  page: Optional[int] = Field(1)
115
112
  items_per_page: int = Field(DEFAULT_ITEMS_PER_PAGE, alias="limit")
116
- sortBy: Optional[List[Tuple[str, str]]] = Field(None, alias="sortby")
113
+ sort_by: Optional[List[Tuple[str, str]]] = Field(None, alias="sortby")
117
114
  raise_errors: bool = False
118
115
 
119
116
  _to_eodag_map: Dict[str, str]
120
117
 
121
- @model_validator(mode="after")
122
- def set_raise_errors(self) -> Self:
123
- """Set raise_errors to True if provider is set"""
124
- if self.provider:
125
- self.raise_errors = True
126
- return self
127
-
128
118
  @model_validator(mode="after")
129
119
  def remove_timeFromAscendingNode(self) -> Self: # pylint: disable=invalid-name
130
120
  """TimeFromAscendingNode are just used for translation and not for search"""
@@ -314,14 +304,14 @@ class EODAGSearch(BaseModel):
314
304
  return ",".join(v)
315
305
  return v
316
306
 
317
- @field_validator("sortBy", mode="before")
307
+ @field_validator("sort_by", mode="before")
318
308
  @classmethod
319
309
  def parse_sortby(
320
310
  cls,
321
311
  sortby_post_params: List[Dict[str, str]],
322
312
  ) -> List[Tuple[str, str]]:
323
313
  """
324
- Convert STAC POST sortby to EODAG sortby
314
+ Convert STAC POST sortby to EODAG sort_by
325
315
  """
326
316
  special_fields = {
327
317
  "start": "startTimeFromAscendingNode",
@@ -370,9 +360,21 @@ class EODAGSearch(BaseModel):
370
360
  return cls._to_eodag_map.get(value, value)
371
361
 
372
362
  @classmethod
373
- def to_stac(cls, field_name: str) -> str:
363
+ def to_stac(
364
+ cls,
365
+ field_name: str,
366
+ stac_item_properties: Optional[List[str]] = None,
367
+ provider: Optional[str] = None,
368
+ ) -> str:
374
369
  """Get the alias of a field in a Pydantic model"""
375
370
  field = cls.model_fields.get(field_name)
376
371
  if field is not None and field.alias is not None:
377
372
  return field.alias
373
+ if (
374
+ provider
375
+ and ":" not in field_name
376
+ and stac_item_properties
377
+ and field_name not in stac_item_properties
378
+ ):
379
+ return f"{provider}:{field_name}"
378
380
  return field_name
@@ -17,7 +17,7 @@
17
17
  # limitations under the License.
18
18
  from __future__ import annotations
19
19
 
20
- from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Union
20
+ from typing import TYPE_CHECKING, Annotated, Any, ClassVar, Dict, List, Optional, Union
21
21
 
22
22
  from pydantic import (
23
23
  BaseModel,
@@ -32,7 +32,6 @@ from pydantic import (
32
32
  from eodag.rest.types.eodag_search import EODAGSearch
33
33
  from eodag.rest.utils.rfc3339 import str_to_interval
34
34
  from eodag.types import python_field_definition_to_json
35
- from eodag.utils import Annotated
36
35
 
37
36
  if TYPE_CHECKING:
38
37
  from pydantic.fields import FieldInfo
@@ -51,14 +50,16 @@ class QueryablesGetParams(BaseModel):
51
50
  dumped: Dict[str, Any] = handler(self)
52
51
  return {EODAGSearch.to_eodag(k): v for k, v in dumped.items()}
53
52
 
54
- @computed_field
53
+ # use [prop-decorator] mypy error code when mypy==1.12 is released
54
+ @computed_field # type: ignore[misc]
55
55
  @property
56
56
  def start_datetime(self) -> Optional[str]:
57
57
  """Extract start_datetime property from datetime"""
58
58
  start = str_to_interval(self.datetime)[0]
59
59
  return start.strftime("%Y-%m-%dT%H:%M:%SZ") if start else None
60
60
 
61
- @computed_field
61
+ # use [prop-decorator] mypy error code when mypy==1.12 is released
62
+ @computed_field # type: ignore[misc]
62
63
  @property
63
64
  def end_datetime(self) -> Optional[str]:
64
65
  """Extract end_datetime property from datetime"""
@@ -70,11 +71,8 @@ class StacQueryableProperty(BaseModel):
70
71
  """A class representing a queryable property.
71
72
 
72
73
  :param description: The description of the queryables property
73
- :type description: str
74
74
  :param ref: (optional) A reference link to the schema of the property.
75
- :type ref: str
76
75
  :param type: (optional) possible types of the property
77
- :type type: list[str]
78
76
  """
79
77
 
80
78
  description: str
@@ -114,19 +112,12 @@ class StacQueryables(BaseModel):
114
112
  """A class representing queryable properties for the STAC API.
115
113
 
116
114
  :param json_schema: The URL of the JSON schema.
117
- :type json_schema: str
118
115
  :param q_id: (optional) The identifier of the queryables.
119
- :type q_id: str
120
116
  :param q_type: The type of the object.
121
- :type q_type: str
122
117
  :param title: The title of the queryables.
123
- :type title: str
124
118
  :param description: The description of the queryables
125
- :type description: str
126
119
  :param properties: A dictionary of queryable properties.
127
- :type properties: dict
128
120
  :param additional_properties: Whether additional properties are allowed.
129
- :type additional_properties: bool
130
121
  """
131
122
 
132
123
  json_schema: str = Field(
@@ -16,9 +16,20 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
  """Model describing a STAC search POST request"""
19
+
19
20
  from __future__ import annotations
20
21
 
21
- from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union
22
+ from typing import (
23
+ TYPE_CHECKING,
24
+ Annotated,
25
+ Any,
26
+ Dict,
27
+ List,
28
+ Literal,
29
+ Optional,
30
+ Tuple,
31
+ Union,
32
+ )
22
33
 
23
34
  import geojson
24
35
  from pydantic import (
@@ -42,16 +53,12 @@ from shapely.geometry import (
42
53
  shape,
43
54
  )
44
55
  from shapely.geometry.base import GEOMETRY_TYPES, BaseGeometry
45
- from typing_extensions import Annotated
46
56
 
47
57
  from eodag.rest.utils.rfc3339 import rfc3339_str_to_datetime, str_to_interval
48
58
  from eodag.utils.exceptions import ValidationError
49
59
 
50
60
  if TYPE_CHECKING:
51
- try:
52
- from typing import Self
53
- except ImportError:
54
- from _typeshed import Self
61
+ from typing_extensions import Self
55
62
 
56
63
  NumType = Union[float, int]
57
64
 
@@ -74,15 +81,13 @@ Geometry = Union[
74
81
  Direction = Annotated[Literal["asc", "desc"], StringConstraints(min_length=1)]
75
82
 
76
83
 
77
- class Sortby(BaseModel):
84
+ class SortBy(BaseModel):
78
85
  """
79
86
  A class representing a parameter with which we want to sort results and its sorting order in a
80
87
  POST search
81
88
 
82
89
  :param field: The name of the parameter with which we want to sort results
83
- :type field: str
84
90
  :param direction: The sorting order of the parameter
85
- :type direction: str
86
91
  """
87
92
 
88
93
  __pydantic_config__ = ConfigDict(extra="forbid")
@@ -120,7 +125,7 @@ class SearchPostRequest(BaseModel):
120
125
  description="The language used for filtering.",
121
126
  validate_default=True,
122
127
  )
123
- sortby: Optional[List[Sortby]] = None
128
+ sortby: Optional[List[SortBy]] = None
124
129
  crunch: Optional[str] = None
125
130
 
126
131
  @field_serializer("intersects")
@@ -262,16 +267,16 @@ class SearchPostRequest(BaseModel):
262
267
 
263
268
  def sortby2list(
264
269
  v: Optional[str],
265
- ) -> Optional[List[Sortby]]:
270
+ ) -> Optional[List[SortBy]]:
266
271
  """
267
272
  Convert sortby filter parameter GET syntax to POST syntax
268
273
  """
269
274
  if not v:
270
275
  return None
271
- sortby: List[Sortby] = []
276
+ sortby: List[SortBy] = []
272
277
  for sortby_param in v.split(","):
273
278
  sortby_param = sortby_param.strip()
274
279
  direction: Direction = "desc" if sortby_param.startswith("-") else "asc"
275
280
  field = sortby_param.lstrip("+-")
276
- sortby.append(Sortby(field=field, direction=direction))
281
+ sortby.append(SortBy(field=field, direction=direction))
277
282
  return sortby
@@ -33,7 +33,6 @@ def str_to_interval(
33
33
 
34
34
  :param interval: The interval string to convert to a :class:`datetime.datetime`
35
35
  tuple.
36
- :type interval: str
37
36
 
38
37
  :raises: :class:`ValueError`
39
38
  """
eodag/types/__init__.py CHANGED
@@ -18,13 +18,25 @@
18
18
  """EODAG types"""
19
19
  from __future__ import annotations
20
20
 
21
- from typing import Any, Dict, List, Literal, Optional, Tuple, TypedDict, Union
21
+ from typing import (
22
+ Annotated,
23
+ Any,
24
+ Dict,
25
+ List,
26
+ Literal,
27
+ Optional,
28
+ Tuple,
29
+ TypedDict,
30
+ Union,
31
+ get_args,
32
+ get_origin,
33
+ )
22
34
 
23
35
  from annotated_types import Gt, Lt
24
36
  from pydantic import Field
25
37
  from pydantic.fields import FieldInfo
26
38
 
27
- from eodag.utils import Annotated, copy_deepcopy, get_args, get_origin
39
+ from eodag.utils import copy_deepcopy
28
40
  from eodag.utils.exceptions import ValidationError
29
41
 
30
42
  # Types mapping from JSON Schema and OpenAPI 3.1.0 specifications to Python
@@ -183,7 +195,7 @@ def python_field_definition_to_json(
183
195
  """Get json field definition from python `typing.Annotated`
184
196
 
185
197
  >>> from pydantic import Field
186
- >>> from eodag.utils import Annotated
198
+ >>> from typing import Annotated
187
199
  >>> python_field_definition_to_json(
188
200
  ... Annotated[
189
201
  ... Optional[str],
@@ -200,7 +212,7 @@ def python_field_definition_to_json(
200
212
  "%s must be an instance of Annotated" % python_field_definition
201
213
  )
202
214
 
203
- json_field_definition = dict()
215
+ json_field_definition: Dict[str, Any] = dict()
204
216
 
205
217
  python_field_args = get_args(python_field_definition)
206
218
 
@@ -210,6 +222,10 @@ def python_field_definition_to_json(
210
222
  type_data = python_type_to_json(type(enum_args[0]))
211
223
  if isinstance(type_data, str):
212
224
  json_field_definition["type"] = type_data
225
+ elif type_data is None:
226
+ json_field_definition["type"] = json_field_definition[
227
+ "min"
228
+ ] = json_field_definition["max"] = None
213
229
  else:
214
230
  json_field_definition["type"] = [row["type"] for row in type_data]
215
231
  json_field_definition["min"] = [
@@ -224,6 +240,10 @@ def python_field_definition_to_json(
224
240
  field_type = python_type_to_json(python_field_args[0])
225
241
  if isinstance(field_type, str):
226
242
  json_field_definition["type"] = field_type
243
+ elif field_type is None:
244
+ json_field_definition["type"] = json_field_definition[
245
+ "min"
246
+ ] = json_field_definition["max"] = None
227
247
  else:
228
248
  json_field_definition["type"] = [row["type"] for row in field_type]
229
249
  json_field_definition["min"] = [
@@ -300,9 +320,7 @@ class ProviderSortables(TypedDict):
300
320
  maximum number of used sortable(s) in a search request with the provider
301
321
 
302
322
  :param sortables: The list of sortable parameter(s) of a provider
303
- :type sortables: list[str]
304
323
  :param max_sort_params: (optional) The allowed maximum number of sortable(s) in a search request with the provider
305
- :type max_sort_params: int
306
324
  """
307
325
 
308
326
  sortables: List[str]
@@ -17,15 +17,24 @@
17
17
  # limitations under the License.
18
18
  from __future__ import annotations
19
19
 
20
- from typing import Dict, TypedDict
20
+ from typing import Dict, Optional, TypedDict
21
21
 
22
22
 
23
23
  class DownloadConf(TypedDict, total=False):
24
- """Download configuration"""
24
+ """Download configuration
25
25
 
26
- outputs_prefix: str
27
- outputs_extension: str
26
+ :cvar output_dir: where to store downloaded products, as an absolute file path
27
+ (Default: local temporary directory)
28
+ :cvar output_extension: downloaded file extension
29
+ :cvar extract: whether to extract the downloaded products, only applies to archived products
30
+ :cvar dl_url_params: additional parameters to pass over to the download url as an url parameter
31
+ :cvar delete_archive: whether to delete the downloaded archives
32
+ :cvar asset: regex filter to identify assets to download
33
+ """
34
+
35
+ output_dir: str
36
+ output_extension: str
28
37
  extract: bool
29
38
  dl_url_params: Dict[str, str]
30
39
  delete_archive: bool
31
- asset: str
40
+ asset: Optional[str]
eodag/types/queryables.py CHANGED
@@ -15,12 +15,11 @@
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 Optional
18
+ from typing import Annotated, Optional
19
19
 
20
20
  from annotated_types import Lt
21
21
  from pydantic import BaseModel, Field
22
22
  from pydantic.types import PositiveInt
23
- from typing_extensions import Annotated
24
23
 
25
24
  Percentage = Annotated[PositiveInt, Lt(100)]
26
25
 
@@ -17,7 +17,7 @@
17
17
  # limitations under the License.
18
18
  import re
19
19
  from datetime import datetime
20
- from typing import Any, Dict, List, Optional, Tuple, Union, cast
20
+ from typing import Annotated, Any, Dict, List, Optional, Tuple, Union, cast
21
21
 
22
22
  from annotated_types import MinLen
23
23
  from pydantic import BaseModel, ConfigDict, Field, conint, field_validator
@@ -27,7 +27,7 @@ from shapely.geometry import Polygon, shape
27
27
  from shapely.geometry.base import GEOMETRY_TYPES, BaseGeometry
28
28
 
29
29
  from eodag.types.bbox import BBox
30
- from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE, Annotated
30
+ from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE
31
31
  from eodag.utils.exceptions import ValidationError
32
32
 
33
33
  NumType = Union[float, int]
@@ -51,7 +51,7 @@ class SearchArgs(BaseModel):
51
51
  locations: Optional[Dict[str, str]] = Field(None)
52
52
  page: Optional[int] = Field(DEFAULT_PAGE, gt=0) # type: ignore
53
53
  items_per_page: Optional[PositiveInt] = Field(DEFAULT_ITEMS_PER_PAGE) # type: ignore
54
- sortBy: Optional[SortByList] = Field(None) # type: ignore
54
+ sort_by: Optional[SortByList] = Field(None) # type: ignore
55
55
 
56
56
  @field_validator("start", "end", mode="before")
57
57
  @classmethod
@@ -87,18 +87,17 @@ class SearchArgs(BaseModel):
87
87
 
88
88
  raise TypeError(f"Invalid geometry type: {type(v)}")
89
89
 
90
- @field_validator("sortBy", mode="before")
90
+ @field_validator("sort_by", mode="before")
91
91
  @classmethod
92
92
  def check_sort_by_arg(
93
- cls, sort_by_arg: Optional[SortByList] # type: ignore
93
+ cls,
94
+ sort_by_arg: Optional[SortByList], # type: ignore
94
95
  ) -> Optional[SortByList]: # type: ignore
95
- """Check if the sortBy argument is correct
96
+ """Check if the sort_by argument is correct
96
97
 
97
- :param sort_by_arg: The sortBy argument
98
- :type sort_by_arg: str
99
- :returns: The sortBy argument with sorting order parsed (whitespace(s) are
98
+ :param sort_by_arg: The sort_by argument
99
+ :returns: The sort_by argument with sorting order parsed (whitespace(s) are
100
100
  removed and only the 3 first letters in uppercase are kept)
101
- :rtype: str
102
101
  """
103
102
  if sort_by_arg is None:
104
103
  return None
@@ -120,7 +119,7 @@ class SearchArgs(BaseModel):
120
119
  )
121
120
  sort_by_arg[i] = (sort_param, sort_order[:3])
122
121
  # remove duplicates
123
- pruned_sort_by_arg: SortByList = list(set(sort_by_arg)) # type: ignore
122
+ pruned_sort_by_arg: SortByList = list(dict.fromkeys(sort_by_arg)) # type: ignore
124
123
  for i, sort_by_tuple in enumerate(pruned_sort_by_arg):
125
124
  for j, sort_by_tuple_tmp in enumerate(pruned_sort_by_arg):
126
125
  # since duplicated tuples or dictionnaries have been removed, if two sorting parameters are equal,
eodag/types/whoosh.py CHANGED
@@ -56,9 +56,7 @@ class EODAGQueryParser(QueryParser):
56
56
  EODAG QueryParser initialization
57
57
 
58
58
  :param filters: list of fieldnames to filter on
59
- :type filters: List[str]
60
59
  :param schema: Whoosh Schema
61
- :type schma: :class:whoosh.fields.Schema
62
60
  """
63
61
  super().__init__(
64
62
  None,