eodag 3.0.1__py3-none-any.whl → 3.1.0b1__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 +116 -86
- eodag/api/product/_assets.py +6 -6
- eodag/api/product/_product.py +18 -18
- eodag/api/product/metadata_mapping.py +39 -11
- eodag/cli.py +22 -1
- eodag/config.py +14 -14
- eodag/plugins/apis/ecmwf.py +37 -14
- eodag/plugins/apis/usgs.py +5 -5
- eodag/plugins/authentication/openid_connect.py +2 -2
- eodag/plugins/authentication/token.py +37 -6
- eodag/plugins/crunch/filter_property.py +2 -3
- eodag/plugins/download/aws.py +11 -12
- eodag/plugins/download/base.py +30 -39
- eodag/plugins/download/creodias_s3.py +29 -0
- eodag/plugins/download/http.py +144 -152
- eodag/plugins/download/s3rest.py +5 -7
- eodag/plugins/search/base.py +73 -25
- eodag/plugins/search/build_search_result.py +1047 -310
- eodag/plugins/search/creodias_s3.py +25 -19
- eodag/plugins/search/data_request_search.py +1 -1
- eodag/plugins/search/qssearch.py +51 -139
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +391 -32
- eodag/resources/providers.yml +678 -1744
- eodag/rest/core.py +92 -62
- eodag/rest/server.py +31 -4
- eodag/rest/types/eodag_search.py +6 -0
- eodag/rest/types/queryables.py +5 -6
- eodag/rest/utils/__init__.py +3 -0
- eodag/types/__init__.py +56 -15
- eodag/types/download_args.py +2 -2
- eodag/types/queryables.py +180 -72
- eodag/types/whoosh.py +126 -0
- eodag/utils/__init__.py +71 -10
- eodag/utils/exceptions.py +27 -20
- eodag/utils/repr.py +65 -6
- eodag/utils/requests.py +11 -11
- {eodag-3.0.1.dist-info → eodag-3.1.0b1.dist-info}/METADATA +76 -76
- {eodag-3.0.1.dist-info → eodag-3.1.0b1.dist-info}/RECORD +43 -44
- {eodag-3.0.1.dist-info → eodag-3.1.0b1.dist-info}/WHEEL +1 -1
- {eodag-3.0.1.dist-info → eodag-3.1.0b1.dist-info}/entry_points.txt +3 -2
- eodag/utils/constraints.py +0 -244
- {eodag-3.0.1.dist-info → eodag-3.1.0b1.dist-info}/LICENSE +0 -0
- {eodag-3.0.1.dist-info → eodag-3.1.0b1.dist-info}/top_level.txt +0 -0
eodag/rest/core.py
CHANGED
|
@@ -33,7 +33,6 @@ from requests.models import Response as RequestsResponse
|
|
|
33
33
|
import eodag
|
|
34
34
|
from eodag import EOProduct
|
|
35
35
|
from eodag.api.product.metadata_mapping import (
|
|
36
|
-
DEFAULT_METADATA_MAPPING,
|
|
37
36
|
NOT_AVAILABLE,
|
|
38
37
|
OFFLINE_STATUS,
|
|
39
38
|
ONLINE_STATUS,
|
|
@@ -54,11 +53,7 @@ from eodag.rest.constants import (
|
|
|
54
53
|
from eodag.rest.errors import ResponseSearchError
|
|
55
54
|
from eodag.rest.stac import StacCatalog, StacCollection, StacCommon, StacItem
|
|
56
55
|
from eodag.rest.types.eodag_search import EODAGSearch
|
|
57
|
-
from eodag.rest.types.queryables import
|
|
58
|
-
QueryablesGetParams,
|
|
59
|
-
StacQueryableProperty,
|
|
60
|
-
StacQueryables,
|
|
61
|
-
)
|
|
56
|
+
from eodag.rest.types.queryables import QueryablesGetParams, StacQueryables
|
|
62
57
|
from eodag.rest.types.stac_search import SearchPostRequest
|
|
63
58
|
from eodag.rest.utils import (
|
|
64
59
|
Cruncher,
|
|
@@ -193,28 +188,18 @@ def search_stac_items(
|
|
|
193
188
|
results.number_matched = len(results)
|
|
194
189
|
total = len(results)
|
|
195
190
|
|
|
196
|
-
|
|
197
|
-
criteria =
|
|
198
|
-
**catalog.search_args,
|
|
199
|
-
**eodag_args.model_dump(exclude_none=True),
|
|
200
|
-
}
|
|
191
|
+
else:
|
|
192
|
+
criteria = eodag_args.model_dump(exclude_none=True)
|
|
201
193
|
# remove provider prefixes
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
for key in criteria:
|
|
194
|
+
# quickfix for ecmwf fake extension to not impact items creation
|
|
195
|
+
stac_extensions = list(stac_config["extensions"].keys()) + ["ecmwf"]
|
|
196
|
+
for key in list(criteria):
|
|
205
197
|
if ":" in key and key.split(":")[0] not in stac_extensions:
|
|
206
198
|
new_key = key.split(":")[1]
|
|
207
|
-
|
|
208
|
-
for key, new_key in keys_to_update.items():
|
|
209
|
-
criteria[new_key] = criteria[key]
|
|
210
|
-
criteria.pop(key)
|
|
199
|
+
criteria[new_key] = criteria.pop(key)
|
|
211
200
|
|
|
212
201
|
results = eodag_api.search(count=True, **criteria)
|
|
213
202
|
total = results.number_matched or 0
|
|
214
|
-
else:
|
|
215
|
-
# return empty results
|
|
216
|
-
results = SearchResult([], 0)
|
|
217
|
-
total = 0
|
|
218
203
|
|
|
219
204
|
if len(results) == 0 and results.errors:
|
|
220
205
|
raise ResponseSearchError(results.errors)
|
|
@@ -342,13 +327,11 @@ def _order_and_update(
|
|
|
342
327
|
if (
|
|
343
328
|
product.properties.get("storageStatus") != ONLINE_STATUS
|
|
344
329
|
and NOT_AVAILABLE in product.properties.get("orderStatusLink", "")
|
|
345
|
-
and hasattr(product.downloader, "
|
|
330
|
+
and hasattr(product.downloader, "_order")
|
|
346
331
|
):
|
|
347
332
|
# first order
|
|
348
333
|
logger.debug("Order product")
|
|
349
|
-
order_status_dict = product.downloader.
|
|
350
|
-
product=product, auth=auth
|
|
351
|
-
)
|
|
334
|
+
order_status_dict = product.downloader._order(product=product, auth=auth)
|
|
352
335
|
query_args.update(order_status_dict or {})
|
|
353
336
|
|
|
354
337
|
if (
|
|
@@ -359,11 +342,11 @@ def _order_and_update(
|
|
|
359
342
|
product.properties["storageStatus"] = STAGING_STATUS
|
|
360
343
|
|
|
361
344
|
if product.properties.get("storageStatus") == STAGING_STATUS and hasattr(
|
|
362
|
-
product.downloader, "
|
|
345
|
+
product.downloader, "_order_status"
|
|
363
346
|
):
|
|
364
347
|
# check order status if needed
|
|
365
348
|
logger.debug("Checking product order status")
|
|
366
|
-
product.downloader.
|
|
349
|
+
product.downloader._order_status(product=product, auth=auth)
|
|
367
350
|
|
|
368
351
|
if product.properties.get("storageStatus") != ONLINE_STATUS:
|
|
369
352
|
raise NotAvailableError("Product is not available yet")
|
|
@@ -417,7 +400,10 @@ async def all_collections(
|
|
|
417
400
|
# # parse f-strings
|
|
418
401
|
format_args = deepcopy(stac_config)
|
|
419
402
|
format_args["collections"].update(
|
|
420
|
-
{
|
|
403
|
+
{
|
|
404
|
+
"url": stac_collection.url,
|
|
405
|
+
"root": stac_collection.root,
|
|
406
|
+
}
|
|
421
407
|
)
|
|
422
408
|
|
|
423
409
|
collections["links"] = [
|
|
@@ -495,7 +481,10 @@ def time_interval_overlap(eodag_args: EODAGSearch, catalog: StacCatalog) -> bool
|
|
|
495
481
|
# check if time filtering appears both in search arguments and catalog
|
|
496
482
|
# (for catalogs built by date: i.e. `year/2020/month/05`)
|
|
497
483
|
if not set(["start", "end"]) <= set(eodag_args.model_dump().keys()) or not set(
|
|
498
|
-
[
|
|
484
|
+
[
|
|
485
|
+
"start",
|
|
486
|
+
"end",
|
|
487
|
+
]
|
|
499
488
|
) <= set(catalog.search_args.keys()):
|
|
500
489
|
return True
|
|
501
490
|
|
|
@@ -591,51 +580,87 @@ async def get_queryables(
|
|
|
591
580
|
|
|
592
581
|
async def _fetch() -> Dict[str, Any]:
|
|
593
582
|
python_queryables = eodag_api.list_queryables(
|
|
594
|
-
provider=provider,
|
|
583
|
+
provider=provider,
|
|
584
|
+
fetch_providers=False,
|
|
585
|
+
**params.model_dump(exclude_none=True, by_alias=True),
|
|
595
586
|
)
|
|
596
|
-
python_queryables.pop("start")
|
|
597
|
-
python_queryables.pop("end")
|
|
598
587
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
python_queryables.pop("id", None)
|
|
602
|
-
|
|
603
|
-
stac_queryables: Dict[str, StacQueryableProperty] = deepcopy(
|
|
604
|
-
StacQueryables.default_properties
|
|
588
|
+
python_queryables_json = python_queryables.get_model().model_json_schema(
|
|
589
|
+
by_alias=True
|
|
605
590
|
)
|
|
591
|
+
|
|
592
|
+
properties: Dict[str, Any] = python_queryables_json["properties"]
|
|
593
|
+
required: List[str] = python_queryables_json.get("required") or []
|
|
594
|
+
|
|
595
|
+
# productType is either simply removed or replaced by collection later.
|
|
596
|
+
if "productType" in properties:
|
|
597
|
+
properties.pop("productType")
|
|
598
|
+
if "productType" in required:
|
|
599
|
+
required.remove("productType")
|
|
600
|
+
|
|
601
|
+
stac_properties: Dict[str, Any] = {}
|
|
602
|
+
|
|
606
603
|
# get stac default properties to set prefixes
|
|
607
604
|
stac_item_properties = list(stac_config["item"]["properties"].values())
|
|
608
|
-
stac_item_properties.extend(
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
for param, queryable in python_queryables.items():
|
|
613
|
-
if param in default_mapping and not any(
|
|
605
|
+
stac_item_properties.extend(stac_config["metadata_ignore"])
|
|
606
|
+
for param, queryable in properties.items():
|
|
607
|
+
# convert key to STAC format
|
|
608
|
+
if param in OSEO_METADATA_MAPPING.keys() and not any(
|
|
614
609
|
param in str(prop) for prop in stac_item_properties
|
|
615
610
|
):
|
|
616
611
|
param = f"oseo:{param}"
|
|
617
612
|
stac_param = EODAGSearch.to_stac(param, stac_item_properties, provider)
|
|
618
|
-
# only keep "datetime" queryable for dates
|
|
619
|
-
if stac_param in stac_queryables or stac_param in (
|
|
620
|
-
"start_datetime",
|
|
621
|
-
"end_datetime",
|
|
622
|
-
):
|
|
623
|
-
continue
|
|
624
613
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
614
|
+
queryable["title"] = stac_param.split(":")[-1]
|
|
615
|
+
|
|
616
|
+
# remove null default values
|
|
617
|
+
if not queryable.get("default"):
|
|
618
|
+
queryable.pop("default", None)
|
|
630
619
|
|
|
631
|
-
|
|
632
|
-
|
|
620
|
+
stac_properties[stac_param] = queryable
|
|
621
|
+
required = list(map(lambda x: x.replace(param, stac_param), required))
|
|
622
|
+
|
|
623
|
+
# due to certain metadata mappings we might only get end_datetime but we can
|
|
624
|
+
# assume that start_datetime is also available
|
|
625
|
+
if (
|
|
626
|
+
"end_datetime" in stac_properties
|
|
627
|
+
and "start_datetime" not in stac_properties
|
|
628
|
+
):
|
|
629
|
+
stac_properties["start_datetime"] = deepcopy(
|
|
630
|
+
stac_properties["end_datetime"]
|
|
631
|
+
)
|
|
632
|
+
stac_properties["start_datetime"]["title"] = "start_datetime"
|
|
633
|
+
# if we can search by start_datetime we can search by datetime
|
|
634
|
+
if "start_datetime" in stac_properties:
|
|
635
|
+
stac_properties["datetime"] = StacQueryables.possible_properties[
|
|
636
|
+
"datetime"
|
|
637
|
+
].model_dump()
|
|
638
|
+
|
|
639
|
+
# format spatial extend properties to STAC format.
|
|
640
|
+
if "geometry" in stac_properties:
|
|
641
|
+
stac_properties["bbox"] = StacQueryables.possible_properties[
|
|
642
|
+
"bbox"
|
|
643
|
+
].model_dump()
|
|
644
|
+
stac_properties["geometry"] = StacQueryables.possible_properties[
|
|
645
|
+
"geometry"
|
|
646
|
+
].model_dump()
|
|
647
|
+
|
|
648
|
+
if not params.collection:
|
|
649
|
+
stac_properties["collection"] = StacQueryables.default_properties[
|
|
650
|
+
"collection"
|
|
651
|
+
].model_dump()
|
|
652
|
+
|
|
653
|
+
additional_properties = python_queryables.additional_properties
|
|
654
|
+
description = "Queryable names for the EODAG STAC API Item Search filter. "
|
|
655
|
+
description += python_queryables.additional_information
|
|
633
656
|
|
|
634
657
|
return StacQueryables(
|
|
635
658
|
q_id=request.state.url,
|
|
636
|
-
additional_properties=
|
|
637
|
-
properties=
|
|
638
|
-
|
|
659
|
+
additional_properties=additional_properties,
|
|
660
|
+
properties=stac_properties,
|
|
661
|
+
required=required or None,
|
|
662
|
+
description=description,
|
|
663
|
+
).model_dump(mode="json", by_alias=True, exclude_none=True)
|
|
639
664
|
|
|
640
665
|
hashed_queryables = hash(params.model_dump_json())
|
|
641
666
|
return await cached(
|
|
@@ -701,11 +726,16 @@ def eodag_api_init() -> None:
|
|
|
701
726
|
constellation: Union[str, List[str]] = ext_col.get("summaries", {}).get(
|
|
702
727
|
"constellation"
|
|
703
728
|
)
|
|
729
|
+
processing_level: Union[str, List[str]] = ext_col.get("summaries", {}).get(
|
|
730
|
+
"processing:level"
|
|
731
|
+
)
|
|
704
732
|
# Check if platform or constellation are lists and join them into a string if they are
|
|
705
733
|
if isinstance(platform, list):
|
|
706
734
|
platform = ",".join(platform)
|
|
707
735
|
if isinstance(constellation, list):
|
|
708
736
|
constellation = ",".join(constellation)
|
|
737
|
+
if isinstance(processing_level, list):
|
|
738
|
+
processing_level = ",".join(processing_level)
|
|
709
739
|
|
|
710
740
|
update_fields = {
|
|
711
741
|
"title": ext_col.get("title"),
|
|
@@ -716,7 +746,7 @@ def eodag_api_init() -> None:
|
|
|
716
746
|
),
|
|
717
747
|
"platform": constellation,
|
|
718
748
|
"platformSerialIdentifier": platform,
|
|
719
|
-
"processingLevel":
|
|
749
|
+
"processingLevel": processing_level,
|
|
720
750
|
"license": ext_col["license"],
|
|
721
751
|
"missionStartDate": ext_col["extent"]["temporal"]["interval"][0][0],
|
|
722
752
|
"missionEndDate": ext_col["extent"]["temporal"]["interval"][-1][1],
|
eodag/rest/server.py
CHANGED
|
@@ -61,7 +61,12 @@ from eodag.rest.core import (
|
|
|
61
61
|
from eodag.rest.errors import add_exception_handlers
|
|
62
62
|
from eodag.rest.types.queryables import QueryablesGetParams
|
|
63
63
|
from eodag.rest.types.stac_search import SearchPostRequest, sortby2list
|
|
64
|
-
from eodag.rest.utils import
|
|
64
|
+
from eodag.rest.utils import (
|
|
65
|
+
LIVENESS_PROBE_PATH,
|
|
66
|
+
format_pydantic_error,
|
|
67
|
+
str2json,
|
|
68
|
+
str2list,
|
|
69
|
+
)
|
|
65
70
|
from eodag.utils import parse_header, update_nested_dict
|
|
66
71
|
|
|
67
72
|
if TYPE_CHECKING:
|
|
@@ -120,6 +125,17 @@ app = FastAPI(lifespan=lifespan, title="EODAG", docs_url="/api.html")
|
|
|
120
125
|
stac_api_config = load_stac_api_config()
|
|
121
126
|
|
|
122
127
|
|
|
128
|
+
@router.api_route(
|
|
129
|
+
methods=["GET", "HEAD"],
|
|
130
|
+
path=LIVENESS_PROBE_PATH,
|
|
131
|
+
include_in_schema=False,
|
|
132
|
+
status_code=200,
|
|
133
|
+
)
|
|
134
|
+
async def liveness_probe(request: Request) -> Dict[str, bool]:
|
|
135
|
+
"Endpoint meant to be used as liveness probe by deployment platforms"
|
|
136
|
+
return {"success": True}
|
|
137
|
+
|
|
138
|
+
|
|
123
139
|
@router.api_route(
|
|
124
140
|
methods=["GET", "HEAD"], path="/api", tags=["Capabilities"], include_in_schema=False
|
|
125
141
|
)
|
|
@@ -375,13 +391,24 @@ async def list_collection_queryables(
|
|
|
375
391
|
:returns: A json object containing the list of available queryable properties for the specified collection.
|
|
376
392
|
"""
|
|
377
393
|
logger.info(f"{request.method} {request.state.url}")
|
|
378
|
-
|
|
394
|
+
# split by `,` to handle list of parameters
|
|
395
|
+
additional_params = {k: v.split(",") for k, v in dict(request.query_params).items()}
|
|
379
396
|
provider = additional_params.pop("provider", None)
|
|
380
397
|
|
|
398
|
+
datetime = additional_params.pop("datetime", None)
|
|
399
|
+
|
|
381
400
|
queryables = await get_queryables(
|
|
382
401
|
request,
|
|
383
|
-
QueryablesGetParams(
|
|
384
|
-
|
|
402
|
+
QueryablesGetParams.model_validate(
|
|
403
|
+
{
|
|
404
|
+
**additional_params,
|
|
405
|
+
**{
|
|
406
|
+
"collection": collection_id,
|
|
407
|
+
"datetime": datetime[0] if datetime else None,
|
|
408
|
+
},
|
|
409
|
+
}
|
|
410
|
+
),
|
|
411
|
+
provider=provider[0] if provider else None,
|
|
385
412
|
)
|
|
386
413
|
|
|
387
414
|
return ORJSONResponse(queryables)
|
eodag/rest/types/eodag_search.py
CHANGED
|
@@ -367,6 +367,12 @@ class EODAGSearch(BaseModel):
|
|
|
367
367
|
provider: Optional[str] = None,
|
|
368
368
|
) -> str:
|
|
369
369
|
"""Get the alias of a field in a Pydantic model"""
|
|
370
|
+
# quick fix. TODO: refactor of EODAGSearch.
|
|
371
|
+
if field_name in ("productType", "id", "start_datetime", "end_datetime"):
|
|
372
|
+
return field_name
|
|
373
|
+
# another quick fix to handle different names of geometry
|
|
374
|
+
if field_name == "geometry":
|
|
375
|
+
field_name = "geom"
|
|
370
376
|
field = cls.model_fields.get(field_name)
|
|
371
377
|
if field is not None and field.alias is not None:
|
|
372
378
|
return field.alias
|
eodag/rest/types/queryables.py
CHANGED
|
@@ -131,14 +131,12 @@ class StacQueryables(BaseModel):
|
|
|
131
131
|
default="Queryable names for the EODAG STAC API Item Search filter."
|
|
132
132
|
)
|
|
133
133
|
default_properties: ClassVar[Dict[str, StacQueryableProperty]] = {
|
|
134
|
-
"id": StacQueryableProperty(
|
|
135
|
-
description="ID",
|
|
136
|
-
ref="https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/id",
|
|
137
|
-
),
|
|
138
134
|
"collection": StacQueryableProperty(
|
|
139
135
|
description="Collection",
|
|
140
136
|
ref="https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/collection",
|
|
141
|
-
)
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
possible_properties: ClassVar[Dict[str, StacQueryableProperty]] = {
|
|
142
140
|
"geometry": StacQueryableProperty(
|
|
143
141
|
description="Geometry",
|
|
144
142
|
ref="https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/geometry",
|
|
@@ -154,7 +152,8 @@ class StacQueryables(BaseModel):
|
|
|
154
152
|
items={"type": "number"},
|
|
155
153
|
),
|
|
156
154
|
}
|
|
157
|
-
properties: Dict[str,
|
|
155
|
+
properties: Dict[str, Any] = Field()
|
|
156
|
+
required: Optional[List[str]] = Field(None)
|
|
158
157
|
additional_properties: bool = Field(
|
|
159
158
|
default=True, serialization_alias="additionalProperties"
|
|
160
159
|
)
|
eodag/rest/utils/__init__.py
CHANGED
eodag/types/__init__.py
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
# See the License for the specific language governing permissions and
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
"""EODAG types"""
|
|
19
|
+
|
|
19
20
|
from __future__ import annotations
|
|
20
21
|
|
|
21
22
|
from typing import (
|
|
@@ -33,7 +34,7 @@ from typing import (
|
|
|
33
34
|
)
|
|
34
35
|
|
|
35
36
|
from annotated_types import Gt, Lt
|
|
36
|
-
from pydantic import Field
|
|
37
|
+
from pydantic import BaseModel, Field, create_model
|
|
37
38
|
from pydantic.fields import FieldInfo
|
|
38
39
|
|
|
39
40
|
from eodag.utils import copy_deepcopy
|
|
@@ -70,8 +71,8 @@ def json_type_to_python(json_type: Union[str, List[str]]) -> type:
|
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def _get_min_or_max(type_info: Union[Lt, Gt, Any]) -> Tuple[str, Any]:
|
|
73
|
-
"""
|
|
74
|
-
|
|
74
|
+
"""Checks if the value from an Annotated object is a minimum or maximum
|
|
75
|
+
|
|
75
76
|
:param type_info: info from Annotated
|
|
76
77
|
:return: "min" or "max"
|
|
77
78
|
"""
|
|
@@ -83,10 +84,10 @@ def _get_min_or_max(type_info: Union[Lt, Gt, Any]) -> Tuple[str, Any]:
|
|
|
83
84
|
|
|
84
85
|
|
|
85
86
|
def _get_type_info_from_annotated(
|
|
86
|
-
annotated_type: Annotated[type, Any]
|
|
87
|
+
annotated_type: Annotated[type, Any],
|
|
87
88
|
) -> Dict[str, Any]:
|
|
88
|
-
"""
|
|
89
|
-
|
|
89
|
+
"""Retrieves type information from an annotated object
|
|
90
|
+
|
|
90
91
|
:param annotated_type: annotated object
|
|
91
92
|
:return: dict containing type and min/max if available
|
|
92
93
|
"""
|
|
@@ -118,7 +119,8 @@ def python_type_to_json(
|
|
|
118
119
|
:param python_type: the python type
|
|
119
120
|
:returns: the json type
|
|
120
121
|
"""
|
|
121
|
-
|
|
122
|
+
origin = get_origin(python_type)
|
|
123
|
+
if origin is Union:
|
|
122
124
|
json_type = list()
|
|
123
125
|
for single_python_type in get_args(python_type):
|
|
124
126
|
type_data = {}
|
|
@@ -138,8 +140,10 @@ def python_type_to_json(
|
|
|
138
140
|
return list(JSON_TYPES_MAPPING.keys())[
|
|
139
141
|
list(JSON_TYPES_MAPPING.values()).index(python_type)
|
|
140
142
|
]
|
|
141
|
-
elif
|
|
143
|
+
elif origin is Annotated:
|
|
142
144
|
return [_get_type_info_from_annotated(python_type)]
|
|
145
|
+
elif origin is list:
|
|
146
|
+
raise NotImplementedError("Never completed")
|
|
143
147
|
else:
|
|
144
148
|
return None
|
|
145
149
|
|
|
@@ -173,12 +177,27 @@ def json_field_definition_to_python(
|
|
|
173
177
|
title=json_field_definition.get("title", None),
|
|
174
178
|
description=json_field_definition.get("description", None),
|
|
175
179
|
pattern=json_field_definition.get("pattern", None),
|
|
180
|
+
le=json_field_definition.get("maximum"),
|
|
181
|
+
ge=json_field_definition.get("minimum"),
|
|
176
182
|
)
|
|
177
183
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
):
|
|
181
|
-
|
|
184
|
+
enum = json_field_definition.get("enum")
|
|
185
|
+
|
|
186
|
+
if python_type in (list, set):
|
|
187
|
+
items = json_field_definition.get("items", None)
|
|
188
|
+
if isinstance(items, list):
|
|
189
|
+
python_type = Tuple[ # type: ignore
|
|
190
|
+
tuple(
|
|
191
|
+
json_field_definition_to_python(item, required=required)
|
|
192
|
+
for item in items
|
|
193
|
+
)
|
|
194
|
+
]
|
|
195
|
+
elif isinstance(items, dict):
|
|
196
|
+
enum = items.get("enum")
|
|
197
|
+
|
|
198
|
+
if enum:
|
|
199
|
+
literal = Literal[tuple(sorted(enum))] # type: ignore
|
|
200
|
+
python_type = List[literal] if python_type in (list, set) else literal # type: ignore
|
|
182
201
|
|
|
183
202
|
if "$ref" in json_field_definition:
|
|
184
203
|
field_type_kwargs["json_schema_extra"] = {"$ref": json_field_definition["$ref"]}
|
|
@@ -190,7 +209,7 @@ def json_field_definition_to_python(
|
|
|
190
209
|
|
|
191
210
|
|
|
192
211
|
def python_field_definition_to_json(
|
|
193
|
-
python_field_definition: Annotated[Any, FieldInfo]
|
|
212
|
+
python_field_definition: Annotated[Any, FieldInfo],
|
|
194
213
|
) -> Dict[str, Any]:
|
|
195
214
|
"""Get json field definition from python `typing.Annotated`
|
|
196
215
|
|
|
@@ -252,6 +271,7 @@ def python_field_definition_to_json(
|
|
|
252
271
|
json_field_definition["max"] = [
|
|
253
272
|
row["max"] if "max" in row else None for row in field_type
|
|
254
273
|
]
|
|
274
|
+
|
|
255
275
|
if "min" in json_field_definition and json_field_definition["min"].count(
|
|
256
276
|
None
|
|
257
277
|
) == len(json_field_definition["min"]):
|
|
@@ -291,7 +311,7 @@ def python_field_definition_to_json(
|
|
|
291
311
|
|
|
292
312
|
|
|
293
313
|
def model_fields_to_annotated(
|
|
294
|
-
model_fields: Dict[str, FieldInfo]
|
|
314
|
+
model_fields: Dict[str, FieldInfo],
|
|
295
315
|
) -> Dict[str, Annotated[Any, FieldInfo]]:
|
|
296
316
|
"""Convert BaseModel.model_fields from FieldInfo to Annotated
|
|
297
317
|
|
|
@@ -306,7 +326,7 @@ def model_fields_to_annotated(
|
|
|
306
326
|
:param model_fields: BaseModel.model_fields to convert
|
|
307
327
|
:returns: Annotated tuple usable as create_model argument
|
|
308
328
|
"""
|
|
309
|
-
annotated_model_fields = dict()
|
|
329
|
+
annotated_model_fields: Dict[str, Annotated[Any, FieldInfo]] = dict()
|
|
310
330
|
for param, field_info in model_fields.items():
|
|
311
331
|
field_type = field_info.annotation or type(None)
|
|
312
332
|
new_field_info = copy_deepcopy(field_info)
|
|
@@ -315,6 +335,27 @@ def model_fields_to_annotated(
|
|
|
315
335
|
return annotated_model_fields
|
|
316
336
|
|
|
317
337
|
|
|
338
|
+
def annotated_dict_to_model(
|
|
339
|
+
model_name: str, annotated_fields: Dict[str, Annotated[Any, FieldInfo]]
|
|
340
|
+
) -> BaseModel:
|
|
341
|
+
"""Convert a dictionary of Annotated values to a Pydantic BaseModel.
|
|
342
|
+
|
|
343
|
+
:param model_name: name of the model to be created
|
|
344
|
+
:param annotated_fields: dict containing the parameters and annotated values that should become
|
|
345
|
+
the properties of the model
|
|
346
|
+
:returns: pydantic model
|
|
347
|
+
"""
|
|
348
|
+
fields = {
|
|
349
|
+
name: (field.__args__[0], field.__metadata__[0])
|
|
350
|
+
for name, field in annotated_fields.items()
|
|
351
|
+
}
|
|
352
|
+
return create_model(
|
|
353
|
+
model_name,
|
|
354
|
+
**fields, # type: ignore
|
|
355
|
+
__config__={"arbitrary_types_allowed": True},
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
318
359
|
class ProviderSortables(TypedDict):
|
|
319
360
|
"""A class representing sortable parameter(s) of a provider and the allowed
|
|
320
361
|
maximum number of used sortable(s) in a search request with the provider
|
eodag/types/download_args.py
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from typing import Dict, Optional, TypedDict
|
|
20
|
+
from typing import Dict, Optional, TypedDict, Union
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class DownloadConf(TypedDict, total=False):
|
|
@@ -33,7 +33,7 @@ class DownloadConf(TypedDict, total=False):
|
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
35
|
output_dir: str
|
|
36
|
-
output_extension: str
|
|
36
|
+
output_extension: Union[str, None]
|
|
37
37
|
extract: bool
|
|
38
38
|
dl_url_params: Dict[str, str]
|
|
39
39
|
delete_archive: bool
|