eodag 3.0.1__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 +164 -127
- eodag/api/product/_assets.py +11 -11
- eodag/api/product/_product.py +45 -30
- 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 +101 -85
- eodag/api/search_result.py +13 -23
- eodag/cli.py +26 -5
- eodag/config.py +78 -81
- eodag/plugins/apis/base.py +1 -1
- eodag/plugins/apis/ecmwf.py +46 -22
- eodag/plugins/apis/usgs.py +16 -15
- 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 +16 -16
- eodag/plugins/authentication/sas_auth.py +4 -4
- eodag/plugins/authentication/token.py +41 -10
- 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 +6 -7
- eodag/plugins/download/aws.py +58 -78
- eodag/plugins/download/base.py +38 -56
- eodag/plugins/download/creodias_s3.py +29 -0
- eodag/plugins/download/http.py +173 -183
- eodag/plugins/download/s3rest.py +10 -11
- eodag/plugins/manager.py +10 -20
- eodag/plugins/search/__init__.py +6 -5
- eodag/plugins/search/base.py +87 -44
- eodag/plugins/search/build_search_result.py +1067 -329
- eodag/plugins/search/cop_marine.py +22 -12
- eodag/plugins/search/creodias_s3.py +9 -73
- eodag/plugins/search/csw.py +11 -11
- eodag/plugins/search/data_request_search.py +16 -15
- eodag/plugins/search/qssearch.py +103 -187
- 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 +663 -304
- eodag/resources/providers.yml +823 -1749
- 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 +112 -82
- eodag/rest/errors.py +5 -5
- eodag/rest/server.py +33 -14
- eodag/rest/stac.py +40 -38
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +29 -23
- eodag/rest/types/queryables.py +15 -16
- eodag/rest/types/stac_search.py +15 -25
- eodag/rest/utils/__init__.py +14 -21
- eodag/rest/utils/cql_evaluate.py +6 -6
- eodag/rest/utils/rfc3339.py +2 -2
- eodag/types/__init__.py +75 -28
- eodag/types/bbox.py +2 -2
- eodag/types/download_args.py +3 -3
- eodag/types/queryables.py +183 -72
- eodag/types/search_args.py +4 -4
- eodag/types/whoosh.py +127 -3
- eodag/utils/__init__.py +152 -50
- eodag/utils/exceptions.py +28 -21
- eodag/utils/import_system.py +2 -2
- eodag/utils/repr.py +65 -6
- eodag/utils/requests.py +13 -13
- eodag/utils/rest.py +2 -2
- eodag/utils/s3.py +208 -0
- eodag/utils/stac_reader.py +10 -10
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/METADATA +77 -76
- eodag-3.1.0b2.dist-info/RECORD +113 -0
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/WHEEL +1 -1
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/entry_points.txt +4 -2
- eodag/utils/constraints.py +0 -244
- eodag-3.0.1.dist-info/RECORD +0 -109
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/LICENSE +0 -0
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/top_level.txt +0 -0
eodag/utils/requests.py
CHANGED
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
|
-
from typing import Any, Optional
|
|
22
|
+
from typing import Any, Optional
|
|
23
23
|
|
|
24
24
|
import requests
|
|
25
25
|
|
|
@@ -30,7 +30,7 @@ logger = logging.getLogger("eodag.utils.requests")
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def fetch_json(
|
|
33
|
-
|
|
33
|
+
url: str,
|
|
34
34
|
req_session: Optional[requests.Session] = None,
|
|
35
35
|
auth: Optional[requests.auth.AuthBase] = None,
|
|
36
36
|
timeout: float = HTTP_REQ_TIMEOUT,
|
|
@@ -38,32 +38,32 @@ def fetch_json(
|
|
|
38
38
|
"""
|
|
39
39
|
Fetches http/distant or local json file
|
|
40
40
|
|
|
41
|
-
:param
|
|
41
|
+
:param url: url from which the file can be fetched
|
|
42
42
|
:param req_session: (optional) requests session
|
|
43
43
|
:param auth: (optional) authenticated object if request needs authentication
|
|
44
44
|
:param timeout: (optional) authenticated object
|
|
45
45
|
:returns: json file content
|
|
46
46
|
"""
|
|
47
47
|
if req_session is None:
|
|
48
|
-
req_session = requests.Session()
|
|
48
|
+
req_session = requests.sessions.Session()
|
|
49
49
|
try:
|
|
50
|
-
if not
|
|
51
|
-
|
|
50
|
+
if not url.lower().startswith("http"):
|
|
51
|
+
url = path_to_uri(os.path.abspath(url))
|
|
52
52
|
req_session.mount("file://", LocalFileAdapter())
|
|
53
53
|
|
|
54
54
|
headers = USER_AGENT
|
|
55
|
-
logger.debug(f"fetching {
|
|
55
|
+
logger.debug(f"fetching {url}")
|
|
56
56
|
res = req_session.get(
|
|
57
|
-
|
|
57
|
+
url,
|
|
58
58
|
headers=headers,
|
|
59
59
|
auth=auth,
|
|
60
60
|
timeout=timeout,
|
|
61
61
|
)
|
|
62
62
|
res.raise_for_status()
|
|
63
63
|
except requests.exceptions.Timeout as exc:
|
|
64
|
-
raise TimeOutError(exc, timeout=
|
|
64
|
+
raise TimeOutError(exc, timeout=timeout) from exc
|
|
65
65
|
except requests.exceptions.RequestException as exc:
|
|
66
|
-
raise RequestError.from_error(exc, f"Unable to fetch {
|
|
66
|
+
raise RequestError.from_error(exc, f"Unable to fetch {url}") from exc
|
|
67
67
|
else:
|
|
68
68
|
return res.json()
|
|
69
69
|
|
|
@@ -75,7 +75,7 @@ class LocalFileAdapter(requests.adapters.BaseAdapter):
|
|
|
75
75
|
"""
|
|
76
76
|
|
|
77
77
|
@staticmethod
|
|
78
|
-
def _chkpath(method: str, path: str) ->
|
|
78
|
+
def _chkpath(method: str, path: str) -> tuple[int, str]:
|
|
79
79
|
"""Return an HTTP status for the given filesystem path.
|
|
80
80
|
|
|
81
81
|
:param method: method of the request
|
|
@@ -100,8 +100,8 @@ class LocalFileAdapter(requests.adapters.BaseAdapter):
|
|
|
100
100
|
) -> requests.Response:
|
|
101
101
|
"""Wraps a file, described in request, in a Response object.
|
|
102
102
|
|
|
103
|
-
:param
|
|
104
|
-
:param kwargs: (not used)
|
|
103
|
+
:param request: The PreparedRequest being "sent".
|
|
104
|
+
:param kwargs: (not used) additional arguments of the request
|
|
105
105
|
:returns: a Response object containing the file
|
|
106
106
|
"""
|
|
107
107
|
response = requests.Response()
|
eodag/utils/rest.py
CHANGED
|
@@ -21,7 +21,7 @@ from __future__ import annotations
|
|
|
21
21
|
|
|
22
22
|
import datetime
|
|
23
23
|
import re
|
|
24
|
-
from typing import Any,
|
|
24
|
+
from typing import Any, Optional
|
|
25
25
|
|
|
26
26
|
import dateutil.parser
|
|
27
27
|
from dateutil import tz
|
|
@@ -35,7 +35,7 @@ RFC3339_PATTERN = (
|
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def get_datetime(arguments:
|
|
38
|
+
def get_datetime(arguments: dict[str, Any]) -> tuple[Optional[str], Optional[str]]:
|
|
39
39
|
"""Get start and end dates from a dict containing `/` separated dates in `datetime` item
|
|
40
40
|
|
|
41
41
|
:param arguments: dict containing a single date or `/` separated dates in `datetime` item
|
eodag/utils/s3.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright 2024, CS GROUP - France, https://www.csgroup.eu/
|
|
3
|
+
#
|
|
4
|
+
# This file is part of EODAG project
|
|
5
|
+
# https://www.github.com/CS-SI/EODAG
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import io
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import zipfile
|
|
24
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
25
|
+
from urllib.parse import urlparse
|
|
26
|
+
|
|
27
|
+
import boto3
|
|
28
|
+
import botocore
|
|
29
|
+
|
|
30
|
+
from eodag.plugins.authentication.aws_auth import AwsAuth
|
|
31
|
+
from eodag.utils import get_bucket_name_and_prefix, guess_file_type
|
|
32
|
+
from eodag.utils.exceptions import (
|
|
33
|
+
AuthenticationError,
|
|
34
|
+
MisconfiguredError,
|
|
35
|
+
NotAvailableError,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from zipfile import ZipInfo
|
|
40
|
+
|
|
41
|
+
from mypy_boto3_s3.client import S3Client
|
|
42
|
+
|
|
43
|
+
from eodag.api.product import EOProduct # type: ignore
|
|
44
|
+
|
|
45
|
+
logger = logging.getLogger("eodag.utils.s3")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def fetch(
|
|
49
|
+
bucket_name: str, key_name: str, start: int, len: int, client_s3: S3Client
|
|
50
|
+
) -> bytes:
|
|
51
|
+
"""
|
|
52
|
+
Range-fetches a S3 key.
|
|
53
|
+
|
|
54
|
+
:param bucket_name: Bucket name of the object to fetch
|
|
55
|
+
:param key_name: Key name of the object to fetch
|
|
56
|
+
:param start: Bucket name to fetch
|
|
57
|
+
:param len: Bucket name to fetch
|
|
58
|
+
:param client_s3: s3 client used to fetch the object
|
|
59
|
+
:returns: Object bytes
|
|
60
|
+
"""
|
|
61
|
+
end = start + len - 1
|
|
62
|
+
s3_object = client_s3.get_object(
|
|
63
|
+
Bucket=bucket_name, Key=key_name, Range="bytes=%d-%d" % (start, end)
|
|
64
|
+
)
|
|
65
|
+
return s3_object["Body"].read()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def parse_int(bytes: bytes) -> int:
|
|
69
|
+
"""
|
|
70
|
+
Parses 2 or 4 little-endian bits into their corresponding integer value.
|
|
71
|
+
|
|
72
|
+
:param bytes: bytes to parse
|
|
73
|
+
:returns: parsed int
|
|
74
|
+
"""
|
|
75
|
+
val = (bytes[0]) + ((bytes[1]) << 8)
|
|
76
|
+
if len(bytes) > 3:
|
|
77
|
+
val += ((bytes[2]) << 16) + ((bytes[3]) << 24)
|
|
78
|
+
return val
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def list_files_in_s3_zipped_object(
|
|
82
|
+
bucket_name: str, key_name: str, client_s3: S3Client
|
|
83
|
+
) -> List[ZipInfo]:
|
|
84
|
+
"""
|
|
85
|
+
List files in s3 zipped object, without downloading it.
|
|
86
|
+
|
|
87
|
+
See https://stackoverflow.com/questions/41789176/how-to-count-files-inside-zip-in-aws-s3-without-downloading-it;
|
|
88
|
+
Based on https://stackoverflow.com/questions/51351000/read-zip-files-from-s3-without-downloading-the-entire-file
|
|
89
|
+
|
|
90
|
+
:param bucket_name: Bucket name of the object to fetch
|
|
91
|
+
:param key_name: Key name of the object to fetch
|
|
92
|
+
:param client_s3: s3 client used to fetch the object
|
|
93
|
+
:returns: List of files in zip
|
|
94
|
+
"""
|
|
95
|
+
response = client_s3.head_object(Bucket=bucket_name, Key=key_name)
|
|
96
|
+
size = response["ContentLength"]
|
|
97
|
+
|
|
98
|
+
# End Of Central Directory bytes
|
|
99
|
+
eocd = fetch(bucket_name, key_name, size - 22, 22, client_s3)
|
|
100
|
+
|
|
101
|
+
# start offset and size of the central directory
|
|
102
|
+
cd_start = parse_int(eocd[16:20])
|
|
103
|
+
cd_size = parse_int(eocd[12:16])
|
|
104
|
+
|
|
105
|
+
# fetch central directory, append EOCD, and open as zipfile
|
|
106
|
+
cd = fetch(bucket_name, key_name, cd_start, cd_size, client_s3)
|
|
107
|
+
zip = zipfile.ZipFile(io.BytesIO(cd + eocd))
|
|
108
|
+
|
|
109
|
+
logger.debug("Found %s files in %s" % (len(zip.filelist), key_name))
|
|
110
|
+
|
|
111
|
+
return zip.filelist
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def update_assets_from_s3(
|
|
115
|
+
product: EOProduct,
|
|
116
|
+
auth: AwsAuth,
|
|
117
|
+
s3_endpoint: Optional[str] = None,
|
|
118
|
+
content_url: Optional[str] = None,
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Update ``EOProduct.assets`` using content listed in its ``remote_location`` or given
|
|
121
|
+
``content_url``.
|
|
122
|
+
|
|
123
|
+
If url points to a zipped archive, its content will also be be listed.
|
|
124
|
+
|
|
125
|
+
:param product: product to update
|
|
126
|
+
:param auth: Authentication plugin
|
|
127
|
+
:param s3_endpoint: s3 endpoint if not hosted on AWS
|
|
128
|
+
:param content_url: s3 URL pointing to the content that must be listed (defaults to
|
|
129
|
+
``product.remote_location`` if empty)
|
|
130
|
+
"""
|
|
131
|
+
required_creds = ["aws_access_key_id", "aws_secret_access_key"]
|
|
132
|
+
|
|
133
|
+
if content_url is None:
|
|
134
|
+
content_url = product.remote_location
|
|
135
|
+
|
|
136
|
+
bucket, prefix = get_bucket_name_and_prefix(content_url)
|
|
137
|
+
|
|
138
|
+
if bucket is None or prefix is None:
|
|
139
|
+
logger.debug(f"No s3 prefix could guessed from {content_url}")
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
auth_dict = auth.authenticate()
|
|
144
|
+
|
|
145
|
+
if not all(x in auth_dict for x in required_creds):
|
|
146
|
+
raise MisconfiguredError(
|
|
147
|
+
f"Incomplete credentials for {product.provider}, missing "
|
|
148
|
+
f"{[x for x in required_creds if x not in auth_dict]}"
|
|
149
|
+
)
|
|
150
|
+
if not getattr(auth, "s3_client", None):
|
|
151
|
+
auth.s3_client = boto3.client(
|
|
152
|
+
service_name="s3",
|
|
153
|
+
endpoint_url=s3_endpoint,
|
|
154
|
+
aws_access_key_id=auth_dict.get("aws_access_key_id"),
|
|
155
|
+
aws_secret_access_key=auth_dict.get("aws_secret_access_key"),
|
|
156
|
+
aws_session_token=auth_dict.get("aws_session_token"),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
logger.debug("Listing assets in %s", prefix)
|
|
160
|
+
|
|
161
|
+
if prefix.endswith(".zip"):
|
|
162
|
+
# List prefix zip content
|
|
163
|
+
assets_urls = [
|
|
164
|
+
f"zip+s3://{bucket}/{prefix}!{f.filename}"
|
|
165
|
+
for f in list_files_in_s3_zipped_object(bucket, prefix, auth.s3_client)
|
|
166
|
+
]
|
|
167
|
+
else:
|
|
168
|
+
# List files in prefix
|
|
169
|
+
assets_urls = [
|
|
170
|
+
f"s3://{bucket}/{obj['Key']}"
|
|
171
|
+
for obj in auth.s3_client.list_objects(
|
|
172
|
+
Bucket=bucket, Prefix=prefix, MaxKeys=300
|
|
173
|
+
).get("Contents", [])
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
for asset_url in assets_urls:
|
|
177
|
+
out_of_zip_url = asset_url.split("!")[-1]
|
|
178
|
+
key, roles = product.driver.guess_asset_key_and_roles(
|
|
179
|
+
out_of_zip_url, product
|
|
180
|
+
)
|
|
181
|
+
parsed_url = urlparse(out_of_zip_url)
|
|
182
|
+
title = os.path.basename(parsed_url.path)
|
|
183
|
+
|
|
184
|
+
if key and key not in product.assets:
|
|
185
|
+
product.assets[key] = {
|
|
186
|
+
"title": title,
|
|
187
|
+
"roles": roles,
|
|
188
|
+
"href": asset_url,
|
|
189
|
+
}
|
|
190
|
+
if mime_type := guess_file_type(asset_url):
|
|
191
|
+
product.assets[key]["type"] = mime_type
|
|
192
|
+
|
|
193
|
+
# sort assets
|
|
194
|
+
product.assets.data = dict(sorted(product.assets.data.items()))
|
|
195
|
+
|
|
196
|
+
# update driver
|
|
197
|
+
product.driver = product.get_driver()
|
|
198
|
+
|
|
199
|
+
except botocore.exceptions.ClientError as e:
|
|
200
|
+
if hasattr(auth.config, "auth_error_code") and str(
|
|
201
|
+
auth.config.auth_error_code
|
|
202
|
+
) in str(e):
|
|
203
|
+
raise AuthenticationError(
|
|
204
|
+
f"Authentication failed on {s3_endpoint} s3"
|
|
205
|
+
) from e
|
|
206
|
+
raise NotAvailableError(
|
|
207
|
+
f"assets for product {prefix} could not be found"
|
|
208
|
+
) from e
|
eodag/utils/stac_reader.py
CHANGED
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import logging
|
|
21
21
|
import re
|
|
22
22
|
import socket
|
|
23
|
-
from typing import Any, Callable,
|
|
23
|
+
from typing import Any, Callable, Optional, Union
|
|
24
24
|
from urllib.error import URLError
|
|
25
25
|
from urllib.request import urlopen
|
|
26
26
|
|
|
@@ -108,7 +108,7 @@ def fetch_stac_items(
|
|
|
108
108
|
max_connections: int = 100,
|
|
109
109
|
timeout: int = HTTP_REQ_TIMEOUT,
|
|
110
110
|
ssl_verify: bool = True,
|
|
111
|
-
) ->
|
|
111
|
+
) -> list[dict[str, Any]]:
|
|
112
112
|
"""Fetch STAC item from a single item file or items from a catalog.
|
|
113
113
|
|
|
114
114
|
:param stac_path: A STAC object filepath
|
|
@@ -142,13 +142,13 @@ def _fetch_stac_items_from_catalog(
|
|
|
142
142
|
recursive: bool,
|
|
143
143
|
max_connections: int,
|
|
144
144
|
_text_opener: Callable[[str, bool], Any],
|
|
145
|
-
) ->
|
|
145
|
+
) -> list[Any]:
|
|
146
146
|
"""Fetch items from a STAC catalog"""
|
|
147
|
-
items:
|
|
147
|
+
items: list[dict[Any, Any]] = []
|
|
148
148
|
|
|
149
149
|
# pystac cannot yet return links from a single file catalog, see:
|
|
150
150
|
# https://github.com/stac-utils/pystac/issues/256
|
|
151
|
-
extensions: Optional[Union[
|
|
151
|
+
extensions: Optional[Union[list[str], str]] = getattr(cat, "stac_extensions", None)
|
|
152
152
|
if extensions:
|
|
153
153
|
extensions = extensions if isinstance(extensions, list) else [extensions]
|
|
154
154
|
if "single-file-stac" in extensions:
|
|
@@ -157,7 +157,7 @@ def _fetch_stac_items_from_catalog(
|
|
|
157
157
|
|
|
158
158
|
# Making the links absolutes allow for both relative and absolute links to be handled.
|
|
159
159
|
if not recursive:
|
|
160
|
-
hrefs:
|
|
160
|
+
hrefs: list[Optional[str]] = [
|
|
161
161
|
link.get_absolute_href() for link in cat.get_item_links()
|
|
162
162
|
]
|
|
163
163
|
else:
|
|
@@ -188,7 +188,7 @@ def fetch_stac_collections(
|
|
|
188
188
|
max_connections: int = 100,
|
|
189
189
|
timeout: int = HTTP_REQ_TIMEOUT,
|
|
190
190
|
ssl_verify: bool = True,
|
|
191
|
-
) ->
|
|
191
|
+
) -> list[dict[str, Any]]:
|
|
192
192
|
"""Fetch STAC collection(s) from a catalog.
|
|
193
193
|
|
|
194
194
|
:param stac_path: A STAC object filepath
|
|
@@ -217,12 +217,12 @@ def _fetch_stac_collections_from_catalog(
|
|
|
217
217
|
collection: Optional[str],
|
|
218
218
|
max_connections: int,
|
|
219
219
|
_text_opener: Callable[[str, bool], Any],
|
|
220
|
-
) ->
|
|
220
|
+
) -> list[Any]:
|
|
221
221
|
"""Fetch collections from a STAC catalog"""
|
|
222
|
-
collections:
|
|
222
|
+
collections: list[dict[Any, Any]] = []
|
|
223
223
|
|
|
224
224
|
# Making the links absolutes allow for both relative and absolute links to be handled.
|
|
225
|
-
hrefs:
|
|
225
|
+
hrefs: list[Optional[str]] = [
|
|
226
226
|
link.get_absolute_href()
|
|
227
227
|
for link in cat.get_child_links()
|
|
228
228
|
if collection is not None and link.title == collection
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: eodag
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0b2
|
|
4
4
|
Summary: Earth Observation Data Access Gateway
|
|
5
5
|
Home-page: https://github.com/CS-SI/eodag
|
|
6
6
|
Author: CS GROUP - France
|
|
@@ -36,95 +36,96 @@ Requires-Dist: boto3
|
|
|
36
36
|
Requires-Dist: botocore
|
|
37
37
|
Requires-Dist: click
|
|
38
38
|
Requires-Dist: geojson
|
|
39
|
-
Requires-Dist: jsonpath-ng
|
|
39
|
+
Requires-Dist: jsonpath-ng<1.6.0
|
|
40
40
|
Requires-Dist: lxml
|
|
41
|
-
Requires-Dist:
|
|
42
|
-
Requires-Dist:
|
|
43
|
-
Requires-Dist:
|
|
44
|
-
Requires-Dist:
|
|
41
|
+
Requires-Dist: orjson<3.10.0; python_version >= "3.12" and platform_system == "Windows"
|
|
42
|
+
Requires-Dist: orjson; python_version < "3.12" or platform_system != "Windows"
|
|
43
|
+
Requires-Dist: pydantic!=2.10.0,>=2.1.0
|
|
44
|
+
Requires-Dist: pydantic_core
|
|
45
|
+
Requires-Dist: PyJWT[crypto]>=2.5.0
|
|
46
|
+
Requires-Dist: pyproj>=2.1.0
|
|
45
47
|
Requires-Dist: pyshp
|
|
46
|
-
Requires-Dist: pystac
|
|
48
|
+
Requires-Dist: pystac>=1.0.0b1
|
|
47
49
|
Requires-Dist: python-dateutil
|
|
48
50
|
Requires-Dist: PyYAML
|
|
49
51
|
Requires-Dist: requests
|
|
50
52
|
Requires-Dist: setuptools
|
|
51
|
-
Requires-Dist: shapely
|
|
53
|
+
Requires-Dist: shapely>=2.0.6
|
|
52
54
|
Requires-Dist: stream-zip
|
|
53
55
|
Requires-Dist: tqdm
|
|
54
|
-
Requires-Dist:
|
|
56
|
+
Requires-Dist: typing_extensions>=4.8.0
|
|
55
57
|
Requires-Dist: urllib3
|
|
56
58
|
Requires-Dist: Whoosh
|
|
57
|
-
Requires-Dist: orjson ; python_version < "3.12" or platform_system != "Windows"
|
|
58
|
-
Requires-Dist: orjson <3.10.0 ; python_version >= "3.12" and platform_system == "Windows"
|
|
59
59
|
Provides-Extra: all
|
|
60
|
-
Requires-Dist: eodag[all-providers,csw,server,tutorials]
|
|
60
|
+
Requires-Dist: eodag[all-providers,csw,server,tutorials]; extra == "all"
|
|
61
61
|
Provides-Extra: all-providers
|
|
62
|
-
Requires-Dist: eodag[ecmwf,usgs]
|
|
62
|
+
Requires-Dist: eodag[ecmwf,usgs]; extra == "all-providers"
|
|
63
63
|
Provides-Extra: csw
|
|
64
|
-
Requires-Dist: OWSLib
|
|
65
|
-
Provides-Extra: dev
|
|
66
|
-
Requires-Dist: eodag[all-providers,csw,server,stubs] ; extra == 'dev'
|
|
67
|
-
Requires-Dist: pytest ; extra == 'dev'
|
|
68
|
-
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
69
|
-
Requires-Dist: py >=1.8.2 ; extra == 'dev'
|
|
70
|
-
Requires-Dist: pytest-html <3.2.0 ; extra == 'dev'
|
|
71
|
-
Requires-Dist: pytest-xdist ; extra == 'dev'
|
|
72
|
-
Requires-Dist: pytest-socket ; extra == 'dev'
|
|
73
|
-
Requires-Dist: pytest-instafail ; extra == 'dev'
|
|
74
|
-
Requires-Dist: tox ; extra == 'dev'
|
|
75
|
-
Requires-Dist: tox-uv ; extra == 'dev'
|
|
76
|
-
Requires-Dist: faker ; extra == 'dev'
|
|
77
|
-
Requires-Dist: moto ; extra == 'dev'
|
|
78
|
-
Requires-Dist: twine ; extra == 'dev'
|
|
79
|
-
Requires-Dist: wheel ; extra == 'dev'
|
|
80
|
-
Requires-Dist: flake8 ; extra == 'dev'
|
|
81
|
-
Requires-Dist: pre-commit ; extra == 'dev'
|
|
82
|
-
Requires-Dist: responses <0.24.0 ; extra == 'dev'
|
|
83
|
-
Requires-Dist: fastapi[all] ; extra == 'dev'
|
|
84
|
-
Requires-Dist: stdlib-list ; extra == 'dev'
|
|
85
|
-
Requires-Dist: mypy ; extra == 'dev'
|
|
86
|
-
Provides-Extra: docs
|
|
87
|
-
Requires-Dist: eodag[all,stubs] ; extra == 'docs'
|
|
88
|
-
Requires-Dist: sphinx ; extra == 'docs'
|
|
89
|
-
Requires-Dist: sphinx-book-theme >=1.0.0 ; extra == 'docs'
|
|
90
|
-
Requires-Dist: sphinx-copybutton ; extra == 'docs'
|
|
91
|
-
Requires-Dist: sphinx-tabs ; extra == 'docs'
|
|
92
|
-
Requires-Dist: nbsphinx ; extra == 'docs'
|
|
93
|
-
Requires-Dist: sphinx-autodoc-typehints ; extra == 'docs'
|
|
94
|
-
Requires-Dist: sphinxemoji ; extra == 'docs'
|
|
64
|
+
Requires-Dist: OWSLib>=0.27.1; extra == "csw"
|
|
95
65
|
Provides-Extra: ecmwf
|
|
96
|
-
Requires-Dist: ecmwf-api-client
|
|
97
|
-
Provides-Extra:
|
|
98
|
-
Requires-Dist:
|
|
66
|
+
Requires-Dist: ecmwf-api-client; extra == "ecmwf"
|
|
67
|
+
Provides-Extra: usgs
|
|
68
|
+
Requires-Dist: usgs>=0.3.1; extra == "usgs"
|
|
99
69
|
Provides-Extra: server
|
|
100
|
-
Requires-Dist: fastapi
|
|
101
|
-
Requires-Dist: pygeofilter
|
|
102
|
-
Requires-Dist: starlette
|
|
103
|
-
Requires-Dist: uvicorn[standard]
|
|
104
|
-
Requires-Dist: pydantic-settings
|
|
105
|
-
Requires-Dist: cachetools
|
|
106
|
-
Provides-Extra:
|
|
107
|
-
Requires-Dist:
|
|
108
|
-
Requires-Dist: types-lxml ; extra == 'stubs'
|
|
109
|
-
Requires-Dist: types-cachetools ; extra == 'stubs'
|
|
110
|
-
Requires-Dist: types-requests ; extra == 'stubs'
|
|
111
|
-
Requires-Dist: types-python-dateutil ; extra == 'stubs'
|
|
112
|
-
Requires-Dist: types-setuptools ; extra == 'stubs'
|
|
113
|
-
Requires-Dist: types-tqdm ; extra == 'stubs'
|
|
114
|
-
Requires-Dist: types-urllib3 ; extra == 'stubs'
|
|
70
|
+
Requires-Dist: fastapi>=0.93.0; extra == "server"
|
|
71
|
+
Requires-Dist: pygeofilter; extra == "server"
|
|
72
|
+
Requires-Dist: starlette; extra == "server"
|
|
73
|
+
Requires-Dist: uvicorn[standard]; extra == "server"
|
|
74
|
+
Requires-Dist: pydantic-settings; extra == "server"
|
|
75
|
+
Requires-Dist: cachetools; extra == "server"
|
|
76
|
+
Provides-Extra: notebook
|
|
77
|
+
Requires-Dist: tqdm[notebook]; extra == "notebook"
|
|
115
78
|
Provides-Extra: tutorials
|
|
116
|
-
Requires-Dist: eodag[ecmwf,notebook]
|
|
117
|
-
Requires-Dist: eodag-cube
|
|
118
|
-
Requires-Dist: jupyter
|
|
119
|
-
Requires-Dist: ipyleaflet
|
|
120
|
-
Requires-Dist: ipywidgets
|
|
121
|
-
Requires-Dist: matplotlib
|
|
122
|
-
Requires-Dist: folium
|
|
123
|
-
Requires-Dist: imageio
|
|
124
|
-
Requires-Dist: rasterio
|
|
125
|
-
Requires-Dist: netcdf4
|
|
126
|
-
|
|
127
|
-
|
|
79
|
+
Requires-Dist: eodag[ecmwf,notebook]; extra == "tutorials"
|
|
80
|
+
Requires-Dist: eodag-cube>=0.6.0b1; extra == "tutorials"
|
|
81
|
+
Requires-Dist: jupyter; extra == "tutorials"
|
|
82
|
+
Requires-Dist: ipyleaflet>=0.10.0; extra == "tutorials"
|
|
83
|
+
Requires-Dist: ipywidgets; extra == "tutorials"
|
|
84
|
+
Requires-Dist: matplotlib; extra == "tutorials"
|
|
85
|
+
Requires-Dist: folium; extra == "tutorials"
|
|
86
|
+
Requires-Dist: imageio; extra == "tutorials"
|
|
87
|
+
Requires-Dist: rasterio; extra == "tutorials"
|
|
88
|
+
Requires-Dist: netcdf4; extra == "tutorials"
|
|
89
|
+
Requires-Dist: cartopy; extra == "tutorials"
|
|
90
|
+
Provides-Extra: dev
|
|
91
|
+
Requires-Dist: eodag[all-providers,csw,server,stubs]; extra == "dev"
|
|
92
|
+
Requires-Dist: pytest; extra == "dev"
|
|
93
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
94
|
+
Requires-Dist: py>=1.8.2; extra == "dev"
|
|
95
|
+
Requires-Dist: pytest-html<3.2.0; extra == "dev"
|
|
96
|
+
Requires-Dist: pytest-xdist; extra == "dev"
|
|
97
|
+
Requires-Dist: pytest-socket; extra == "dev"
|
|
98
|
+
Requires-Dist: pytest-instafail; extra == "dev"
|
|
99
|
+
Requires-Dist: tox; extra == "dev"
|
|
100
|
+
Requires-Dist: tox-uv; extra == "dev"
|
|
101
|
+
Requires-Dist: faker; extra == "dev"
|
|
102
|
+
Requires-Dist: moto>=5; extra == "dev"
|
|
103
|
+
Requires-Dist: twine; extra == "dev"
|
|
104
|
+
Requires-Dist: wheel; extra == "dev"
|
|
105
|
+
Requires-Dist: flake8; extra == "dev"
|
|
106
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
107
|
+
Requires-Dist: responses<0.24.0; extra == "dev"
|
|
108
|
+
Requires-Dist: fastapi[all]; extra == "dev"
|
|
109
|
+
Requires-Dist: stdlib-list; extra == "dev"
|
|
110
|
+
Requires-Dist: mypy; extra == "dev"
|
|
111
|
+
Provides-Extra: stubs
|
|
112
|
+
Requires-Dist: boto3-stubs[essential]; extra == "stubs"
|
|
113
|
+
Requires-Dist: types-lxml; extra == "stubs"
|
|
114
|
+
Requires-Dist: types-cachetools; extra == "stubs"
|
|
115
|
+
Requires-Dist: types-requests; extra == "stubs"
|
|
116
|
+
Requires-Dist: types-python-dateutil; extra == "stubs"
|
|
117
|
+
Requires-Dist: types-setuptools; extra == "stubs"
|
|
118
|
+
Requires-Dist: types-tqdm; extra == "stubs"
|
|
119
|
+
Requires-Dist: types-urllib3; extra == "stubs"
|
|
120
|
+
Provides-Extra: docs
|
|
121
|
+
Requires-Dist: eodag[all,stubs]; extra == "docs"
|
|
122
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
123
|
+
Requires-Dist: sphinx-book-theme>=1.0.0; extra == "docs"
|
|
124
|
+
Requires-Dist: sphinx-copybutton; extra == "docs"
|
|
125
|
+
Requires-Dist: sphinx-tabs; extra == "docs"
|
|
126
|
+
Requires-Dist: nbsphinx; extra == "docs"
|
|
127
|
+
Requires-Dist: sphinx_autodoc_typehints; extra == "docs"
|
|
128
|
+
Requires-Dist: sphinxemoji; extra == "docs"
|
|
128
129
|
|
|
129
130
|
.. image:: https://eodag.readthedocs.io/en/latest/_static/eodag_bycs.png
|
|
130
131
|
:target: https://github.com/CS-SI/eodag
|
|
@@ -315,7 +316,7 @@ An eodag instance can be exposed through a STAC compliant REST api from the comm
|
|
|
315
316
|
|
|
316
317
|
.. code-block:: bash
|
|
317
318
|
|
|
318
|
-
docker run -p 5000:5000 --rm csspace/eodag-server:3.
|
|
319
|
+
docker run -p 5000:5000 --rm csspace/eodag-server:3.1.0b2
|
|
319
320
|
|
|
320
321
|
You can also browse over your STAC API server using `STAC Browser <https://github.com/radiantearth/stac-browser>`_.
|
|
321
322
|
Simply run:
|