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.
- eodag/__init__.py +6 -8
- eodag/api/core.py +295 -287
- eodag/api/product/__init__.py +10 -4
- eodag/api/product/_assets.py +2 -14
- eodag/api/product/_product.py +16 -30
- eodag/api/product/drivers/__init__.py +7 -2
- eodag/api/product/drivers/base.py +0 -3
- eodag/api/product/metadata_mapping.py +12 -31
- eodag/api/search_result.py +33 -12
- eodag/cli.py +35 -19
- eodag/config.py +455 -155
- eodag/plugins/apis/base.py +13 -7
- eodag/plugins/apis/ecmwf.py +16 -7
- eodag/plugins/apis/usgs.py +68 -16
- eodag/plugins/authentication/aws_auth.py +25 -7
- eodag/plugins/authentication/base.py +10 -1
- eodag/plugins/authentication/generic.py +14 -3
- eodag/plugins/authentication/header.py +12 -4
- eodag/plugins/authentication/keycloak.py +41 -22
- eodag/plugins/authentication/oauth.py +11 -1
- eodag/plugins/authentication/openid_connect.py +183 -167
- eodag/plugins/authentication/qsauth.py +12 -4
- eodag/plugins/authentication/sas_auth.py +19 -2
- eodag/plugins/authentication/token.py +59 -11
- eodag/plugins/authentication/token_exchange.py +19 -19
- eodag/plugins/crunch/base.py +7 -2
- eodag/plugins/crunch/filter_date.py +8 -11
- eodag/plugins/crunch/filter_latest_intersect.py +5 -7
- eodag/plugins/crunch/filter_latest_tpl_name.py +2 -5
- eodag/plugins/crunch/filter_overlap.py +9 -15
- eodag/plugins/crunch/filter_property.py +9 -14
- eodag/plugins/download/aws.py +84 -99
- eodag/plugins/download/base.py +36 -77
- eodag/plugins/download/creodias_s3.py +11 -2
- eodag/plugins/download/http.py +134 -109
- eodag/plugins/download/s3rest.py +37 -43
- eodag/plugins/manager.py +173 -41
- eodag/plugins/search/__init__.py +9 -9
- eodag/plugins/search/base.py +35 -35
- eodag/plugins/search/build_search_result.py +55 -64
- eodag/plugins/search/cop_marine.py +113 -32
- eodag/plugins/search/creodias_s3.py +20 -8
- eodag/plugins/search/csw.py +41 -1
- eodag/plugins/search/data_request_search.py +119 -14
- eodag/plugins/search/qssearch.py +619 -197
- eodag/plugins/search/static_stac_search.py +25 -23
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +211 -56
- eodag/resources/providers.yml +1762 -1809
- eodag/resources/stac.yml +3 -163
- eodag/resources/user_conf_template.yml +134 -119
- eodag/rest/config.py +1 -2
- eodag/rest/constants.py +0 -1
- eodag/rest/core.py +70 -92
- eodag/rest/errors.py +181 -0
- eodag/rest/server.py +24 -330
- eodag/rest/stac.py +105 -630
- eodag/rest/types/eodag_search.py +17 -15
- eodag/rest/types/queryables.py +5 -14
- eodag/rest/types/stac_search.py +18 -13
- eodag/rest/utils/rfc3339.py +0 -1
- eodag/types/__init__.py +24 -6
- eodag/types/download_args.py +14 -5
- eodag/types/queryables.py +1 -2
- eodag/types/search_args.py +10 -11
- eodag/types/whoosh.py +0 -2
- eodag/utils/__init__.py +97 -136
- eodag/utils/constraints.py +0 -8
- eodag/utils/exceptions.py +23 -9
- eodag/utils/import_system.py +0 -4
- eodag/utils/logging.py +37 -80
- eodag/utils/notebook.py +4 -4
- eodag/utils/requests.py +13 -23
- eodag/utils/rest.py +0 -4
- eodag/utils/stac_reader.py +3 -15
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/METADATA +41 -24
- eodag-3.0.1.dist-info/RECORD +109 -0
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/WHEEL +1 -1
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/entry_points.txt +1 -0
- eodag/resources/constraints/climate-dt.json +0 -13
- eodag/resources/constraints/extremes-dt.json +0 -8
- eodag-3.0.0b2.dist-info/RECORD +0 -110
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/LICENSE +0 -0
- {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/top_level.txt +0 -0
eodag/plugins/download/s3rest.py
CHANGED
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
22
|
import os.path
|
|
23
|
-
from typing import TYPE_CHECKING, Dict, Optional, Union
|
|
23
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Union
|
|
24
24
|
from xml.dom import minidom
|
|
25
25
|
from xml.parsers.expat import ExpatError
|
|
26
26
|
|
|
@@ -62,26 +62,28 @@ logger = logging.getLogger("eodag.download.s3rest")
|
|
|
62
62
|
|
|
63
63
|
class S3RestDownload(Download):
|
|
64
64
|
"""Http download on S3-like object storage location
|
|
65
|
-
|
|
65
|
+
|
|
66
|
+
For example using Mundi REST API (free account)
|
|
66
67
|
https://mundiwebservices.com/keystoneapi/uploads/documents/CWS-DATA-MUT-087-EN-Mundi_Download_v1.1.pdf#page=13
|
|
67
68
|
|
|
68
|
-
Re-use AwsDownload bucket some handling methods
|
|
69
|
+
Re-use AwsDownload bucket and some handling methods
|
|
69
70
|
|
|
70
71
|
:param provider: provider name
|
|
71
|
-
:type provider: str
|
|
72
72
|
:param config: Download plugin configuration:
|
|
73
73
|
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
* :attr:`~eodag.config.PluginConfig.base_uri` (``str``) (**mandatory**): default endpoint url
|
|
75
|
+
* :attr:`~eodag.config.PluginConfig.extract` (``bool``): extract downloaded archive or not
|
|
76
|
+
* :attr:`~eodag.config.PluginConfig.auth_error_code` (``int``): authentication error code
|
|
77
|
+
* :attr:`~eodag.config.PluginConfig.bucket_path_level` (``int``): bucket location index in ``path.split('/')``
|
|
78
|
+
* :attr:`~eodag.config.PluginConfig.order_enabled` (``bool``): whether order is enabled
|
|
79
|
+
or not if product is `OFFLINE`
|
|
80
|
+
* :attr:`~eodag.config.PluginConfig.order_method` (``str``) HTTP request method, ``GET`` (default) or ``POST``
|
|
81
|
+
* :attr:`~eodag.config.PluginConfig.order_headers` (``[Dict[str, str]]``): order request headers
|
|
82
|
+
* :attr:`~eodag.config.PluginConfig.order_on_response` (:class:`~eodag.config.PluginConfig.OrderOnResponse`):
|
|
83
|
+
a typed dictionary containing the key :attr:`~eodag.config.PluginConfig.OrderOnResponse.metadata_mapping`
|
|
84
|
+
which can be used to add new product properties based on the data in response to the order request
|
|
85
|
+
* :attr:`~eodag.config.PluginConfig.order_status` (:class:`~eodag.config.PluginConfig.OrderStatus`):
|
|
86
|
+
Order status handling
|
|
85
87
|
"""
|
|
86
88
|
|
|
87
89
|
def __init__(self, provider: str, config: PluginConfig) -> None:
|
|
@@ -100,22 +102,17 @@ class S3RestDownload(Download):
|
|
|
100
102
|
"""Download method for S3 REST API.
|
|
101
103
|
|
|
102
104
|
:param product: The EO product to download
|
|
103
|
-
:type product: :class:`~eodag.api.product._product.EOProduct`
|
|
104
105
|
:param auth: (optional) authenticated object
|
|
105
|
-
:type auth: Optional[Union[AuthBase, Dict[str, str]]]
|
|
106
106
|
:param progress_callback: (optional) A method or a callable object
|
|
107
107
|
which takes a current size and a maximum
|
|
108
108
|
size as inputs and handle progress bar
|
|
109
109
|
creation and update to give the user a
|
|
110
110
|
feedback on the download progress
|
|
111
|
-
:
|
|
112
|
-
:param kwargs: `outputs_prefix` (str), `extract` (bool), `delete_archive` (bool)
|
|
111
|
+
:param kwargs: `output_dir` (str), `extract` (bool), `delete_archive` (bool)
|
|
113
112
|
and `dl_url_params` (dict) can be provided as additional kwargs
|
|
114
113
|
and will override any other values defined in a configuration
|
|
115
114
|
file or with environment variables.
|
|
116
|
-
:type kwargs: Union[str, bool, dict]
|
|
117
115
|
:returns: The absolute path to the downloaded product in the local filesystem
|
|
118
|
-
:rtype: str
|
|
119
116
|
"""
|
|
120
117
|
if auth is not None and not isinstance(auth, AuthBase):
|
|
121
118
|
raise MisconfiguredError(f"Incompatible auth plugin: {type(auth)}")
|
|
@@ -133,7 +130,7 @@ class S3RestDownload(Download):
|
|
|
133
130
|
and "storageStatus" in product.properties
|
|
134
131
|
and product.properties["storageStatus"] != ONLINE_STATUS
|
|
135
132
|
):
|
|
136
|
-
self.http_download_plugin.
|
|
133
|
+
self.http_download_plugin.order_download(product=product, auth=auth)
|
|
137
134
|
|
|
138
135
|
@self._download_retry(product, wait, timeout)
|
|
139
136
|
def download_request(
|
|
@@ -145,7 +142,7 @@ class S3RestDownload(Download):
|
|
|
145
142
|
):
|
|
146
143
|
# check order status
|
|
147
144
|
if product.properties.get("orderStatusLink", None):
|
|
148
|
-
self.http_download_plugin.
|
|
145
|
+
self.http_download_plugin.order_download_status(
|
|
149
146
|
product=product, auth=auth
|
|
150
147
|
)
|
|
151
148
|
|
|
@@ -153,6 +150,8 @@ class S3RestDownload(Download):
|
|
|
153
150
|
bucket_name, prefix = get_bucket_name_and_prefix(
|
|
154
151
|
url=product.location, bucket_path_level=self.config.bucket_path_level
|
|
155
152
|
)
|
|
153
|
+
if prefix is None:
|
|
154
|
+
raise DownloadError(f"Could not extract prefix from {product.location}")
|
|
156
155
|
|
|
157
156
|
if (
|
|
158
157
|
bucket_name is None
|
|
@@ -195,12 +194,9 @@ class S3RestDownload(Download):
|
|
|
195
194
|
auth_errors = [auth_errors]
|
|
196
195
|
if err.response and err.response.status_code in auth_errors:
|
|
197
196
|
raise AuthenticationError(
|
|
198
|
-
"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
err.response.text.strip(),
|
|
202
|
-
self.provider,
|
|
203
|
-
)
|
|
197
|
+
f"Please check your credentials for {self.provider}.",
|
|
198
|
+
f"HTTP Error {err.response.status_code} returned.",
|
|
199
|
+
err.response.text.strip(),
|
|
204
200
|
)
|
|
205
201
|
# product not available
|
|
206
202
|
elif (
|
|
@@ -231,7 +227,7 @@ class S3RestDownload(Download):
|
|
|
231
227
|
self.__class__.__name__,
|
|
232
228
|
bucket_contents.text,
|
|
233
229
|
)
|
|
234
|
-
raise RequestError(
|
|
230
|
+
raise RequestError.from_error(err) from err
|
|
235
231
|
try:
|
|
236
232
|
xmldoc = minidom.parseString(bucket_contents.text)
|
|
237
233
|
except ExpatError as err:
|
|
@@ -243,14 +239,12 @@ class S3RestDownload(Download):
|
|
|
243
239
|
logger.warning("Could not load any content from %s", nodes_list_url)
|
|
244
240
|
|
|
245
241
|
# destination product path
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
)
|
|
249
|
-
abs_outputs_prefix = os.path.abspath(outputs_prefix)
|
|
250
|
-
product_local_path = os.path.join(abs_outputs_prefix, prefix.split("/")[-1])
|
|
242
|
+
output_dir = kwargs.pop("output_dir", None) or self.config.output_dir
|
|
243
|
+
abs_output_dir = os.path.abspath(output_dir)
|
|
244
|
+
product_local_path = os.path.join(abs_output_dir, prefix.split("/")[-1])
|
|
251
245
|
|
|
252
246
|
# .downloaded cache record directory
|
|
253
|
-
download_records_dir = os.path.join(
|
|
247
|
+
download_records_dir = os.path.join(abs_output_dir, ".downloaded")
|
|
254
248
|
try:
|
|
255
249
|
os.makedirs(download_records_dir)
|
|
256
250
|
except OSError as exc:
|
|
@@ -278,18 +272,18 @@ class S3RestDownload(Download):
|
|
|
278
272
|
os.remove(record_filename)
|
|
279
273
|
|
|
280
274
|
# total size for progress_callback
|
|
281
|
-
|
|
282
|
-
[
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
)
|
|
275
|
+
size_list: List[int] = [
|
|
276
|
+
int(node.firstChild.nodeValue) # type: ignore[attr-defined]
|
|
277
|
+
for node in xmldoc.getElementsByTagName("Size")
|
|
278
|
+
if node.firstChild is not None
|
|
279
|
+
]
|
|
280
|
+
total_size = sum(size_list)
|
|
287
281
|
progress_callback.reset(total=total_size)
|
|
288
282
|
|
|
289
283
|
# download each node key
|
|
290
284
|
for node_xml in nodes_xml_list:
|
|
291
285
|
node_key = unquote(
|
|
292
|
-
node_xml.getElementsByTagName("Key")[0].firstChild.nodeValue
|
|
286
|
+
node_xml.getElementsByTagName("Key")[0].firstChild.nodeValue # type: ignore[union-attr]
|
|
293
287
|
)
|
|
294
288
|
# As "Key", "Size" and "ETag" (md5 hash) can also be retrieved from node_xml
|
|
295
289
|
node_url = urljoin(bucket_url.strip("/") + "/", node_key.strip("/"))
|
eodag/plugins/manager.py
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
|
+
import re
|
|
21
22
|
from operator import attrgetter
|
|
22
23
|
from pathlib import Path
|
|
23
24
|
from typing import (
|
|
@@ -35,17 +36,28 @@ from typing import (
|
|
|
35
36
|
|
|
36
37
|
import pkg_resources
|
|
37
38
|
|
|
38
|
-
from eodag.config import
|
|
39
|
+
from eodag.config import (
|
|
40
|
+
AUTH_TOPIC_KEYS,
|
|
41
|
+
PLUGINS_TOPICS_KEYS,
|
|
42
|
+
load_config,
|
|
43
|
+
merge_configs,
|
|
44
|
+
)
|
|
39
45
|
from eodag.plugins.apis.base import Api
|
|
40
46
|
from eodag.plugins.authentication.base import Authentication
|
|
41
47
|
from eodag.plugins.base import EODAGPluginMount
|
|
42
48
|
from eodag.plugins.crunch.base import Crunch
|
|
43
49
|
from eodag.plugins.download.base import Download
|
|
44
50
|
from eodag.plugins.search.base import Search
|
|
45
|
-
from eodag.utils import GENERIC_PRODUCT_TYPE
|
|
46
|
-
from eodag.utils.exceptions import
|
|
51
|
+
from eodag.utils import GENERIC_PRODUCT_TYPE, deepcopy, dict_md5sum
|
|
52
|
+
from eodag.utils.exceptions import (
|
|
53
|
+
AuthenticationError,
|
|
54
|
+
MisconfiguredError,
|
|
55
|
+
UnsupportedProvider,
|
|
56
|
+
)
|
|
47
57
|
|
|
48
58
|
if TYPE_CHECKING:
|
|
59
|
+
from requests.auth import AuthBase
|
|
60
|
+
|
|
49
61
|
from eodag.api.product import EOProduct
|
|
50
62
|
from eodag.config import PluginConfig, ProviderConfig
|
|
51
63
|
from eodag.plugins.base import PluginTopic
|
|
@@ -68,10 +80,9 @@ class PluginManager:
|
|
|
68
80
|
|
|
69
81
|
:param providers_config: The configuration with all information about the providers
|
|
70
82
|
supported by ``eodag``
|
|
71
|
-
:type providers_config: dict[str, :class:`~eodag.config.ProviderConfig`]
|
|
72
83
|
"""
|
|
73
84
|
|
|
74
|
-
supported_topics =
|
|
85
|
+
supported_topics = set(PLUGINS_TOPICS_KEYS)
|
|
75
86
|
|
|
76
87
|
product_type_to_provider_config_map: Dict[str, List[ProviderConfig]]
|
|
77
88
|
|
|
@@ -111,7 +122,11 @@ class PluginManager:
|
|
|
111
122
|
"Check that the plugin module (%s) is importable",
|
|
112
123
|
entry_point.module_name,
|
|
113
124
|
)
|
|
114
|
-
if
|
|
125
|
+
if (
|
|
126
|
+
entry_point.dist
|
|
127
|
+
and entry_point.dist.key != "eodag"
|
|
128
|
+
and entry_point.dist.location is not None
|
|
129
|
+
):
|
|
115
130
|
# use plugin providers if any
|
|
116
131
|
plugin_providers_config_path = [
|
|
117
132
|
str(x)
|
|
@@ -136,7 +151,7 @@ class PluginManager:
|
|
|
136
151
|
self.providers_config = providers_config
|
|
137
152
|
|
|
138
153
|
self.build_product_type_to_provider_config_map()
|
|
139
|
-
self._built_plugins_cache: Dict[Tuple[str, str], Any] = {}
|
|
154
|
+
self._built_plugins_cache: Dict[Tuple[str, str, str], Any] = {}
|
|
140
155
|
|
|
141
156
|
def build_product_type_to_provider_config_map(self) -> None:
|
|
142
157
|
"""Build mapping conf between product types and providers"""
|
|
@@ -172,13 +187,10 @@ class PluginManager:
|
|
|
172
187
|
|
|
173
188
|
:param product_type: (optional) The product type that the constructed plugins
|
|
174
189
|
must support
|
|
175
|
-
:type product_type: str
|
|
176
190
|
:param provider: (optional) The provider or the provider group on which to get
|
|
177
191
|
the search plugins
|
|
178
|
-
:type provider: str
|
|
179
192
|
:returns: All the plugins supporting the product type, one by one (a generator
|
|
180
193
|
object)
|
|
181
|
-
:rtype: types.GeneratorType(:class:`~eodag.plugins.search.Search`
|
|
182
194
|
or :class:`~eodag.plugins.download.Api`)
|
|
183
195
|
:raises: :class:`~eodag.utils.exceptions.UnsupportedProvider`
|
|
184
196
|
"""
|
|
@@ -230,9 +242,7 @@ class PluginManager:
|
|
|
230
242
|
product.
|
|
231
243
|
|
|
232
244
|
:param product: The product to get a download plugin for
|
|
233
|
-
:type product: :class:`~eodag.api.product._product.EOProduct`
|
|
234
245
|
:returns: The download plugin capable of downloading the product
|
|
235
|
-
:rtype: :class:`~eodag.plugins.download.Download` or :class:`~eodag.plugins.download.Api`
|
|
236
246
|
"""
|
|
237
247
|
plugin_conf = self.providers_config[product.provider]
|
|
238
248
|
if download := getattr(plugin_conf, "download", None):
|
|
@@ -251,27 +261,146 @@ class PluginManager:
|
|
|
251
261
|
)
|
|
252
262
|
return plugin
|
|
253
263
|
|
|
254
|
-
def get_auth_plugin(
|
|
264
|
+
def get_auth_plugin(
|
|
265
|
+
self, associated_plugin: PluginTopic, product: Optional[EOProduct] = None
|
|
266
|
+
) -> Optional[Authentication]:
|
|
267
|
+
"""Build and return the authentication plugin associated to the given
|
|
268
|
+
search/download plugin
|
|
269
|
+
|
|
270
|
+
.. versionchanged:: v3.0.0
|
|
271
|
+
``get_auth_plugin()`` now needs ``associated_plugin`` instead of ``provider``
|
|
272
|
+
as argument.
|
|
273
|
+
|
|
274
|
+
:param associated_plugin: The search/download plugin to which the authentication
|
|
275
|
+
plugin is linked
|
|
276
|
+
:param product: The product to download. ``None`` for search authentication
|
|
277
|
+
:returns: The Authentication plugin
|
|
278
|
+
"""
|
|
279
|
+
# matching url from product to download
|
|
280
|
+
if product is not None and len(product.assets) > 0:
|
|
281
|
+
matching_url = next(iter(product.assets.values()))["href"]
|
|
282
|
+
elif product is not None:
|
|
283
|
+
matching_url = product.properties.get(
|
|
284
|
+
"downloadLink"
|
|
285
|
+
) or product.properties.get("orderLink")
|
|
286
|
+
else:
|
|
287
|
+
# search auth
|
|
288
|
+
matching_url = getattr(associated_plugin.config, "api_endpoint", None)
|
|
289
|
+
|
|
290
|
+
try:
|
|
291
|
+
auth_plugin = next(
|
|
292
|
+
self.get_auth_plugins(
|
|
293
|
+
associated_plugin.provider,
|
|
294
|
+
matching_url=matching_url,
|
|
295
|
+
matching_conf=associated_plugin.config,
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
except StopIteration:
|
|
299
|
+
auth_plugin = None
|
|
300
|
+
return auth_plugin
|
|
301
|
+
|
|
302
|
+
def get_auth_plugins(
|
|
303
|
+
self,
|
|
304
|
+
provider: str,
|
|
305
|
+
matching_url: Optional[str] = None,
|
|
306
|
+
matching_conf: Optional[PluginConfig] = None,
|
|
307
|
+
) -> Iterator[Authentication]:
|
|
255
308
|
"""Build and return the authentication plugin for the given product_type and
|
|
256
309
|
provider
|
|
257
310
|
|
|
258
311
|
:param provider: The provider for which to get the authentication plugin
|
|
259
|
-
:
|
|
260
|
-
:
|
|
261
|
-
:
|
|
312
|
+
:param matching_url: url to compare with plugin matching_url pattern
|
|
313
|
+
:param matching_conf: configuration to compare with plugin matching_conf
|
|
314
|
+
:returns: All the Authentication plugins for the given criteria
|
|
262
315
|
"""
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
316
|
+
auth_conf: Optional[PluginConfig] = None
|
|
317
|
+
|
|
318
|
+
def _is_auth_plugin_matching(
|
|
319
|
+
auth_conf: PluginConfig,
|
|
320
|
+
matching_url: Optional[str],
|
|
321
|
+
matching_conf: Optional[PluginConfig],
|
|
322
|
+
) -> bool:
|
|
323
|
+
plugin_matching_conf = getattr(auth_conf, "matching_conf", {})
|
|
324
|
+
if matching_conf:
|
|
325
|
+
if (
|
|
326
|
+
plugin_matching_conf
|
|
327
|
+
and matching_conf.__dict__.items() >= plugin_matching_conf.items()
|
|
328
|
+
):
|
|
329
|
+
# conf matches
|
|
330
|
+
return True
|
|
331
|
+
plugin_matching_url = getattr(auth_conf, "matching_url", None)
|
|
332
|
+
if matching_url:
|
|
333
|
+
if plugin_matching_url and re.match(
|
|
334
|
+
rf"{plugin_matching_url}", matching_url
|
|
335
|
+
):
|
|
336
|
+
# url matches
|
|
337
|
+
return True
|
|
338
|
+
# no match
|
|
339
|
+
return False
|
|
340
|
+
|
|
341
|
+
# providers configs with given provider at first
|
|
342
|
+
sorted_providers_config = deepcopy(self.providers_config)
|
|
343
|
+
sorted_providers_config = {
|
|
344
|
+
provider: sorted_providers_config.pop(provider),
|
|
345
|
+
**sorted_providers_config,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
for plugin_provider, provider_conf in sorted_providers_config.items():
|
|
349
|
+
for key in AUTH_TOPIC_KEYS:
|
|
350
|
+
auth_conf = getattr(provider_conf, key, None)
|
|
351
|
+
if auth_conf is None:
|
|
352
|
+
continue
|
|
353
|
+
# plugin without configured match criteria: only works for given provider
|
|
354
|
+
unconfigured_match = (
|
|
355
|
+
True
|
|
356
|
+
if (
|
|
357
|
+
not getattr(auth_conf, "matching_conf", {})
|
|
358
|
+
and not getattr(auth_conf, "matching_url", None)
|
|
359
|
+
and provider == plugin_provider
|
|
360
|
+
)
|
|
361
|
+
else False
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if unconfigured_match or _is_auth_plugin_matching(
|
|
365
|
+
auth_conf, matching_url, matching_conf
|
|
366
|
+
):
|
|
367
|
+
auth_conf.priority = provider_conf.priority
|
|
368
|
+
plugin = cast(
|
|
369
|
+
Authentication,
|
|
370
|
+
self._build_plugin(plugin_provider, auth_conf, Authentication),
|
|
371
|
+
)
|
|
372
|
+
yield plugin
|
|
373
|
+
else:
|
|
374
|
+
continue
|
|
375
|
+
|
|
376
|
+
def get_auth(
|
|
377
|
+
self,
|
|
378
|
+
provider: str,
|
|
379
|
+
matching_url: Optional[str] = None,
|
|
380
|
+
matching_conf: Optional[PluginConfig] = None,
|
|
381
|
+
) -> Optional[Union[AuthBase, Dict[str, str]]]:
|
|
382
|
+
"""Authenticate and return the authenticated object for the first matching
|
|
383
|
+
authentication plugin
|
|
384
|
+
|
|
385
|
+
:param provider: The provider for which to get the authentication plugin
|
|
386
|
+
:param matching_url: url to compare with plugin matching_url pattern
|
|
387
|
+
:param matching_conf: configuration to compare with plugin matching_conf
|
|
388
|
+
:returns: All the Authentication plugins for the given criteria
|
|
389
|
+
"""
|
|
390
|
+
for auth_plugin in self.get_auth_plugins(provider, matching_url, matching_conf):
|
|
391
|
+
if auth_plugin and callable(getattr(auth_plugin, "authenticate", None)):
|
|
392
|
+
try:
|
|
393
|
+
auth = auth_plugin.authenticate()
|
|
394
|
+
return auth
|
|
395
|
+
except (AuthenticationError, MisconfiguredError) as e:
|
|
396
|
+
logger.debug(f"Could not authenticate on {provider}: {str(e)}")
|
|
397
|
+
continue
|
|
398
|
+
else:
|
|
399
|
+
logger.debug(
|
|
400
|
+
f"Could not authenticate on {provider} using {auth_plugin} plugin"
|
|
401
|
+
)
|
|
402
|
+
continue
|
|
403
|
+
return None
|
|
275
404
|
|
|
276
405
|
@staticmethod
|
|
277
406
|
def get_crunch_plugin(name: str, **options: Any) -> Crunch:
|
|
@@ -279,11 +408,8 @@ class PluginManager:
|
|
|
279
408
|
it with the `options`
|
|
280
409
|
|
|
281
410
|
:param name: The name of the Crunch plugin to instantiate
|
|
282
|
-
:type name: str
|
|
283
411
|
:param options: The configuration parameters of the cruncher
|
|
284
|
-
:type options: dict
|
|
285
412
|
:returns: The cruncher named `name`
|
|
286
|
-
:rtype: :class:`~eodag.plugins.crunch.Crunch`
|
|
287
413
|
"""
|
|
288
414
|
klass = Crunch.get_plugin_by_class_name(name)
|
|
289
415
|
return klass(options)
|
|
@@ -297,9 +423,7 @@ class PluginManager:
|
|
|
297
423
|
"""Set the priority of the given provider
|
|
298
424
|
|
|
299
425
|
:param provider: The provider which is assigned the priority
|
|
300
|
-
:type provider: str
|
|
301
426
|
:param priority: The priority to assign to the provider
|
|
302
|
-
:type priority: int
|
|
303
427
|
"""
|
|
304
428
|
# Update the priority in the configurations so that it is taken into account
|
|
305
429
|
# when a plugin of this provider is latterly built
|
|
@@ -313,9 +437,11 @@ class PluginManager:
|
|
|
313
437
|
# Sort the provider configs, taking into account the new priority order
|
|
314
438
|
provider_configs.sort(key=attrgetter("priority"), reverse=True)
|
|
315
439
|
# Update the priority of already built plugins of the given provider
|
|
316
|
-
for provider_name, topic_class in self._built_plugins_cache:
|
|
440
|
+
for provider_name, topic_class, auth_match_md5 in self._built_plugins_cache:
|
|
317
441
|
if provider_name == provider:
|
|
318
|
-
self._built_plugins_cache[
|
|
442
|
+
self._built_plugins_cache[
|
|
443
|
+
(provider, topic_class, auth_match_md5)
|
|
444
|
+
].priority = priority
|
|
319
445
|
|
|
320
446
|
def _build_plugin(
|
|
321
447
|
self,
|
|
@@ -327,19 +453,23 @@ class PluginManager:
|
|
|
327
453
|
registered as the given provider
|
|
328
454
|
|
|
329
455
|
:param provider: The provider for which to build the plugin
|
|
330
|
-
:type provider: str
|
|
331
456
|
:param plugin_conf: The configuration of the plugin to be built
|
|
332
|
-
:type plugin_conf: :class:`~eodag.config.PluginConfig`
|
|
333
457
|
:param topic_class: The type of plugin to build
|
|
334
|
-
:type topic_class: :class:`~eodag.plugin.base.PluginTopic`
|
|
335
458
|
:returns: The built plugin
|
|
336
|
-
:rtype: :class:`~eodag.plugin.search.Search` or
|
|
337
459
|
:class:`~eodag.plugin.download.Download` or
|
|
338
460
|
:class:`~eodag.plugin.authentication.Authentication` or
|
|
339
461
|
:class:`~eodag.plugin.crunch.Crunch`
|
|
340
462
|
"""
|
|
463
|
+
# md5 hash to helps identifying an auth plugin within several for a given provider
|
|
464
|
+
# (each has distinct matching settings)
|
|
465
|
+
auth_match_md5 = dict_md5sum(
|
|
466
|
+
{
|
|
467
|
+
"matching_url": getattr(plugin_conf, "matching_url", None),
|
|
468
|
+
"matching_conf": getattr(plugin_conf, "matching_conf", None),
|
|
469
|
+
}
|
|
470
|
+
)
|
|
341
471
|
cached_instance = self._built_plugins_cache.setdefault(
|
|
342
|
-
(provider, topic_class.__name__), None
|
|
472
|
+
(provider, topic_class.__name__, auth_match_md5), None
|
|
343
473
|
)
|
|
344
474
|
if cached_instance is not None:
|
|
345
475
|
return cached_instance
|
|
@@ -349,5 +479,7 @@ class PluginManager:
|
|
|
349
479
|
plugin: Union[Api, Search, Download, Authentication, Crunch] = plugin_class(
|
|
350
480
|
provider, plugin_conf
|
|
351
481
|
)
|
|
352
|
-
self._built_plugins_cache[
|
|
482
|
+
self._built_plugins_cache[
|
|
483
|
+
(provider, topic_class.__name__, auth_match_md5)
|
|
484
|
+
] = plugin
|
|
353
485
|
return plugin
|
eodag/plugins/search/__init__.py
CHANGED
|
@@ -36,8 +36,8 @@ class PreparedSearch:
|
|
|
36
36
|
"""An object collecting needed information for search."""
|
|
37
37
|
|
|
38
38
|
product_type: Optional[str] = None
|
|
39
|
-
page: int = DEFAULT_PAGE
|
|
40
|
-
items_per_page: int = DEFAULT_ITEMS_PER_PAGE
|
|
39
|
+
page: Optional[int] = DEFAULT_PAGE
|
|
40
|
+
items_per_page: Optional[int] = DEFAULT_ITEMS_PER_PAGE
|
|
41
41
|
auth: Optional[Union[AuthBase, Dict[str, str]]] = None
|
|
42
42
|
auth_plugin: Optional[Authentication] = None
|
|
43
43
|
count: bool = True
|
|
@@ -45,10 +45,10 @@ class PreparedSearch:
|
|
|
45
45
|
info_message: Optional[str] = None
|
|
46
46
|
exception_message: Optional[str] = None
|
|
47
47
|
|
|
48
|
-
need_count: bool = field(init=False)
|
|
49
|
-
query_params: Dict[str, Any] = field(init=False)
|
|
50
|
-
query_string: str = field(init=False)
|
|
51
|
-
search_urls: List[str] = field(init=False)
|
|
52
|
-
product_type_def_params: Dict[str, Any] = field(init=False)
|
|
53
|
-
total_items_nb: int = field(init=False)
|
|
54
|
-
sort_by_qs: str = field(init=False)
|
|
48
|
+
need_count: bool = field(init=False, repr=False)
|
|
49
|
+
query_params: Dict[str, Any] = field(init=False, repr=False)
|
|
50
|
+
query_string: str = field(init=False, repr=False)
|
|
51
|
+
search_urls: List[str] = field(init=False, repr=False)
|
|
52
|
+
product_type_def_params: Dict[str, Any] = field(init=False, repr=False)
|
|
53
|
+
total_items_nb: int = field(init=False, repr=False)
|
|
54
|
+
sort_by_qs: str = field(init=False, repr=False)
|