eodag 4.0.0a3__py3-none-any.whl → 4.0.0a5__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/collection.py +8 -8
- eodag/api/core.py +216 -313
- eodag/api/product/_product.py +28 -6
- eodag/api/provider.py +990 -0
- eodag/cli.py +11 -4
- eodag/config.py +73 -444
- eodag/plugins/apis/ecmwf.py +3 -24
- eodag/plugins/apis/usgs.py +3 -24
- eodag/plugins/authentication/token.py +0 -1
- eodag/plugins/download/aws.py +83 -44
- eodag/plugins/download/base.py +117 -41
- eodag/plugins/download/http.py +84 -56
- eodag/plugins/manager.py +24 -34
- eodag/resources/ext_collections.json +1 -1
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/providers.yml +2 -0
- eodag/utils/s3.py +4 -4
- {eodag-4.0.0a3.dist-info → eodag-4.0.0a5.dist-info}/METADATA +1 -1
- {eodag-4.0.0a3.dist-info → eodag-4.0.0a5.dist-info}/RECORD +23 -22
- {eodag-4.0.0a3.dist-info → eodag-4.0.0a5.dist-info}/WHEEL +0 -0
- {eodag-4.0.0a3.dist-info → eodag-4.0.0a5.dist-info}/entry_points.txt +0 -0
- {eodag-4.0.0a3.dist-info → eodag-4.0.0a5.dist-info}/licenses/LICENSE +0 -0
- {eodag-4.0.0a3.dist-info → eodag-4.0.0a5.dist-info}/top_level.txt +0 -0
eodag/api/core.py
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import datetime
|
|
21
|
+
import itertools
|
|
21
22
|
import logging
|
|
22
23
|
import os
|
|
23
24
|
import re
|
|
@@ -25,19 +26,18 @@ import shutil
|
|
|
25
26
|
import tempfile
|
|
26
27
|
import warnings
|
|
27
28
|
from collections import deque
|
|
29
|
+
from copy import deepcopy
|
|
28
30
|
from importlib.metadata import version
|
|
29
31
|
from importlib.resources import files as res_files
|
|
30
32
|
from operator import attrgetter, itemgetter
|
|
31
|
-
from typing import TYPE_CHECKING, Any, Iterator, Optional, Union
|
|
33
|
+
from typing import TYPE_CHECKING, Any, Iterator, Optional, Union, cast
|
|
32
34
|
|
|
33
35
|
import geojson
|
|
34
36
|
import yaml
|
|
35
37
|
|
|
36
38
|
from eodag.api.collection import Collection, CollectionsDict, CollectionsList
|
|
37
|
-
from eodag.api.product.metadata_mapping import
|
|
38
|
-
|
|
39
|
-
mtd_cfg_as_conversion_and_querypath,
|
|
40
|
-
)
|
|
39
|
+
from eodag.api.product.metadata_mapping import mtd_cfg_as_conversion_and_querypath
|
|
40
|
+
from eodag.api.provider import Provider, ProvidersDict
|
|
41
41
|
from eodag.api.search_result import SearchResult
|
|
42
42
|
from eodag.config import (
|
|
43
43
|
PLUGINS_TOPICS_KEYS,
|
|
@@ -46,13 +46,7 @@ from eodag.config import (
|
|
|
46
46
|
credentials_in_auth,
|
|
47
47
|
get_ext_collections_conf,
|
|
48
48
|
load_default_config,
|
|
49
|
-
load_stac_provider_config,
|
|
50
49
|
load_yml_config,
|
|
51
|
-
override_config_from_env,
|
|
52
|
-
override_config_from_file,
|
|
53
|
-
override_config_from_mapping,
|
|
54
|
-
provider_config_init,
|
|
55
|
-
share_credentials,
|
|
56
50
|
)
|
|
57
51
|
from eodag.plugins.manager import PluginManager
|
|
58
52
|
from eodag.plugins.search import PreparedSearch
|
|
@@ -93,6 +87,7 @@ from eodag.utils.free_text_search import compile_free_text_query
|
|
|
93
87
|
from eodag.utils.stac_reader import fetch_stac_items
|
|
94
88
|
|
|
95
89
|
if TYPE_CHECKING:
|
|
90
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
96
91
|
from shapely.geometry.base import BaseGeometry
|
|
97
92
|
|
|
98
93
|
from eodag.api.product import EOProduct
|
|
@@ -124,7 +119,8 @@ class EODataAccessGateway:
|
|
|
124
119
|
)
|
|
125
120
|
collections_config_dict = SimpleYamlProxyConfig(collections_config_path).source
|
|
126
121
|
self.collections_config = self._collections_config_init(collections_config_dict)
|
|
127
|
-
|
|
122
|
+
|
|
123
|
+
self._providers = ProvidersDict.from_configs(load_default_config())
|
|
128
124
|
|
|
129
125
|
env_var_cfg_dir = "EODAG_CFG_DIR"
|
|
130
126
|
self.conf_dir = os.getenv(
|
|
@@ -148,9 +144,8 @@ class EODataAccessGateway:
|
|
|
148
144
|
self.conf_dir = tmp_conf_dir
|
|
149
145
|
makedirs(self.conf_dir)
|
|
150
146
|
|
|
151
|
-
self._plugins_manager = PluginManager(self.
|
|
152
|
-
|
|
153
|
-
self.providers_config = self._plugins_manager.providers_config
|
|
147
|
+
self._plugins_manager = PluginManager(self._providers)
|
|
148
|
+
self._providers = self._plugins_manager.providers
|
|
154
149
|
|
|
155
150
|
# First level override: From a user configuration file
|
|
156
151
|
if user_conf_file_path is None:
|
|
@@ -166,67 +161,29 @@ class EODataAccessGateway:
|
|
|
166
161
|
),
|
|
167
162
|
standard_configuration_path,
|
|
168
163
|
)
|
|
169
|
-
|
|
164
|
+
self._providers.update_from_config_file(user_conf_file_path)
|
|
170
165
|
|
|
171
166
|
# Second level override: From environment variables
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
# share credentials between updated plugins confs
|
|
175
|
-
share_credentials(self.providers_config)
|
|
167
|
+
self._providers.update_from_env()
|
|
176
168
|
|
|
177
169
|
# init updated providers conf
|
|
178
170
|
strict_mode = is_env_var_true("EODAG_STRICT_COLLECTIONS")
|
|
179
|
-
available_collections = set(self.collections_config.keys())
|
|
180
171
|
|
|
181
|
-
for provider in self.
|
|
182
|
-
|
|
183
|
-
self.providers_config[provider],
|
|
184
|
-
load_stac_provider_config(),
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
self._sync_provider_collections(
|
|
188
|
-
provider, available_collections, strict_mode
|
|
189
|
-
)
|
|
172
|
+
for provider in self._providers.values():
|
|
173
|
+
provider.sync_collections(self, strict_mode)
|
|
190
174
|
|
|
191
175
|
# re-build _plugins_manager using up-to-date providers_config
|
|
192
|
-
self._plugins_manager.rebuild(self.
|
|
176
|
+
self._plugins_manager.rebuild(self._providers)
|
|
193
177
|
|
|
194
178
|
# store pruned providers configs
|
|
195
179
|
self._pruned_providers_config: dict[str, Any] = {}
|
|
180
|
+
|
|
196
181
|
# filter out providers needing auth that have no credentials set
|
|
197
182
|
self._prune_providers_list()
|
|
198
183
|
|
|
199
184
|
# Sort providers taking into account of possible new priority orders
|
|
200
185
|
self._plugins_manager.sort_providers()
|
|
201
186
|
|
|
202
|
-
# set locations configuration
|
|
203
|
-
if locations_conf_path is None:
|
|
204
|
-
locations_conf_path = os.getenv("EODAG_LOCS_CFG_FILE")
|
|
205
|
-
if locations_conf_path is None:
|
|
206
|
-
locations_conf_path = os.path.join(self.conf_dir, "locations.yml")
|
|
207
|
-
if not os.path.isfile(locations_conf_path):
|
|
208
|
-
# copy locations conf file and replace path example
|
|
209
|
-
locations_conf_template = str(
|
|
210
|
-
res_files("eodag") / "resources" / "locations_conf_template.yml"
|
|
211
|
-
)
|
|
212
|
-
with (
|
|
213
|
-
open(locations_conf_template) as infile,
|
|
214
|
-
open(locations_conf_path, "w") as outfile,
|
|
215
|
-
):
|
|
216
|
-
# The template contains paths in the form of:
|
|
217
|
-
# /path/to/locations/file.shp
|
|
218
|
-
path_template = "/path/to/locations/"
|
|
219
|
-
for line in infile:
|
|
220
|
-
line = line.replace(
|
|
221
|
-
path_template,
|
|
222
|
-
os.path.join(self.conf_dir, "shp") + os.path.sep,
|
|
223
|
-
)
|
|
224
|
-
outfile.write(line)
|
|
225
|
-
# copy sample shapefile dir
|
|
226
|
-
shutil.copytree(
|
|
227
|
-
str(res_files("eodag") / "resources" / "shp"),
|
|
228
|
-
os.path.join(self.conf_dir, "shp"),
|
|
229
|
-
)
|
|
230
187
|
self.set_locations_conf(locations_conf_path)
|
|
231
188
|
|
|
232
189
|
def _collections_config_init(
|
|
@@ -243,57 +200,19 @@ class EODataAccessGateway:
|
|
|
243
200
|
]
|
|
244
201
|
return CollectionsDict(collections)
|
|
245
202
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
In strict mode, removes collections not in available_collections.
|
|
256
|
-
In permissive mode, adds empty collection configs for missing types.
|
|
257
|
-
|
|
258
|
-
:param provider: The provider name whose collections should be synchronized.
|
|
259
|
-
:param available_collections: The set of available collection IDs.
|
|
260
|
-
:param strict_mode: If True, remove unknown collections; if False, add empty configs for them.
|
|
261
|
-
:returns: None
|
|
262
|
-
"""
|
|
263
|
-
provider_products = self.providers_config[provider].products
|
|
264
|
-
products_to_remove: list[str] = []
|
|
265
|
-
products_to_add: list[str] = []
|
|
266
|
-
|
|
267
|
-
for product_id in provider_products:
|
|
268
|
-
if product_id == GENERIC_COLLECTION:
|
|
269
|
-
continue
|
|
270
|
-
|
|
271
|
-
if product_id not in available_collections:
|
|
272
|
-
if strict_mode:
|
|
273
|
-
products_to_remove.append(product_id)
|
|
274
|
-
continue
|
|
275
|
-
|
|
276
|
-
empty_product = Collection.create_with_dag(
|
|
277
|
-
self, id=product_id, title=product_id, description=NOT_AVAILABLE
|
|
278
|
-
)
|
|
279
|
-
self.collections_config[product_id] = empty_product
|
|
280
|
-
products_to_add.append(product_id)
|
|
281
|
-
|
|
282
|
-
if products_to_add:
|
|
283
|
-
logger.debug(
|
|
284
|
-
"Collections permissive mode, %s added (provider %s)",
|
|
285
|
-
", ".join(products_to_add),
|
|
286
|
-
provider,
|
|
203
|
+
@property
|
|
204
|
+
def providers(self) -> ProvidersDict:
|
|
205
|
+
"""Providers of eodag configuration sorted by priority in descending order and by name in ascending order."""
|
|
206
|
+
providers = deepcopy(self._providers)
|
|
207
|
+
# Sort: priority descending, then name ascending
|
|
208
|
+
providers.data = {
|
|
209
|
+
k: v
|
|
210
|
+
for k, v in sorted(
|
|
211
|
+
providers.data.items(), key=lambda item: (-item[1].priority, item[0])
|
|
287
212
|
)
|
|
213
|
+
}
|
|
288
214
|
|
|
289
|
-
|
|
290
|
-
logger.debug(
|
|
291
|
-
"Collections strict mode, ignoring %s (provider %s)",
|
|
292
|
-
", ".join(products_to_remove),
|
|
293
|
-
provider,
|
|
294
|
-
)
|
|
295
|
-
for id in products_to_remove:
|
|
296
|
-
del self.providers_config[provider].products[id]
|
|
215
|
+
return providers
|
|
297
216
|
|
|
298
217
|
def get_version(self) -> str:
|
|
299
218
|
"""Get eodag package version"""
|
|
@@ -305,10 +224,11 @@ class EODataAccessGateway:
|
|
|
305
224
|
:param provider: The name of the provider that should be considered as the
|
|
306
225
|
preferred provider to be used for this instance
|
|
307
226
|
"""
|
|
308
|
-
if provider not in self.
|
|
227
|
+
if provider not in self.providers.names:
|
|
309
228
|
raise UnsupportedProvider(
|
|
310
229
|
f"This provider is not recognised by eodag: {provider}"
|
|
311
230
|
)
|
|
231
|
+
|
|
312
232
|
preferred_provider, max_priority = self.get_preferred_provider()
|
|
313
233
|
if preferred_provider != provider:
|
|
314
234
|
new_priority = max_priority + 1
|
|
@@ -320,12 +240,7 @@ class EODataAccessGateway:
|
|
|
320
240
|
|
|
321
241
|
:returns: The provider with the maximum priority and its priority
|
|
322
242
|
"""
|
|
323
|
-
|
|
324
|
-
(provider, conf.priority)
|
|
325
|
-
for provider, conf in self.providers_config.items()
|
|
326
|
-
]
|
|
327
|
-
preferred, priority = max(providers_with_priority, key=itemgetter(1))
|
|
328
|
-
return preferred, priority
|
|
243
|
+
return max(self._providers.priorities.items(), key=itemgetter(1))
|
|
329
244
|
|
|
330
245
|
def update_providers_config(
|
|
331
246
|
self,
|
|
@@ -347,27 +262,17 @@ class EODataAccessGateway:
|
|
|
347
262
|
return None
|
|
348
263
|
|
|
349
264
|
# restore the pruned configuration
|
|
350
|
-
for
|
|
351
|
-
|
|
265
|
+
for name in list(self._pruned_providers_config):
|
|
266
|
+
config = self._pruned_providers_config[name]
|
|
267
|
+
if name in conf_update:
|
|
352
268
|
logger.info(
|
|
353
|
-
"%s: provider restored from the pruned configurations",
|
|
354
|
-
provider,
|
|
269
|
+
"%s: provider restored from the pruned configurations", name
|
|
355
270
|
)
|
|
356
|
-
self.
|
|
357
|
-
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
override_config_from_mapping(self.providers_config, conf_update)
|
|
271
|
+
self._providers[name] = Provider(config)
|
|
272
|
+
self._pruned_providers_config.pop(name)
|
|
361
273
|
|
|
362
|
-
|
|
363
|
-
share_credentials(self.providers_config)
|
|
274
|
+
self._providers.update_from_configs(conf_update)
|
|
364
275
|
|
|
365
|
-
for provider in conf_update.keys():
|
|
366
|
-
provider_config_init(
|
|
367
|
-
self.providers_config[provider],
|
|
368
|
-
load_stac_provider_config(),
|
|
369
|
-
)
|
|
370
|
-
setattr(self.providers_config[provider], "collections_fetched", False)
|
|
371
276
|
# re-create _plugins_manager using up-to-date providers_config
|
|
372
277
|
self._plugins_manager.build_collection_to_provider_config_map()
|
|
373
278
|
|
|
@@ -398,10 +303,11 @@ class EODataAccessGateway:
|
|
|
398
303
|
:param search: Search :class:`~eodag.config.PluginConfig` mapping
|
|
399
304
|
:param products: Provider collections mapping
|
|
400
305
|
:param download: Download :class:`~eodag.config.PluginConfig` mapping
|
|
401
|
-
:param kwargs: Additional :class:`~eodag.
|
|
306
|
+
:param kwargs: Additional :class:`~eodag.api.provider.ProviderConfig` mapping
|
|
402
307
|
"""
|
|
403
308
|
conf_dict: dict[str, Any] = {
|
|
404
309
|
name: {
|
|
310
|
+
"name": name,
|
|
405
311
|
"url": url,
|
|
406
312
|
"search": {"type": "StacSearch", **search},
|
|
407
313
|
"products": {
|
|
@@ -440,8 +346,10 @@ class EODataAccessGateway:
|
|
|
440
346
|
def _prune_providers_list(self) -> None:
|
|
441
347
|
"""Removes from config providers needing auth that have no credentials set."""
|
|
442
348
|
update_needed = False
|
|
443
|
-
|
|
444
|
-
|
|
349
|
+
|
|
350
|
+
# loop over a copy to allow popping items
|
|
351
|
+
for name, provider in list(self._providers.items()):
|
|
352
|
+
conf = provider.config
|
|
445
353
|
|
|
446
354
|
# remove providers using skipped plugins
|
|
447
355
|
if [
|
|
@@ -450,7 +358,7 @@ class EODataAccessGateway:
|
|
|
450
358
|
if isinstance(v, PluginConfig)
|
|
451
359
|
and getattr(v, "type", None) in self._plugins_manager.skipped_plugins
|
|
452
360
|
]:
|
|
453
|
-
self.
|
|
361
|
+
del self._providers[provider.name]
|
|
454
362
|
logger.debug(
|
|
455
363
|
f"{provider}: provider needing unavailable plugin has been removed"
|
|
456
364
|
)
|
|
@@ -461,26 +369,28 @@ class EODataAccessGateway:
|
|
|
461
369
|
credentials_exist = credentials_in_auth(conf.api)
|
|
462
370
|
if not credentials_exist:
|
|
463
371
|
# credentials needed but not found
|
|
464
|
-
self._pruned_providers_config[provider] =
|
|
465
|
-
|
|
466
|
-
|
|
372
|
+
self._pruned_providers_config[provider.name] = conf
|
|
373
|
+
del self._providers[provider.name]
|
|
374
|
+
|
|
467
375
|
update_needed = True
|
|
468
376
|
logger.info(
|
|
469
377
|
"%s: provider needing auth for search has been pruned because no credentials could be found",
|
|
470
378
|
provider,
|
|
471
379
|
)
|
|
380
|
+
|
|
472
381
|
elif hasattr(conf, "search") and getattr(conf.search, "need_auth", False):
|
|
473
382
|
if not hasattr(conf, "auth") and not hasattr(conf, "search_auth"):
|
|
474
383
|
# credentials needed but no auth plugin was found
|
|
475
|
-
self._pruned_providers_config[provider] =
|
|
476
|
-
|
|
477
|
-
|
|
384
|
+
self._pruned_providers_config[provider.name] = conf
|
|
385
|
+
del self._providers[provider.name]
|
|
386
|
+
|
|
478
387
|
update_needed = True
|
|
479
388
|
logger.info(
|
|
480
389
|
"%s: provider needing auth for search has been pruned because no auth plugin could be found",
|
|
481
390
|
provider,
|
|
482
391
|
)
|
|
483
392
|
continue
|
|
393
|
+
|
|
484
394
|
credentials_exist = (
|
|
485
395
|
hasattr(conf, "search_auth")
|
|
486
396
|
and credentials_in_auth(conf.search_auth)
|
|
@@ -491,30 +401,31 @@ class EODataAccessGateway:
|
|
|
491
401
|
)
|
|
492
402
|
if not credentials_exist:
|
|
493
403
|
# credentials needed but not found
|
|
494
|
-
self._pruned_providers_config[provider] =
|
|
495
|
-
|
|
496
|
-
|
|
404
|
+
self._pruned_providers_config[provider.name] = conf
|
|
405
|
+
del self._providers[provider.name]
|
|
406
|
+
|
|
497
407
|
update_needed = True
|
|
498
408
|
logger.info(
|
|
499
409
|
"%s: provider needing auth for search has been pruned because no credentials could be found",
|
|
500
410
|
provider,
|
|
501
411
|
)
|
|
412
|
+
|
|
502
413
|
elif not hasattr(conf, "api") and not hasattr(conf, "search"):
|
|
503
414
|
# provider should have at least an api or search plugin
|
|
504
|
-
self._pruned_providers_config[provider] =
|
|
505
|
-
|
|
506
|
-
|
|
415
|
+
self._pruned_providers_config[provider.name] = conf
|
|
416
|
+
del self._providers[provider.name]
|
|
417
|
+
|
|
418
|
+
update_needed = True
|
|
507
419
|
logger.info(
|
|
508
420
|
"%s: provider has been pruned because no api or search plugin could be found",
|
|
509
421
|
provider,
|
|
510
422
|
)
|
|
511
|
-
update_needed = True
|
|
512
423
|
|
|
513
424
|
if update_needed:
|
|
514
425
|
# rebuild _plugins_manager with updated providers list
|
|
515
|
-
self._plugins_manager.rebuild(self.
|
|
426
|
+
self._plugins_manager.rebuild(self._providers)
|
|
516
427
|
|
|
517
|
-
def set_locations_conf(self, locations_conf_path: str) -> None:
|
|
428
|
+
def set_locations_conf(self, locations_conf_path: Optional[str]) -> None:
|
|
518
429
|
"""Set locations configuration.
|
|
519
430
|
This configuration (YML format) will contain a shapefile list associated
|
|
520
431
|
to a name and attribute parameters needed to identify the needed geometry.
|
|
@@ -537,6 +448,37 @@ class EODataAccessGateway:
|
|
|
537
448
|
|
|
538
449
|
:param locations_conf_path: Path to the locations configuration file
|
|
539
450
|
"""
|
|
451
|
+
if locations_conf_path is None:
|
|
452
|
+
locations_conf_path = os.getenv("EODAG_LOCS_CFG_FILE")
|
|
453
|
+
if locations_conf_path is None:
|
|
454
|
+
locations_conf_path = os.path.join(self.conf_dir, "locations.yml")
|
|
455
|
+
if not os.path.isfile(locations_conf_path):
|
|
456
|
+
# Ensure the directory exists
|
|
457
|
+
os.makedirs(os.path.dirname(locations_conf_path), exist_ok=True)
|
|
458
|
+
|
|
459
|
+
# copy locations conf file and replace path example
|
|
460
|
+
locations_conf_template = str(
|
|
461
|
+
res_files("eodag") / "resources" / "locations_conf_template.yml"
|
|
462
|
+
)
|
|
463
|
+
with (
|
|
464
|
+
open(locations_conf_template) as infile,
|
|
465
|
+
open(locations_conf_path, "w") as outfile,
|
|
466
|
+
):
|
|
467
|
+
# The template contains paths in the form of:
|
|
468
|
+
# /path/to/locations/file.shp
|
|
469
|
+
path_template = "/path/to/locations/"
|
|
470
|
+
for line in infile:
|
|
471
|
+
line = line.replace(
|
|
472
|
+
path_template,
|
|
473
|
+
os.path.join(self.conf_dir, "shp") + os.path.sep,
|
|
474
|
+
)
|
|
475
|
+
outfile.write(line)
|
|
476
|
+
# copy sample shapefile dir
|
|
477
|
+
shutil.copytree(
|
|
478
|
+
str(res_files("eodag") / "resources" / "shp"),
|
|
479
|
+
os.path.join(self.conf_dir, "shp"),
|
|
480
|
+
)
|
|
481
|
+
|
|
540
482
|
if os.path.isfile(locations_conf_path):
|
|
541
483
|
locations_config = load_yml_config(locations_conf_path)
|
|
542
484
|
|
|
@@ -567,17 +509,11 @@ class EODataAccessGateway:
|
|
|
567
509
|
# First, update collections list if possible
|
|
568
510
|
self.fetch_collections_list(provider=provider)
|
|
569
511
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if not provider
|
|
573
|
-
else [
|
|
574
|
-
p
|
|
575
|
-
for p in self.providers_config.values()
|
|
576
|
-
if provider in [p.name, getattr(p, "group", None)]
|
|
577
|
-
]
|
|
512
|
+
providers_iter, providers_check = itertools.tee(
|
|
513
|
+
self._providers.filter_by_name_or_group(provider)
|
|
578
514
|
)
|
|
579
515
|
|
|
580
|
-
if provider and not
|
|
516
|
+
if provider and not any(providers_check):
|
|
581
517
|
raise UnsupportedProvider(
|
|
582
518
|
f"The requested provider is not (yet) supported: {provider}"
|
|
583
519
|
)
|
|
@@ -585,8 +521,8 @@ class EODataAccessGateway:
|
|
|
585
521
|
# unique collection ids from providers configs
|
|
586
522
|
collection_ids = {
|
|
587
523
|
collection_id
|
|
588
|
-
for p in
|
|
589
|
-
for collection_id in p.
|
|
524
|
+
for p in providers_iter
|
|
525
|
+
for collection_id in p.collections_config
|
|
590
526
|
if collection_id != GENERIC_COLLECTION
|
|
591
527
|
}
|
|
592
528
|
|
|
@@ -611,42 +547,18 @@ class EODataAccessGateway:
|
|
|
611
547
|
if strict_mode:
|
|
612
548
|
return
|
|
613
549
|
|
|
614
|
-
providers_to_fetch = list(self.providers_config.keys())
|
|
615
|
-
# check if some providers are grouped under a group name which is not a provider name
|
|
616
|
-
if provider is not None and provider not in self.providers_config:
|
|
617
|
-
providers_to_fetch = [
|
|
618
|
-
p
|
|
619
|
-
for p, pconf in self.providers_config.items()
|
|
620
|
-
if provider == getattr(pconf, "group", None)
|
|
621
|
-
]
|
|
622
|
-
if providers_to_fetch:
|
|
623
|
-
logger.info(
|
|
624
|
-
f"Fetch collections for {provider} group: {', '.join(providers_to_fetch)}"
|
|
625
|
-
)
|
|
626
|
-
else:
|
|
627
|
-
return None
|
|
628
|
-
elif provider is not None:
|
|
629
|
-
providers_to_fetch = [provider]
|
|
630
|
-
|
|
631
550
|
# providers discovery confs that are fetchable
|
|
632
|
-
providers_discovery_configs_fetchable: dict[
|
|
551
|
+
providers_discovery_configs_fetchable: dict[
|
|
552
|
+
str, PluginConfig.DiscoverCollections
|
|
553
|
+
] = {}
|
|
633
554
|
# check if any provider has not already been fetched for collections
|
|
634
555
|
already_fetched = True
|
|
635
|
-
for provider_to_fetch in
|
|
636
|
-
|
|
637
|
-
# get discovery conf
|
|
638
|
-
if hasattr(provider_config, "search"):
|
|
639
|
-
provider_search_config = provider_config.search
|
|
640
|
-
elif hasattr(provider_config, "api"):
|
|
641
|
-
provider_search_config = provider_config.api
|
|
642
|
-
else:
|
|
643
|
-
continue
|
|
644
|
-
discovery_conf = getattr(provider_search_config, "discover_collections", {})
|
|
645
|
-
if discovery_conf.get("fetch_url"):
|
|
556
|
+
for provider_to_fetch in self._providers.filter_by_name_or_group(provider):
|
|
557
|
+
if provider_to_fetch.fetchable and provider_to_fetch.search_config:
|
|
646
558
|
providers_discovery_configs_fetchable[
|
|
647
|
-
provider_to_fetch
|
|
648
|
-
] =
|
|
649
|
-
if not
|
|
559
|
+
provider_to_fetch.name
|
|
560
|
+
] = provider_to_fetch.search_config.discover_collections
|
|
561
|
+
if not provider_to_fetch.collections_fetched:
|
|
650
562
|
already_fetched = False
|
|
651
563
|
|
|
652
564
|
if not already_fetched:
|
|
@@ -672,54 +584,55 @@ class EODataAccessGateway:
|
|
|
672
584
|
# and collections list would need to be fetched
|
|
673
585
|
|
|
674
586
|
# get ext_collections conf for user modified providers
|
|
675
|
-
|
|
587
|
+
default_providers = ProvidersDict.from_configs(load_default_config())
|
|
676
588
|
for (
|
|
677
589
|
provider,
|
|
678
590
|
user_discovery_conf,
|
|
679
591
|
) in providers_discovery_configs_fetchable.items():
|
|
680
592
|
# default discover_collections conf
|
|
681
|
-
if provider in
|
|
682
|
-
|
|
683
|
-
if
|
|
684
|
-
default_provider_search_config = default_provider_config.search
|
|
685
|
-
elif hasattr(default_provider_config, "api"):
|
|
686
|
-
default_provider_search_config = default_provider_config.api
|
|
687
|
-
else:
|
|
593
|
+
if provider in default_providers:
|
|
594
|
+
default_provider = default_providers[provider]
|
|
595
|
+
if not default_provider.search_config:
|
|
688
596
|
continue
|
|
689
|
-
|
|
690
|
-
|
|
597
|
+
|
|
598
|
+
default_discovery_conf = (
|
|
599
|
+
default_provider.search_config.discover_collections
|
|
691
600
|
)
|
|
601
|
+
|
|
692
602
|
# compare confs
|
|
693
603
|
if default_discovery_conf["result_type"] == "json" and isinstance(
|
|
694
604
|
default_discovery_conf["results_entry"], str
|
|
695
605
|
):
|
|
696
|
-
default_discovery_conf_parsed =
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
606
|
+
default_discovery_conf_parsed = cast(
|
|
607
|
+
PluginConfig.DiscoverCollections,
|
|
608
|
+
dict(
|
|
609
|
+
default_discovery_conf,
|
|
610
|
+
**{
|
|
611
|
+
"results_entry": string_to_jsonpath(
|
|
612
|
+
default_discovery_conf["results_entry"], force=True
|
|
613
|
+
)
|
|
614
|
+
},
|
|
615
|
+
**mtd_cfg_as_conversion_and_querypath(
|
|
616
|
+
dict(
|
|
617
|
+
generic_collection_id=default_discovery_conf[
|
|
618
|
+
"generic_collection_id"
|
|
619
|
+
]
|
|
620
|
+
)
|
|
621
|
+
),
|
|
622
|
+
**dict(
|
|
623
|
+
generic_collection_parsable_properties=mtd_cfg_as_conversion_and_querypath(
|
|
624
|
+
default_discovery_conf[
|
|
625
|
+
"generic_collection_parsable_properties"
|
|
626
|
+
]
|
|
627
|
+
)
|
|
628
|
+
),
|
|
629
|
+
**dict(
|
|
630
|
+
generic_collection_parsable_metadata=mtd_cfg_as_conversion_and_querypath(
|
|
631
|
+
default_discovery_conf[
|
|
632
|
+
"generic_collection_parsable_metadata"
|
|
633
|
+
]
|
|
634
|
+
)
|
|
635
|
+
),
|
|
723
636
|
),
|
|
724
637
|
)
|
|
725
638
|
else:
|
|
@@ -757,51 +670,34 @@ class EODataAccessGateway:
|
|
|
757
670
|
all providers (None value).
|
|
758
671
|
:returns: external collections configuration
|
|
759
672
|
"""
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
if provider and
|
|
766
|
-
logger.info(
|
|
767
|
-
f"Discover collections for {provider} group: {', '.join(grouped_providers)}"
|
|
768
|
-
)
|
|
769
|
-
elif provider and provider not in self.providers_config:
|
|
673
|
+
|
|
674
|
+
providers_iter, providers_check = itertools.tee(
|
|
675
|
+
self.providers.filter_by_name_or_group(provider)
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
if provider and not any(providers_check):
|
|
770
679
|
raise UnsupportedProvider(
|
|
771
680
|
f"The requested provider is not (yet) supported: {provider}"
|
|
772
681
|
)
|
|
682
|
+
|
|
773
683
|
ext_collections_conf: dict[str, Any] = {}
|
|
774
|
-
|
|
775
|
-
p
|
|
776
|
-
for p in (
|
|
777
|
-
[
|
|
778
|
-
p
|
|
779
|
-
for p in self.providers_config
|
|
780
|
-
if p in grouped_providers + [provider]
|
|
781
|
-
]
|
|
782
|
-
if provider
|
|
783
|
-
else self.available_providers()
|
|
784
|
-
)
|
|
785
|
-
]
|
|
684
|
+
|
|
786
685
|
kwargs: dict[str, Any] = {}
|
|
787
|
-
for
|
|
788
|
-
if
|
|
789
|
-
search_plugin_config = self.providers_config[provider].search
|
|
790
|
-
elif hasattr(self.providers_config[provider], "api"):
|
|
791
|
-
search_plugin_config = self.providers_config[provider].api
|
|
792
|
-
else:
|
|
686
|
+
for p in providers_iter:
|
|
687
|
+
if not p.search_config:
|
|
793
688
|
return None
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
):
|
|
689
|
+
|
|
690
|
+
if p.fetchable:
|
|
797
691
|
search_plugin: Union[Search, Api] = next(
|
|
798
|
-
self._plugins_manager.get_search_plugins(provider=
|
|
692
|
+
self._plugins_manager.get_search_plugins(provider=p.name)
|
|
799
693
|
)
|
|
694
|
+
|
|
800
695
|
# check after plugin init if still fetchable
|
|
801
696
|
if not getattr(search_plugin.config, "discover_collections", {}).get(
|
|
802
697
|
"fetch_url"
|
|
803
698
|
):
|
|
804
699
|
continue
|
|
700
|
+
|
|
805
701
|
# append auth to search plugin if needed
|
|
806
702
|
if getattr(search_plugin.config, "need_auth", False):
|
|
807
703
|
if auth := self._plugins_manager.get_auth(
|
|
@@ -812,12 +708,12 @@ class EODataAccessGateway:
|
|
|
812
708
|
kwargs["auth"] = auth
|
|
813
709
|
else:
|
|
814
710
|
logger.debug(
|
|
815
|
-
f"Could not authenticate on {
|
|
711
|
+
f"Could not authenticate on {p} for collections discovery"
|
|
816
712
|
)
|
|
817
|
-
ext_collections_conf[
|
|
713
|
+
ext_collections_conf[p.name] = None
|
|
818
714
|
continue
|
|
819
715
|
|
|
820
|
-
ext_collections_conf[
|
|
716
|
+
ext_collections_conf[p.name] = search_plugin.discover_collections(
|
|
821
717
|
**kwargs
|
|
822
718
|
)
|
|
823
719
|
|
|
@@ -831,20 +727,15 @@ class EODataAccessGateway:
|
|
|
831
727
|
:param ext_collections_conf: external collections configuration
|
|
832
728
|
"""
|
|
833
729
|
for provider, new_collections_conf in ext_collections_conf.items():
|
|
834
|
-
if new_collections_conf and provider in self.
|
|
730
|
+
if new_collections_conf and provider in self._providers:
|
|
835
731
|
try:
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
) or getattr(self.providers_config[provider], "api", None)
|
|
839
|
-
if search_plugin_config is None:
|
|
840
|
-
continue
|
|
841
|
-
if not getattr(
|
|
842
|
-
search_plugin_config, "discover_collections", {}
|
|
843
|
-
).get("fetch_url"):
|
|
732
|
+
fetchable = self._providers[provider].fetchable
|
|
733
|
+
if not fetchable:
|
|
844
734
|
# conf has been updated and provider collections are no more discoverable
|
|
845
735
|
continue
|
|
736
|
+
|
|
846
737
|
provider_products_config = (
|
|
847
|
-
self.
|
|
738
|
+
self._providers[provider].collections_config or {}
|
|
848
739
|
)
|
|
849
740
|
except UnsupportedProvider:
|
|
850
741
|
logger.debug(
|
|
@@ -852,6 +743,7 @@ class EODataAccessGateway:
|
|
|
852
743
|
provider,
|
|
853
744
|
)
|
|
854
745
|
continue
|
|
746
|
+
|
|
855
747
|
new_collections: list[str] = []
|
|
856
748
|
bad_formatted_col_count = 0
|
|
857
749
|
for (
|
|
@@ -861,11 +753,10 @@ class EODataAccessGateway:
|
|
|
861
753
|
if new_collection not in provider_products_config:
|
|
862
754
|
for existing_collection in provider_products_config.copy():
|
|
863
755
|
# compare parsed extracted conf (without metadata_mapping entry)
|
|
864
|
-
unparsable_keys =
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
)
|
|
756
|
+
unparsable_keys = self._providers[
|
|
757
|
+
provider
|
|
758
|
+
].unparsable_properties
|
|
759
|
+
|
|
869
760
|
new_parsed_collections_conf = {
|
|
870
761
|
k: v
|
|
871
762
|
for k, v in new_collection_conf.items()
|
|
@@ -930,19 +821,27 @@ class EODataAccessGateway:
|
|
|
930
821
|
provider,
|
|
931
822
|
)
|
|
932
823
|
|
|
933
|
-
elif provider not in self.
|
|
824
|
+
elif provider not in self._providers:
|
|
934
825
|
# unknown provider
|
|
935
826
|
continue
|
|
936
|
-
|
|
827
|
+
|
|
828
|
+
self._providers[provider].collections_fetched = True
|
|
937
829
|
|
|
938
830
|
# re-create _plugins_manager using up-to-date providers_config
|
|
939
831
|
self._plugins_manager.build_collection_to_provider_config_map()
|
|
940
832
|
|
|
833
|
+
@_deprecated(
|
|
834
|
+
reason="Please use 'EODataAccessGateway.providers' instead",
|
|
835
|
+
version="4.0.0",
|
|
836
|
+
)
|
|
941
837
|
def available_providers(
|
|
942
838
|
self, collection: Optional[str] = None, by_group: bool = False
|
|
943
839
|
) -> list[str]:
|
|
944
840
|
"""Gives the sorted list of the available providers or groups
|
|
945
841
|
|
|
842
|
+
.. deprecated:: v4.0.0
|
|
843
|
+
Please use :attr:`eodag.api.core.EODataAccessGateway.providers` instead.
|
|
844
|
+
|
|
946
845
|
The providers or groups are sorted first by their priority level in descending order,
|
|
947
846
|
and then alphabetically in ascending order for providers or groups with the same
|
|
948
847
|
priority level.
|
|
@@ -952,32 +851,26 @@ class EODataAccessGateway:
|
|
|
952
851
|
of providers, mixed with other providers
|
|
953
852
|
:returns: the sorted list of the available providers or groups
|
|
954
853
|
"""
|
|
854
|
+
candidates = []
|
|
955
855
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
if collection in getattr(v, "products", {}).keys()
|
|
961
|
-
]
|
|
962
|
-
else:
|
|
963
|
-
providers = [
|
|
964
|
-
(v.group if by_group and hasattr(v, "group") else k, v.priority)
|
|
965
|
-
for k, v in self.providers_config.items()
|
|
966
|
-
]
|
|
856
|
+
# use "providers" property to get sorted providers
|
|
857
|
+
for key, provider in self.providers.items():
|
|
858
|
+
if collection and collection not in provider.collections_config:
|
|
859
|
+
continue
|
|
967
860
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
for name, priority in providers:
|
|
972
|
-
if name not in group_priority or priority > group_priority[name]:
|
|
973
|
-
group_priority[name] = priority
|
|
974
|
-
providers = list(group_priority.items())
|
|
861
|
+
group = getattr(provider.config, "group", None)
|
|
862
|
+
name = group if by_group and group else key
|
|
863
|
+
candidates.append((name, provider.priority))
|
|
975
864
|
|
|
976
|
-
|
|
977
|
-
|
|
865
|
+
if by_group:
|
|
866
|
+
# Keep only the highest-priority entry per group
|
|
867
|
+
grouped: dict[str, int] = {}
|
|
868
|
+
for name, priority in candidates:
|
|
869
|
+
if name not in grouped or priority > grouped[name]:
|
|
870
|
+
grouped[name] = priority
|
|
871
|
+
candidates = list(grouped.items())
|
|
978
872
|
|
|
979
|
-
|
|
980
|
-
return [name for name, _ in providers]
|
|
873
|
+
return [name for name, _ in candidates]
|
|
981
874
|
|
|
982
875
|
def get_collection_from_alias(self, alias_or_id: str) -> str:
|
|
983
876
|
"""Return the id of a collection by either its id or alias
|
|
@@ -1253,7 +1146,7 @@ class EODataAccessGateway:
|
|
|
1253
1146
|
warnings.warn(
|
|
1254
1147
|
"Usage of deprecated search parameter 'page' "
|
|
1255
1148
|
"(Please use 'SearchResult.next_page()' instead)"
|
|
1256
|
-
" -- Deprecated since
|
|
1149
|
+
" -- Deprecated since v4.0.0",
|
|
1257
1150
|
DeprecationWarning,
|
|
1258
1151
|
stacklevel=2,
|
|
1259
1152
|
)
|
|
@@ -1323,7 +1216,7 @@ class EODataAccessGateway:
|
|
|
1323
1216
|
|
|
1324
1217
|
@_deprecated(
|
|
1325
1218
|
reason="Please use 'SearchResult.next_page()' instead",
|
|
1326
|
-
version="
|
|
1219
|
+
version="4.0.0",
|
|
1327
1220
|
)
|
|
1328
1221
|
def search_iter_page(
|
|
1329
1222
|
self,
|
|
@@ -1336,7 +1229,7 @@ class EODataAccessGateway:
|
|
|
1336
1229
|
) -> Iterator[SearchResult]:
|
|
1337
1230
|
"""Iterate over the pages of a products search.
|
|
1338
1231
|
|
|
1339
|
-
.. deprecated::
|
|
1232
|
+
.. deprecated:: v4.0.0
|
|
1340
1233
|
Please use :meth:`eodag.api.search_result.SearchResult.next_page` instead.
|
|
1341
1234
|
|
|
1342
1235
|
:param items_per_page: (optional) The number of results requested per page
|
|
@@ -1391,7 +1284,7 @@ class EODataAccessGateway:
|
|
|
1391
1284
|
|
|
1392
1285
|
@_deprecated(
|
|
1393
1286
|
reason="Please use 'SearchResult.next_page()' instead",
|
|
1394
|
-
version="
|
|
1287
|
+
version="4.0.0",
|
|
1395
1288
|
)
|
|
1396
1289
|
def search_iter_page_plugin(
|
|
1397
1290
|
self,
|
|
@@ -1401,7 +1294,7 @@ class EODataAccessGateway:
|
|
|
1401
1294
|
) -> Iterator[SearchResult]:
|
|
1402
1295
|
"""Iterate over the pages of a products search using a given search plugin.
|
|
1403
1296
|
|
|
1404
|
-
.. deprecated::
|
|
1297
|
+
.. deprecated:: v4.0.0
|
|
1405
1298
|
Please use :meth:`eodag.api.search_result.SearchResult.next_page` instead.
|
|
1406
1299
|
|
|
1407
1300
|
:param items_per_page: (optional) The number of results requested per page
|
|
@@ -2030,6 +1923,7 @@ class EODataAccessGateway:
|
|
|
2030
1923
|
search_result: SearchResult,
|
|
2031
1924
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
2032
1925
|
progress_callback: Optional[ProgressCallback] = None,
|
|
1926
|
+
executor: Optional[ThreadPoolExecutor] = None,
|
|
2033
1927
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
2034
1928
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
2035
1929
|
**kwargs: Unpack[DownloadConf],
|
|
@@ -2047,6 +1941,8 @@ class EODataAccessGateway:
|
|
|
2047
1941
|
size as inputs and handle progress bar
|
|
2048
1942
|
creation and update to give the user a
|
|
2049
1943
|
feedback on the download progress
|
|
1944
|
+
:param executor: (optional) An executor to download EO products of ``search_result`` in parallel
|
|
1945
|
+
which will also be reused to download assets of these products in parallel.
|
|
2050
1946
|
:param wait: (optional) If download fails, wait time in minutes between
|
|
2051
1947
|
two download tries of the same product
|
|
2052
1948
|
:param timeout: (optional) If download fails, maximum time in minutes
|
|
@@ -2067,8 +1963,7 @@ class EODataAccessGateway:
|
|
|
2067
1963
|
paths = []
|
|
2068
1964
|
if search_result:
|
|
2069
1965
|
logger.info("Downloading %s products", len(search_result))
|
|
2070
|
-
# Get download plugin using first product assuming
|
|
2071
|
-
# aren't mixed into a search result
|
|
1966
|
+
# Get download plugin using first product assuming all plugins use base.Download.download_all
|
|
2072
1967
|
download_plugin = self._plugins_manager.get_download_plugin(
|
|
2073
1968
|
search_result[0]
|
|
2074
1969
|
)
|
|
@@ -2076,6 +1971,7 @@ class EODataAccessGateway:
|
|
|
2076
1971
|
search_result,
|
|
2077
1972
|
downloaded_callback=downloaded_callback,
|
|
2078
1973
|
progress_callback=progress_callback,
|
|
1974
|
+
executor=executor,
|
|
2079
1975
|
wait=wait,
|
|
2080
1976
|
timeout=timeout,
|
|
2081
1977
|
**kwargs,
|
|
@@ -2137,6 +2033,7 @@ class EODataAccessGateway:
|
|
|
2137
2033
|
self,
|
|
2138
2034
|
product: EOProduct,
|
|
2139
2035
|
progress_callback: Optional[ProgressCallback] = None,
|
|
2036
|
+
executor: Optional[ThreadPoolExecutor] = None,
|
|
2140
2037
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
2141
2038
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
2142
2039
|
**kwargs: Unpack[DownloadConf],
|
|
@@ -2167,6 +2064,8 @@ class EODataAccessGateway:
|
|
|
2167
2064
|
size as inputs and handle progress bar
|
|
2168
2065
|
creation and update to give the user a
|
|
2169
2066
|
feedback on the download progress
|
|
2067
|
+
:param executor: (optional) An executor to download assets of ``product`` in parallel if it has any. If ``None``
|
|
2068
|
+
, a default executor will be created
|
|
2170
2069
|
:param wait: (optional) If download fails, wait time in minutes between
|
|
2171
2070
|
two download tries
|
|
2172
2071
|
:param timeout: (optional) If download fails, maximum time in minutes
|
|
@@ -2191,7 +2090,11 @@ class EODataAccessGateway:
|
|
|
2191
2090
|
return uri_to_path(product.location)
|
|
2192
2091
|
self._setup_downloader(product)
|
|
2193
2092
|
path = product.download(
|
|
2194
|
-
progress_callback=progress_callback,
|
|
2093
|
+
progress_callback=progress_callback,
|
|
2094
|
+
executor=executor,
|
|
2095
|
+
wait=wait,
|
|
2096
|
+
timeout=timeout,
|
|
2097
|
+
**kwargs,
|
|
2195
2098
|
)
|
|
2196
2099
|
|
|
2197
2100
|
return path
|