eodag 3.1.0b1__py3-none-any.whl → 3.1.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eodag/api/core.py +59 -52
- eodag/api/product/_assets.py +5 -5
- eodag/api/product/_product.py +27 -12
- eodag/api/product/drivers/__init__.py +81 -4
- eodag/api/product/drivers/base.py +65 -4
- eodag/api/product/drivers/generic.py +65 -0
- eodag/api/product/drivers/sentinel1.py +97 -0
- eodag/api/product/drivers/sentinel2.py +95 -0
- eodag/api/product/metadata_mapping.py +62 -74
- eodag/api/search_result.py +13 -23
- eodag/cli.py +4 -4
- eodag/config.py +66 -69
- eodag/plugins/apis/base.py +1 -1
- eodag/plugins/apis/ecmwf.py +10 -9
- eodag/plugins/apis/usgs.py +11 -10
- eodag/plugins/authentication/aws_auth.py +16 -13
- eodag/plugins/authentication/base.py +5 -3
- eodag/plugins/authentication/header.py +3 -3
- eodag/plugins/authentication/keycloak.py +4 -4
- eodag/plugins/authentication/oauth.py +7 -3
- eodag/plugins/authentication/openid_connect.py +14 -14
- eodag/plugins/authentication/sas_auth.py +4 -4
- eodag/plugins/authentication/token.py +7 -7
- eodag/plugins/authentication/token_exchange.py +1 -1
- eodag/plugins/base.py +4 -4
- eodag/plugins/crunch/base.py +4 -4
- eodag/plugins/crunch/filter_date.py +4 -4
- eodag/plugins/crunch/filter_latest_intersect.py +6 -6
- eodag/plugins/crunch/filter_latest_tpl_name.py +7 -7
- eodag/plugins/crunch/filter_overlap.py +4 -4
- eodag/plugins/crunch/filter_property.py +4 -4
- eodag/plugins/download/aws.py +47 -66
- eodag/plugins/download/base.py +8 -17
- eodag/plugins/download/creodias_s3.py +2 -2
- eodag/plugins/download/http.py +30 -32
- eodag/plugins/download/s3rest.py +5 -4
- eodag/plugins/manager.py +10 -20
- eodag/plugins/search/__init__.py +6 -5
- eodag/plugins/search/base.py +35 -40
- eodag/plugins/search/build_search_result.py +69 -68
- eodag/plugins/search/cop_marine.py +22 -12
- eodag/plugins/search/creodias_s3.py +8 -78
- eodag/plugins/search/csw.py +11 -11
- eodag/plugins/search/data_request_search.py +16 -15
- eodag/plugins/search/qssearch.py +56 -52
- eodag/plugins/search/stac_list_assets.py +85 -0
- eodag/plugins/search/static_stac_search.py +3 -3
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +288 -288
- eodag/resources/providers.yml +146 -6
- eodag/resources/stac_api.yml +2 -2
- eodag/resources/user_conf_template.yml +11 -0
- eodag/rest/cache.py +2 -2
- eodag/rest/config.py +3 -3
- eodag/rest/core.py +24 -24
- eodag/rest/errors.py +5 -5
- eodag/rest/server.py +3 -11
- eodag/rest/stac.py +40 -38
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +23 -23
- eodag/rest/types/queryables.py +13 -13
- eodag/rest/types/stac_search.py +15 -25
- eodag/rest/utils/__init__.py +11 -21
- eodag/rest/utils/cql_evaluate.py +6 -6
- eodag/rest/utils/rfc3339.py +2 -2
- eodag/types/__init__.py +24 -18
- eodag/types/bbox.py +2 -2
- eodag/types/download_args.py +2 -2
- eodag/types/queryables.py +5 -2
- eodag/types/search_args.py +4 -4
- eodag/types/whoosh.py +1 -3
- eodag/utils/__init__.py +81 -40
- eodag/utils/exceptions.py +2 -2
- eodag/utils/import_system.py +2 -2
- eodag/utils/requests.py +2 -2
- eodag/utils/rest.py +2 -2
- eodag/utils/s3.py +208 -0
- eodag/utils/stac_reader.py +10 -10
- {eodag-3.1.0b1.dist-info → eodag-3.1.0b2.dist-info}/METADATA +5 -4
- eodag-3.1.0b2.dist-info/RECORD +113 -0
- {eodag-3.1.0b1.dist-info → eodag-3.1.0b2.dist-info}/entry_points.txt +1 -0
- eodag-3.1.0b1.dist-info/RECORD +0 -108
- {eodag-3.1.0b1.dist-info → eodag-3.1.0b2.dist-info}/LICENSE +0 -0
- {eodag-3.1.0b1.dist-info → eodag-3.1.0b2.dist-info}/WHEEL +0 -0
- {eodag-3.1.0b1.dist-info → eodag-3.1.0b2.dist-info}/top_level.txt +0 -0
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
|
-
from typing import TYPE_CHECKING, Any
|
|
21
|
+
from typing import TYPE_CHECKING, Any
|
|
22
22
|
|
|
23
23
|
from eodag.plugins.crunch.base import Crunch
|
|
24
24
|
from eodag.utils import get_geometry_from_various
|
|
@@ -49,8 +49,8 @@ class FilterOverlap(Crunch):
|
|
|
49
49
|
"""
|
|
50
50
|
|
|
51
51
|
def proceed(
|
|
52
|
-
self, products:
|
|
53
|
-
) ->
|
|
52
|
+
self, products: list[EOProduct], **search_params: Any
|
|
53
|
+
) -> list[EOProduct]:
|
|
54
54
|
"""Execute crunch: Filter products, retaining only those that are overlapping with the search_extent
|
|
55
55
|
|
|
56
56
|
:param products: A list of products resulting from a search
|
|
@@ -58,7 +58,7 @@ class FilterOverlap(Crunch):
|
|
|
58
58
|
:returns: The filtered products
|
|
59
59
|
"""
|
|
60
60
|
logger.debug("Start filtering for overlapping products")
|
|
61
|
-
filtered:
|
|
61
|
+
filtered: list[EOProduct] = []
|
|
62
62
|
add_to_filtered = filtered.append
|
|
63
63
|
|
|
64
64
|
search_geom = get_geometry_from_various(**search_params)
|
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
21
|
import operator
|
|
22
|
-
from typing import TYPE_CHECKING, Any
|
|
22
|
+
from typing import TYPE_CHECKING, Any
|
|
23
23
|
|
|
24
24
|
from eodag.plugins.crunch.base import Crunch
|
|
25
25
|
|
|
@@ -42,8 +42,8 @@ class FilterProperty(Crunch):
|
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
def proceed(
|
|
45
|
-
self, products:
|
|
46
|
-
) ->
|
|
45
|
+
self, products: list[EOProduct], **search_params: Any
|
|
46
|
+
) -> list[EOProduct]:
|
|
47
47
|
"""Execute crunch: Filter products, retaining only those that match property filtering
|
|
48
48
|
|
|
49
49
|
:param products: A list of products resulting from a search
|
|
@@ -72,7 +72,7 @@ class FilterProperty(Crunch):
|
|
|
72
72
|
property_key,
|
|
73
73
|
property_value,
|
|
74
74
|
)
|
|
75
|
-
filtered:
|
|
75
|
+
filtered: list[EOProduct] = []
|
|
76
76
|
add_to_filtered = filtered.append
|
|
77
77
|
|
|
78
78
|
for product in products:
|
eodag/plugins/download/aws.py
CHANGED
|
@@ -23,21 +23,7 @@ import re
|
|
|
23
23
|
from datetime import datetime
|
|
24
24
|
from itertools import chain
|
|
25
25
|
from pathlib import Path
|
|
26
|
-
from typing import
|
|
27
|
-
TYPE_CHECKING,
|
|
28
|
-
Any,
|
|
29
|
-
Callable,
|
|
30
|
-
Dict,
|
|
31
|
-
Iterator,
|
|
32
|
-
List,
|
|
33
|
-
Match,
|
|
34
|
-
Optional,
|
|
35
|
-
Set,
|
|
36
|
-
Tuple,
|
|
37
|
-
TypedDict,
|
|
38
|
-
Union,
|
|
39
|
-
cast,
|
|
40
|
-
)
|
|
26
|
+
from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Union, cast
|
|
41
27
|
|
|
42
28
|
import boto3
|
|
43
29
|
import requests
|
|
@@ -81,6 +67,7 @@ if TYPE_CHECKING:
|
|
|
81
67
|
from eodag.api.product import EOProduct
|
|
82
68
|
from eodag.api.search_result import SearchResult
|
|
83
69
|
from eodag.config import PluginConfig
|
|
70
|
+
from eodag.types import S3SessionKwargs
|
|
84
71
|
from eodag.types.download_args import DownloadConf
|
|
85
72
|
from eodag.utils import DownloadedCallback, Unpack
|
|
86
73
|
|
|
@@ -230,14 +217,14 @@ class AwsDownload(Download):
|
|
|
230
217
|
* :attr:`~eodag.config.PluginConfig.bucket_path_level` (``int``): at which level of the
|
|
231
218
|
path part of the url the bucket can be found; If no bucket_path_level is given, the bucket
|
|
232
219
|
is taken from the first element of the netloc part.
|
|
233
|
-
* :attr:`~eodag.config.PluginConfig.products` (``
|
|
220
|
+
* :attr:`~eodag.config.PluginConfig.products` (``dict[str, dict[str, Any]``): product type
|
|
234
221
|
specific config; the keys are the product types, the values are dictionaries which can contain the keys:
|
|
235
222
|
|
|
236
223
|
* **default_bucket** (``str``): bucket where the product type can be found
|
|
237
224
|
* **complementary_url_key** (``str``): keys to add additional urls
|
|
238
225
|
* **build_safe** (``bool``): if a SAFE (Standard Archive Format for Europe) product should
|
|
239
226
|
be created; used for Sentinel products; default: False
|
|
240
|
-
* **fetch_metadata** (``
|
|
227
|
+
* **fetch_metadata** (``dict[str, Any]``): config for metadata to be fetched for the SAFE product
|
|
241
228
|
|
|
242
229
|
"""
|
|
243
230
|
|
|
@@ -249,7 +236,7 @@ class AwsDownload(Download):
|
|
|
249
236
|
def download(
|
|
250
237
|
self,
|
|
251
238
|
product: EOProduct,
|
|
252
|
-
auth: Optional[Union[AuthBase,
|
|
239
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
253
240
|
progress_callback: Optional[ProgressCallback] = None,
|
|
254
241
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
255
242
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -407,7 +394,7 @@ class AwsDownload(Download):
|
|
|
407
394
|
product: EOProduct,
|
|
408
395
|
progress_callback: ProgressCallback,
|
|
409
396
|
**kwargs: Unpack[DownloadConf],
|
|
410
|
-
) ->
|
|
397
|
+
) -> tuple[Optional[str], Optional[str]]:
|
|
411
398
|
"""
|
|
412
399
|
preparation for the download:
|
|
413
400
|
- check if file was already downloaded
|
|
@@ -480,7 +467,7 @@ class AwsDownload(Download):
|
|
|
480
467
|
product: EOProduct,
|
|
481
468
|
asset_filter: Optional[str] = None,
|
|
482
469
|
ignore_assets: Optional[bool] = False,
|
|
483
|
-
) ->
|
|
470
|
+
) -> list[tuple[str, Optional[str]]]:
|
|
484
471
|
"""
|
|
485
472
|
Retrieves the bucket names and path prefixes for the assets
|
|
486
473
|
|
|
@@ -522,9 +509,9 @@ class AwsDownload(Download):
|
|
|
522
509
|
|
|
523
510
|
def _do_authentication(
|
|
524
511
|
self,
|
|
525
|
-
bucket_names_and_prefixes:
|
|
526
|
-
auth: Optional[Union[AuthBase,
|
|
527
|
-
) ->
|
|
512
|
+
bucket_names_and_prefixes: list[tuple[str, Optional[str]]],
|
|
513
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
514
|
+
) -> tuple[dict[str, Any], ResourceCollection]:
|
|
528
515
|
"""
|
|
529
516
|
authenticates with s3 and retrieves the available objects
|
|
530
517
|
raises an error when authentication is not possible
|
|
@@ -538,8 +525,8 @@ class AwsDownload(Download):
|
|
|
538
525
|
)
|
|
539
526
|
if auth is None:
|
|
540
527
|
auth = {}
|
|
541
|
-
authenticated_objects:
|
|
542
|
-
auth_error_messages:
|
|
528
|
+
authenticated_objects: dict[str, Any] = {}
|
|
529
|
+
auth_error_messages: set[str] = set()
|
|
543
530
|
for _, pack in enumerate(bucket_names_and_prefixes):
|
|
544
531
|
try:
|
|
545
532
|
bucket_name, prefix = pack
|
|
@@ -591,12 +578,12 @@ class AwsDownload(Download):
|
|
|
591
578
|
|
|
592
579
|
def _get_unique_products(
|
|
593
580
|
self,
|
|
594
|
-
bucket_names_and_prefixes:
|
|
595
|
-
authenticated_objects:
|
|
581
|
+
bucket_names_and_prefixes: list[tuple[str, Optional[str]]],
|
|
582
|
+
authenticated_objects: dict[str, Any],
|
|
596
583
|
asset_filter: Optional[str],
|
|
597
584
|
ignore_assets: bool,
|
|
598
585
|
product: EOProduct,
|
|
599
|
-
) ->
|
|
586
|
+
) -> set[Any]:
|
|
600
587
|
"""
|
|
601
588
|
retrieve unique product chunks based on authenticated objects and asset filters
|
|
602
589
|
:param bucket_names_and_prefixes: list of bucket names and corresponding path prefixes
|
|
@@ -606,7 +593,7 @@ class AwsDownload(Download):
|
|
|
606
593
|
:param product: product that shall be downloaded
|
|
607
594
|
:return: set of product chunks that can be downloaded
|
|
608
595
|
"""
|
|
609
|
-
product_chunks:
|
|
596
|
+
product_chunks: list[Any] = []
|
|
610
597
|
for bucket_name, prefix in bucket_names_and_prefixes:
|
|
611
598
|
# unauthenticated items filtered out
|
|
612
599
|
if bucket_name in authenticated_objects.keys():
|
|
@@ -637,7 +624,7 @@ class AwsDownload(Download):
|
|
|
637
624
|
|
|
638
625
|
def _raise_if_auth_error(self, exception: ClientError) -> None:
|
|
639
626
|
"""Raises an error if given exception is an authentication error"""
|
|
640
|
-
err = cast(
|
|
627
|
+
err = cast(dict[str, str], exception.response["Error"])
|
|
641
628
|
if err["Code"] in AWS_AUTH_ERROR_MESSAGES and "key" in err["Message"].lower():
|
|
642
629
|
raise AuthenticationError(
|
|
643
630
|
f"Please check your credentials for {self.provider}.",
|
|
@@ -648,7 +635,7 @@ class AwsDownload(Download):
|
|
|
648
635
|
def _stream_download_dict(
|
|
649
636
|
self,
|
|
650
637
|
product: EOProduct,
|
|
651
|
-
auth: Optional[Union[AuthBase,
|
|
638
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
652
639
|
progress_callback: Optional[ProgressCallback] = None,
|
|
653
640
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
654
641
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -754,11 +741,11 @@ class AwsDownload(Download):
|
|
|
754
741
|
|
|
755
742
|
def _stream_download(
|
|
756
743
|
self,
|
|
757
|
-
unique_product_chunks:
|
|
744
|
+
unique_product_chunks: set[Any],
|
|
758
745
|
product: EOProduct,
|
|
759
746
|
build_safe: bool,
|
|
760
747
|
progress_callback: ProgressCallback,
|
|
761
|
-
assets_values:
|
|
748
|
+
assets_values: list[dict[str, Any]],
|
|
762
749
|
) -> Iterator[Any]:
|
|
763
750
|
"""Yield product data chunks"""
|
|
764
751
|
|
|
@@ -829,7 +816,7 @@ class AwsDownload(Download):
|
|
|
829
816
|
)
|
|
830
817
|
|
|
831
818
|
def _get_commonpath(
|
|
832
|
-
self, product: EOProduct, product_chunks:
|
|
819
|
+
self, product: EOProduct, product_chunks: set[Any], build_safe: bool
|
|
833
820
|
) -> str:
|
|
834
821
|
chunk_paths = []
|
|
835
822
|
for product_chunk in product_chunks:
|
|
@@ -839,8 +826,8 @@ class AwsDownload(Download):
|
|
|
839
826
|
return os.path.commonpath(chunk_paths)
|
|
840
827
|
|
|
841
828
|
def get_rio_env(
|
|
842
|
-
self, bucket_name: str, prefix: str, auth_dict:
|
|
843
|
-
) ->
|
|
829
|
+
self, bucket_name: str, prefix: str, auth_dict: S3SessionKwargs
|
|
830
|
+
) -> dict[str, Any]:
|
|
844
831
|
"""Get rasterio environment variables needed for data access authentication.
|
|
845
832
|
|
|
846
833
|
:param bucket_name: Bucket containg objects
|
|
@@ -848,23 +835,26 @@ class AwsDownload(Download):
|
|
|
848
835
|
:param auth_dict: Dictionary containing authentication keys
|
|
849
836
|
:returns: The rasterio environement variables
|
|
850
837
|
"""
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
838
|
+
rio_env_kwargs = {}
|
|
839
|
+
if endpoint_url := getattr(self.config, "s3_endpoint", None):
|
|
840
|
+
rio_env_kwargs["endpoint_url"] = endpoint_url.split("://")[-1]
|
|
841
|
+
rio_env_kwargs |= auth_dict
|
|
842
|
+
|
|
843
|
+
if self.s3_session is None:
|
|
844
|
+
_ = self.get_authenticated_objects(bucket_name, prefix, auth_dict)
|
|
856
845
|
|
|
857
|
-
_ = self.get_authenticated_objects(bucket_name, prefix, auth_dict)
|
|
858
846
|
if self.s3_session is not None:
|
|
859
847
|
if self.requester_pays:
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
848
|
+
rio_env_kwargs["requester_pays"] = True
|
|
849
|
+
return {
|
|
850
|
+
"session": self.s3_session,
|
|
851
|
+
**rio_env_kwargs,
|
|
852
|
+
}
|
|
863
853
|
else:
|
|
864
|
-
return {"aws_unsigned": True}
|
|
854
|
+
return {"aws_unsigned": True, **rio_env_kwargs}
|
|
865
855
|
|
|
866
856
|
def get_authenticated_objects(
|
|
867
|
-
self, bucket_name: str, prefix: str, auth_dict:
|
|
857
|
+
self, bucket_name: str, prefix: str, auth_dict: S3SessionKwargs
|
|
868
858
|
) -> ResourceCollection:
|
|
869
859
|
"""Get boto3 authenticated objects for the given bucket using
|
|
870
860
|
the most adapted auth strategy.
|
|
@@ -876,8 +866,8 @@ class AwsDownload(Download):
|
|
|
876
866
|
:param auth_dict: Dictionary containing authentication keys
|
|
877
867
|
:returns: The boto3 authenticated objects
|
|
878
868
|
"""
|
|
879
|
-
auth_methods:
|
|
880
|
-
Callable[[str, str,
|
|
869
|
+
auth_methods: list[
|
|
870
|
+
Callable[[str, str, S3SessionKwargs], Optional[ResourceCollection]]
|
|
881
871
|
] = [
|
|
882
872
|
self._get_authenticated_objects_unsigned,
|
|
883
873
|
self._get_authenticated_objects_from_auth_profile,
|
|
@@ -912,7 +902,7 @@ class AwsDownload(Download):
|
|
|
912
902
|
)
|
|
913
903
|
|
|
914
904
|
def _get_authenticated_objects_unsigned(
|
|
915
|
-
self, bucket_name: str, prefix: str, auth_dict:
|
|
905
|
+
self, bucket_name: str, prefix: str, auth_dict: S3SessionKwargs
|
|
916
906
|
) -> Optional[ResourceCollection]:
|
|
917
907
|
"""Auth strategy using no-sign-request"""
|
|
918
908
|
|
|
@@ -927,7 +917,7 @@ class AwsDownload(Download):
|
|
|
927
917
|
return objects
|
|
928
918
|
|
|
929
919
|
def _get_authenticated_objects_from_auth_profile(
|
|
930
|
-
self, bucket_name: str, prefix: str, auth_dict:
|
|
920
|
+
self, bucket_name: str, prefix: str, auth_dict: S3SessionKwargs
|
|
931
921
|
) -> Optional[ResourceCollection]:
|
|
932
922
|
"""Auth strategy using RequestPayer=requester and ``aws_profile`` from provided credentials"""
|
|
933
923
|
|
|
@@ -950,21 +940,12 @@ class AwsDownload(Download):
|
|
|
950
940
|
return None
|
|
951
941
|
|
|
952
942
|
def _get_authenticated_objects_from_auth_keys(
|
|
953
|
-
self, bucket_name: str, prefix: str, auth_dict:
|
|
943
|
+
self, bucket_name: str, prefix: str, auth_dict: S3SessionKwargs
|
|
954
944
|
) -> Optional[ResourceCollection]:
|
|
955
945
|
"""Auth strategy using RequestPayer=requester and ``aws_access_key_id``/``aws_secret_access_key``
|
|
956
946
|
from provided credentials"""
|
|
957
947
|
|
|
958
948
|
if all(k in auth_dict for k in ("aws_access_key_id", "aws_secret_access_key")):
|
|
959
|
-
S3SessionKwargs = TypedDict(
|
|
960
|
-
"S3SessionKwargs",
|
|
961
|
-
{
|
|
962
|
-
"aws_access_key_id": str,
|
|
963
|
-
"aws_secret_access_key": str,
|
|
964
|
-
"aws_session_token": str,
|
|
965
|
-
},
|
|
966
|
-
total=False,
|
|
967
|
-
)
|
|
968
949
|
s3_session_kwargs: S3SessionKwargs = {
|
|
969
950
|
"aws_access_key_id": auth_dict["aws_access_key_id"],
|
|
970
951
|
"aws_secret_access_key": auth_dict["aws_secret_access_key"],
|
|
@@ -989,7 +970,7 @@ class AwsDownload(Download):
|
|
|
989
970
|
return None
|
|
990
971
|
|
|
991
972
|
def _get_authenticated_objects_from_env(
|
|
992
|
-
self, bucket_name: str, prefix: str, auth_dict:
|
|
973
|
+
self, bucket_name: str, prefix: str, auth_dict: S3SessionKwargs
|
|
993
974
|
) -> Optional[ResourceCollection]:
|
|
994
975
|
"""Auth strategy using RequestPayer=requester and current environment"""
|
|
995
976
|
|
|
@@ -1009,7 +990,7 @@ class AwsDownload(Download):
|
|
|
1009
990
|
|
|
1010
991
|
def get_product_bucket_name_and_prefix(
|
|
1011
992
|
self, product: EOProduct, url: Optional[str] = None
|
|
1012
|
-
) ->
|
|
993
|
+
) -> tuple[str, Optional[str]]:
|
|
1013
994
|
"""Extract bucket name and prefix from product URL
|
|
1014
995
|
|
|
1015
996
|
:param product: The EO product to download
|
|
@@ -1140,7 +1121,7 @@ class AwsDownload(Download):
|
|
|
1140
1121
|
s1_title_suffix: Optional[str] = None
|
|
1141
1122
|
# S2 common
|
|
1142
1123
|
if product.product_type and "S2_MSI" in product.product_type:
|
|
1143
|
-
title_search: Optional[Match[str]] = re.search(
|
|
1124
|
+
title_search: Optional[re.Match[str]] = re.search(
|
|
1144
1125
|
r"^\w+_\w+_(\w+)_(\w+)_(\w+)_(\w+)_(\w+)$",
|
|
1145
1126
|
product.properties["title"],
|
|
1146
1127
|
)
|
|
@@ -1326,13 +1307,13 @@ class AwsDownload(Download):
|
|
|
1326
1307
|
def download_all(
|
|
1327
1308
|
self,
|
|
1328
1309
|
products: SearchResult,
|
|
1329
|
-
auth: Optional[Union[AuthBase,
|
|
1310
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
1330
1311
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
1331
1312
|
progress_callback: Optional[ProgressCallback] = None,
|
|
1332
1313
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
1333
1314
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
1334
1315
|
**kwargs: Unpack[DownloadConf],
|
|
1335
|
-
) ->
|
|
1316
|
+
) -> list[str]:
|
|
1336
1317
|
"""
|
|
1337
1318
|
download_all using parent (base plugin) method
|
|
1338
1319
|
"""
|
eodag/plugins/download/base.py
CHANGED
|
@@ -26,17 +26,7 @@ import tempfile
|
|
|
26
26
|
import zipfile
|
|
27
27
|
from datetime import datetime, timedelta
|
|
28
28
|
from time import sleep
|
|
29
|
-
from typing import
|
|
30
|
-
TYPE_CHECKING,
|
|
31
|
-
Any,
|
|
32
|
-
Callable,
|
|
33
|
-
Dict,
|
|
34
|
-
List,
|
|
35
|
-
Optional,
|
|
36
|
-
Tuple,
|
|
37
|
-
TypeVar,
|
|
38
|
-
Union,
|
|
39
|
-
)
|
|
29
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
|
|
40
30
|
|
|
41
31
|
from eodag.plugins.base import PluginTopic
|
|
42
32
|
from eodag.utils import (
|
|
@@ -60,6 +50,7 @@ if TYPE_CHECKING:
|
|
|
60
50
|
from eodag.api.product import EOProduct
|
|
61
51
|
from eodag.api.search_result import SearchResult
|
|
62
52
|
from eodag.config import PluginConfig
|
|
53
|
+
from eodag.types import S3SessionKwargs
|
|
63
54
|
from eodag.types.download_args import DownloadConf
|
|
64
55
|
from eodag.utils import DownloadedCallback, Unpack
|
|
65
56
|
|
|
@@ -110,7 +101,7 @@ class Download(PluginTopic):
|
|
|
110
101
|
def download(
|
|
111
102
|
self,
|
|
112
103
|
product: EOProduct,
|
|
113
|
-
auth: Optional[Union[AuthBase,
|
|
104
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
114
105
|
progress_callback: Optional[ProgressCallback] = None,
|
|
115
106
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
116
107
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -140,7 +131,7 @@ class Download(PluginTopic):
|
|
|
140
131
|
def _stream_download_dict(
|
|
141
132
|
self,
|
|
142
133
|
product: EOProduct,
|
|
143
|
-
auth: Optional[Union[AuthBase,
|
|
134
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
144
135
|
progress_callback: Optional[ProgressCallback] = None,
|
|
145
136
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
146
137
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -170,7 +161,7 @@ class Download(PluginTopic):
|
|
|
170
161
|
product: EOProduct,
|
|
171
162
|
progress_callback: Optional[ProgressCallback] = None,
|
|
172
163
|
**kwargs: Unpack[DownloadConf],
|
|
173
|
-
) ->
|
|
164
|
+
) -> tuple[Optional[str], Optional[str]]:
|
|
174
165
|
"""Check if file has already been downloaded, and prepare product download
|
|
175
166
|
|
|
176
167
|
:param product: The EO product to download
|
|
@@ -439,13 +430,13 @@ class Download(PluginTopic):
|
|
|
439
430
|
def download_all(
|
|
440
431
|
self,
|
|
441
432
|
products: SearchResult,
|
|
442
|
-
auth: Optional[Union[AuthBase,
|
|
433
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
443
434
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
444
435
|
progress_callback: Optional[ProgressCallback] = None,
|
|
445
436
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
446
437
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
447
438
|
**kwargs: Unpack[DownloadConf],
|
|
448
|
-
) ->
|
|
439
|
+
) -> list[str]:
|
|
449
440
|
"""
|
|
450
441
|
Base download_all method.
|
|
451
442
|
|
|
@@ -474,7 +465,7 @@ class Download(PluginTopic):
|
|
|
474
465
|
# Products are going to be removed one by one from this sequence once
|
|
475
466
|
# downloaded.
|
|
476
467
|
products = products[:]
|
|
477
|
-
paths:
|
|
468
|
+
paths: list[str] = []
|
|
478
469
|
# initiate retry loop
|
|
479
470
|
start_time = datetime.now()
|
|
480
471
|
stop_time = start_time + timedelta(minutes=timeout)
|
|
@@ -15,7 +15,7 @@
|
|
|
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
|
|
18
|
+
from typing import Optional
|
|
19
19
|
|
|
20
20
|
import boto3
|
|
21
21
|
from botocore.exceptions import ClientError
|
|
@@ -73,7 +73,7 @@ class CreodiasS3Download(AwsDownload):
|
|
|
73
73
|
product: EOProduct,
|
|
74
74
|
asset_filter: Optional[str] = None,
|
|
75
75
|
ignore_assets: Optional[bool] = False,
|
|
76
|
-
) ->
|
|
76
|
+
) -> list[tuple[str, Optional[str]]]:
|
|
77
77
|
"""
|
|
78
78
|
Retrieves the bucket names and path prefixes for the assets
|
|
79
79
|
|
eodag/plugins/download/http.py
CHANGED
|
@@ -28,17 +28,7 @@ from email.message import Message
|
|
|
28
28
|
from itertools import chain
|
|
29
29
|
from json import JSONDecodeError
|
|
30
30
|
from pathlib import Path
|
|
31
|
-
from typing import
|
|
32
|
-
TYPE_CHECKING,
|
|
33
|
-
Any,
|
|
34
|
-
Dict,
|
|
35
|
-
Iterator,
|
|
36
|
-
List,
|
|
37
|
-
Optional,
|
|
38
|
-
TypedDict,
|
|
39
|
-
Union,
|
|
40
|
-
cast,
|
|
41
|
-
)
|
|
31
|
+
from typing import TYPE_CHECKING, Any, Iterator, Optional, TypedDict, Union, cast
|
|
42
32
|
from urllib.parse import parse_qs, urlparse
|
|
43
33
|
|
|
44
34
|
import geojson
|
|
@@ -72,6 +62,7 @@ from eodag.utils import (
|
|
|
72
62
|
guess_file_type,
|
|
73
63
|
parse_header,
|
|
74
64
|
path_to_uri,
|
|
65
|
+
rename_with_version,
|
|
75
66
|
sanitize,
|
|
76
67
|
string_to_jsonpath,
|
|
77
68
|
uri_to_path,
|
|
@@ -91,6 +82,7 @@ if TYPE_CHECKING:
|
|
|
91
82
|
from eodag.api.product import Asset, EOProduct # type: ignore
|
|
92
83
|
from eodag.api.search_result import SearchResult
|
|
93
84
|
from eodag.config import PluginConfig
|
|
85
|
+
from eodag.types import S3SessionKwargs
|
|
94
86
|
from eodag.types.download_args import DownloadConf
|
|
95
87
|
from eodag.utils import DownloadedCallback, Unpack
|
|
96
88
|
|
|
@@ -111,7 +103,7 @@ class HTTPDownload(Download):
|
|
|
111
103
|
extracted; default: ``True``
|
|
112
104
|
* :attr:`~eodag.config.PluginConfig.auth_error_code` (``int``): which error code is returned in case of an
|
|
113
105
|
authentication error
|
|
114
|
-
* :attr:`~eodag.config.PluginConfig.dl_url_params` (``
|
|
106
|
+
* :attr:`~eodag.config.PluginConfig.dl_url_params` (``dict[str, Any]``): parameters to be
|
|
115
107
|
added to the query params of the request
|
|
116
108
|
* :attr:`~eodag.config.PluginConfig.archive_depth` (``int``): level in extracted path tree where to find data;
|
|
117
109
|
default: ``1``
|
|
@@ -130,7 +122,7 @@ class HTTPDownload(Download):
|
|
|
130
122
|
the search plugin used for the provider; default: ``False``
|
|
131
123
|
* :attr:`~eodag.config.PluginConfig.order_method` (``str``): HTTP request method for the order request (``GET``
|
|
132
124
|
or ``POST``); default: ``GET``
|
|
133
|
-
* :attr:`~eodag.config.PluginConfig.order_headers` (``[
|
|
125
|
+
* :attr:`~eodag.config.PluginConfig.order_headers` (``[dict[str, str]]``): headers to be added to the order
|
|
134
126
|
request
|
|
135
127
|
* :attr:`~eodag.config.PluginConfig.order_on_response` (:class:`~eodag.config.PluginConfig.OrderOnResponse`):
|
|
136
128
|
a typed dictionary containing the key ``metadata_mapping`` which can be used to add new product properties
|
|
@@ -138,7 +130,7 @@ class HTTPDownload(Download):
|
|
|
138
130
|
* :attr:`~eodag.config.PluginConfig.order_status` (:class:`~eodag.config.PluginConfig.OrderStatus`):
|
|
139
131
|
configuration to handle the order status; contains information which method to use, how the response data is
|
|
140
132
|
interpreted, which status corresponds to success, ordered and error and what should be done on success.
|
|
141
|
-
* :attr:`~eodag.config.PluginConfig.products` (``
|
|
133
|
+
* :attr:`~eodag.config.PluginConfig.products` (``dict[str, dict[str, Any]``): product type specific config; the
|
|
142
134
|
keys are the product types, the values are dictionaries which can contain the key
|
|
143
135
|
:attr:`~eodag.config.PluginConfig.extract` to overwrite the provider config for a specific product type
|
|
144
136
|
|
|
@@ -152,7 +144,7 @@ class HTTPDownload(Download):
|
|
|
152
144
|
product: EOProduct,
|
|
153
145
|
auth: Optional[AuthBase] = None,
|
|
154
146
|
**kwargs: Unpack[DownloadConf],
|
|
155
|
-
) -> Optional[
|
|
147
|
+
) -> Optional[dict[str, Any]]:
|
|
156
148
|
"""Send product order request.
|
|
157
149
|
|
|
158
150
|
It will be executed once before the download retry loop, if the product is OFFLINE
|
|
@@ -184,7 +176,7 @@ class HTTPDownload(Download):
|
|
|
184
176
|
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
185
177
|
timeout = getattr(self.config, "timeout", HTTP_REQ_TIMEOUT)
|
|
186
178
|
OrderKwargs = TypedDict(
|
|
187
|
-
"OrderKwargs", {"json":
|
|
179
|
+
"OrderKwargs", {"json": dict[str, Union[Any, list[str]]]}, total=False
|
|
188
180
|
)
|
|
189
181
|
order_kwargs: OrderKwargs = {}
|
|
190
182
|
if order_method == "POST":
|
|
@@ -236,7 +228,7 @@ class HTTPDownload(Download):
|
|
|
236
228
|
|
|
237
229
|
def order_response_process(
|
|
238
230
|
self, response: Response, product: EOProduct
|
|
239
|
-
) -> Optional[
|
|
231
|
+
) -> Optional[dict[str, Any]]:
|
|
240
232
|
"""Process order response
|
|
241
233
|
|
|
242
234
|
:param response: The order response
|
|
@@ -300,7 +292,7 @@ class HTTPDownload(Download):
|
|
|
300
292
|
def _request(
|
|
301
293
|
url: str,
|
|
302
294
|
method: str = "GET",
|
|
303
|
-
headers: Optional[
|
|
295
|
+
headers: Optional[dict[str, Any]] = None,
|
|
304
296
|
json: Optional[Any] = None,
|
|
305
297
|
timeout: int = HTTP_REQ_TIMEOUT,
|
|
306
298
|
) -> Response:
|
|
@@ -336,7 +328,7 @@ class HTTPDownload(Download):
|
|
|
336
328
|
except requests.exceptions.Timeout as exc:
|
|
337
329
|
raise TimeOutError(exc, timeout=timeout) from exc
|
|
338
330
|
|
|
339
|
-
status_request:
|
|
331
|
+
status_request: dict[str, Any] = status_config.get("request", {})
|
|
340
332
|
status_request_method = str(status_request.get("method", "GET")).upper()
|
|
341
333
|
|
|
342
334
|
if status_request_method == "POST":
|
|
@@ -353,8 +345,8 @@ class HTTPDownload(Download):
|
|
|
353
345
|
|
|
354
346
|
# check header for success before full status request
|
|
355
347
|
skip_parsing_status_response = False
|
|
356
|
-
status_dict:
|
|
357
|
-
config_on_success:
|
|
348
|
+
status_dict: dict[str, Any] = {}
|
|
349
|
+
config_on_success: dict[str, Any] = status_config.get("on_success", {})
|
|
358
350
|
on_success_mm = config_on_success.get("metadata_mapping", {})
|
|
359
351
|
|
|
360
352
|
status_response_content_needed = (
|
|
@@ -438,13 +430,13 @@ class HTTPDownload(Download):
|
|
|
438
430
|
product.properties["orderStatus"] = status_dict.get("status")
|
|
439
431
|
|
|
440
432
|
# handle status error
|
|
441
|
-
errors:
|
|
433
|
+
errors: dict[str, Any] = status_config.get("error", {})
|
|
442
434
|
if errors and errors.items() <= status_dict.items():
|
|
443
435
|
raise DownloadError(
|
|
444
436
|
f"Provider {product.provider} returned: {status_dict.get('error_message', status_message)}"
|
|
445
437
|
)
|
|
446
438
|
|
|
447
|
-
success_status:
|
|
439
|
+
success_status: dict[str, Any] = status_config.get("success", {}).get("status")
|
|
448
440
|
# if not success
|
|
449
441
|
if (success_status and success_status != status_dict.get("status")) or (
|
|
450
442
|
success_code and success_code != response.status_code
|
|
@@ -562,7 +554,7 @@ class HTTPDownload(Download):
|
|
|
562
554
|
def download(
|
|
563
555
|
self,
|
|
564
556
|
product: EOProduct,
|
|
565
|
-
auth: Optional[Union[AuthBase,
|
|
557
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
566
558
|
progress_callback: Optional[ProgressCallback] = None,
|
|
567
559
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
568
560
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -666,6 +658,8 @@ class HTTPDownload(Download):
|
|
|
666
658
|
os.path.dirname(path),
|
|
667
659
|
sanitize(product.properties["title"]),
|
|
668
660
|
)
|
|
661
|
+
if os.path.isfile(new_fs_path):
|
|
662
|
+
rename_with_version(new_fs_path)
|
|
669
663
|
if not os.path.isdir(new_fs_path):
|
|
670
664
|
os.makedirs(new_fs_path)
|
|
671
665
|
shutil.move(path, new_fs_path)
|
|
@@ -719,7 +713,7 @@ class HTTPDownload(Download):
|
|
|
719
713
|
def _stream_download_dict(
|
|
720
714
|
self,
|
|
721
715
|
product: EOProduct,
|
|
722
|
-
auth: Optional[Union[AuthBase,
|
|
716
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
723
717
|
progress_callback: Optional[ProgressCallback] = None,
|
|
724
718
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
725
719
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -778,13 +772,17 @@ class HTTPDownload(Download):
|
|
|
778
772
|
)
|
|
779
773
|
|
|
780
774
|
else:
|
|
775
|
+
# get first chunk to check if it does not contain an error (if it does, that error will be raised)
|
|
776
|
+
first_chunks_tuple = next(chunks_tuples)
|
|
781
777
|
outputs_filename = (
|
|
782
778
|
sanitize(product.properties["title"])
|
|
783
779
|
if "title" in product.properties
|
|
784
780
|
else sanitize(product.properties.get("id", "download"))
|
|
785
781
|
)
|
|
786
782
|
return StreamResponse(
|
|
787
|
-
content=stream_zip(
|
|
783
|
+
content=stream_zip(
|
|
784
|
+
chain(iter([first_chunks_tuple]), chunks_tuples)
|
|
785
|
+
),
|
|
788
786
|
media_type="application/zip",
|
|
789
787
|
headers={
|
|
790
788
|
"content-disposition": f"attachment; filename={outputs_filename}.zip",
|
|
@@ -888,7 +886,7 @@ class HTTPDownload(Download):
|
|
|
888
886
|
def order(
|
|
889
887
|
self,
|
|
890
888
|
product: EOProduct,
|
|
891
|
-
auth: Optional[Union[AuthBase,
|
|
889
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
892
890
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
893
891
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
894
892
|
) -> None:
|
|
@@ -951,7 +949,7 @@ class HTTPDownload(Download):
|
|
|
951
949
|
if not query_dict and parts.query:
|
|
952
950
|
query_dict = geojson.loads(parts.query)
|
|
953
951
|
req_url = parts._replace(query="").geturl()
|
|
954
|
-
req_kwargs:
|
|
952
|
+
req_kwargs: dict[str, Any] = {"json": query_dict} if query_dict else {}
|
|
955
953
|
else:
|
|
956
954
|
req_url = url
|
|
957
955
|
req_kwargs = {}
|
|
@@ -1019,7 +1017,7 @@ class HTTPDownload(Download):
|
|
|
1019
1017
|
product: EOProduct,
|
|
1020
1018
|
auth: Optional[AuthBase] = None,
|
|
1021
1019
|
progress_callback: Optional[ProgressCallback] = None,
|
|
1022
|
-
assets_values:
|
|
1020
|
+
assets_values: list[Asset] = [],
|
|
1023
1021
|
**kwargs: Unpack[DownloadConf],
|
|
1024
1022
|
) -> Iterator[Any]:
|
|
1025
1023
|
if progress_callback is None:
|
|
@@ -1289,9 +1287,9 @@ class HTTPDownload(Download):
|
|
|
1289
1287
|
|
|
1290
1288
|
def _get_asset_sizes(
|
|
1291
1289
|
self,
|
|
1292
|
-
assets_values:
|
|
1290
|
+
assets_values: list[Asset],
|
|
1293
1291
|
auth: Optional[AuthBase],
|
|
1294
|
-
params: Optional[
|
|
1292
|
+
params: Optional[dict[str, str]],
|
|
1295
1293
|
zipped: bool = False,
|
|
1296
1294
|
) -> int:
|
|
1297
1295
|
total_size = 0
|
|
@@ -1364,7 +1362,7 @@ class HTTPDownload(Download):
|
|
|
1364
1362
|
def download_all(
|
|
1365
1363
|
self,
|
|
1366
1364
|
products: SearchResult,
|
|
1367
|
-
auth: Optional[Union[AuthBase,
|
|
1365
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
1368
1366
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
1369
1367
|
progress_callback: Optional[ProgressCallback] = None,
|
|
1370
1368
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|