From 954d59ac8916eb44110625b8ec04a3e64e9a22d8 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 15 Jan 2026 13:00:01 -0500 Subject: [PATCH] Add TypedDicts for extra fields --- src/pystac/asset.py | 10 ++++--- src/pystac/common.py | 62 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/pystac/common.py diff --git a/src/pystac/asset.py b/src/pystac/asset.py index d12f858d7..56047f39a 100644 --- a/src/pystac/asset.py +++ b/src/pystac/asset.py @@ -5,6 +5,7 @@ from typing_extensions import deprecated +from .common import T_DataValue, T_Instrument from .media_type import MediaType from .utils import is_absolute_href, make_absolute_href, make_relative_href from .writer import Writer @@ -26,7 +27,7 @@ def __init__( self.description: str | None = description self.type: str | None = type self.roles: list[str] | None = roles - self.extra_fields: dict[str, Any] = kwargs + self.extra_fields: dict[str, Any] | T_DataValue | T_Instrument = kwargs @classmethod def try_from[T: ItemAsset](cls: type[T], data: T | dict[str, Any]) -> T: @@ -45,7 +46,9 @@ def clone[T: ItemAsset](self: T) -> T: return self.from_dict(self.to_dict()) def to_dict(self) -> dict[str, Any]: - data = copy.deepcopy(self.extra_fields) + data: dict[str, Any] = { + k: v for k, v in copy.deepcopy(self.extra_fields).items() + } if self.title is not None: data["title"] = self.title if self.description is not None: @@ -103,8 +106,7 @@ def get_absolute_href(self) -> str | None: @override def to_dict(self) -> dict[str, Any]: data = super().to_dict() - data["href"] = self.href - return data + return {"href": self.href, **data} class Assets(Protocol): diff --git a/src/pystac/common.py b/src/pystac/common.py new file mode 100644 index 000000000..49d03bda1 --- /dev/null +++ b/src/pystac/common.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from typing import Literal, TypedDict + + +class T_Basics(TypedDict, total=False): + title: str + description: str + keywords: list[str] + roles: list[str] + + +class T_DateTime(TypedDict, total=False): + datetime: str + created: str + updated: str + start_datetime: str + end_datetime: str + + +class T_Instrument(TypedDict, total=False): + platform: str + instruments: list[str] + constellation: str + mission: str + gsd: int + + +class T_Statistics(TypedDict, total=False): + minimum: float + maximum: float + mean: float + stddev: float + count: int + valid_percent: float + + +class T_DataValue(TypedDict, total=False): + nodata: float | str + unit: str + data_type: DataType + statistics: T_Statistics + + +type DataType = Literal[ + "int8", + "int16", + "int32", + "int64", + "uint8", + "uint16", + "uint32", + "uint64", + "float16", + "float32", + "float64", + "cint16", + "cint32", + "cfloat32", + "cfloat64", + "other", +]