Skip to content

Commit 78a6f97

Browse files
improve typing (#826)
* improve typing * Apply suggestions from code review * Apply suggestions from code review * revert optional output for str2bbox * update changelog
1 parent 738f6d6 commit 78a6f97

File tree

9 files changed

+53
-52
lines changed

9 files changed

+53
-52
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Fixed
66

77
- Remove defaults in OpenAPI schemas
8+
- Type Hints for TypedDict
89

910
### Added
1011

stac_fastapi/api/stac_fastapi/api/middleware.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
8282

8383
await self.app(scope, receive, send)
8484

85-
def _get_forwarded_url_parts(self, scope: Scope) -> Tuple[str]:
85+
def _get_forwarded_url_parts(self, scope: Scope) -> Tuple[str, str, str]:
8686
proto = scope.get("scheme", "http")
8787
header_host = self._get_header_value_by_name(scope, "host")
8888
if header_host is None:
@@ -127,7 +127,7 @@ def _get_header_value_by_name(
127127
@staticmethod
128128
def _replace_header_value_by_name(
129129
scope: Scope, header_name: str, new_value: str
130-
) -> List[Tuple[str]]:
130+
) -> List[Tuple[str, str]]:
131131
return [
132132
(name, value)
133133
for name, value in scope["headers"]

stac_fastapi/extensions/stac_fastapi/extensions/core/aggregation/types.py

+17-16
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,34 @@
22

33
from typing import Any, Dict, List, Literal, Optional, Union
44

5-
from pydantic import Field
6-
from typing_extensions import TypedDict
5+
from typing_extensions import NotRequired, TypedDict
76

87
from stac_fastapi.types.rfc3339 import DateTimeType
98

9+
Bucket = TypedDict(
10+
"Bucket",
11+
{
12+
"key": str,
13+
"data_type": str,
14+
"frequency": NotRequired[Dict],
15+
# we can't use the `class Bucket` notation because `from` is a reserved key
16+
"from": NotRequired[Union[int, float]],
17+
"to": NotRequired[Optional[Union[int, float]]],
18+
},
19+
)
1020

11-
class Bucket(TypedDict, total=False):
12-
"""A STAC aggregation bucket."""
1321

14-
key: str
15-
data_type: str
16-
frequency: Optional[Dict] = None
17-
_from: Optional[Union[int, float]] = Field(alias="from", default=None)
18-
to: Optional[Optional[Union[int, float]]] = None
19-
20-
21-
class Aggregation(TypedDict, total=False):
22+
class Aggregation(TypedDict):
2223
"""A STAC aggregation."""
2324

2425
name: str
2526
data_type: str
26-
buckets: Optional[List[Bucket]] = None
27-
overflow: Optional[int] = None
28-
value: Optional[Union[str, int, DateTimeType]] = None
27+
buckets: NotRequired[List[Bucket]]
28+
overflow: NotRequired[int]
29+
value: NotRequired[Union[str, int, DateTimeType]]
2930

3031

31-
class AggregationCollection(TypedDict, total=False):
32+
class AggregationCollection(TypedDict):
3233
"""STAC Item Aggregation Collection."""
3334

3435
type: Literal["AggregationCollection"]

stac_fastapi/extensions/stac_fastapi/extensions/core/free_text/request.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pydantic import BaseModel, Field
88
from typing_extensions import Annotated
99

10-
from stac_fastapi.types.search import APIRequest, str2list
10+
from stac_fastapi.types.search import APIRequest
1111

1212

1313
def _ft_converter(
@@ -22,7 +22,9 @@ def _ft_converter(
2222
),
2323
] = None,
2424
) -> Optional[List[str]]:
25-
return str2list(val)
25+
if val:
26+
return val.split(",")
27+
return None
2628

2729

2830
@attr.s

stac_fastapi/types/stac_fastapi/types/core.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ def item_collection(
533533
bbox: Optional[BBox] = None,
534534
datetime: Optional[str] = None,
535535
limit: int = 10,
536-
token: str = None,
536+
token: Optional[str] = None,
537537
**kwargs,
538538
) -> stac.ItemCollection:
539539
"""Get all items from a specific collection.
@@ -744,7 +744,7 @@ async def item_collection(
744744
bbox: Optional[BBox] = None,
745745
datetime: Optional[str] = None,
746746
limit: int = 10,
747-
token: str = None,
747+
token: Optional[str] = None,
748748
**kwargs,
749749
) -> stac.ItemCollection:
750750
"""Get all items from a specific collection.

stac_fastapi/types/stac_fastapi/types/extension.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ApiExtension(abc.ABC):
1515
GET = None
1616
POST = None
1717

18-
def get_request_model(self, verb: Optional[str] = "GET") -> Optional[BaseModel]:
18+
def get_request_model(self, verb: str = "GET") -> Optional[BaseModel]:
1919
"""Return the request model for the extension.method.
2020
2121
The model can differ based on HTTP verb

stac_fastapi/types/stac_fastapi/types/rfc3339.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def str_to_interval(interval: Optional[str]) -> Optional[DateTimeType]:
145145
status_code=400, detail="Start datetime cannot be before end datetime."
146146
)
147147

148-
return start, end
148+
return start, end # type: ignore
149149

150150

151151
def now_in_utc() -> datetime:

stac_fastapi/types/stac_fastapi/types/search.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def str2list(x: str) -> Optional[List[str]]:
3434
def str2bbox(x: str) -> Optional[BBox]:
3535
"""Convert string to BBox based on , delimiter."""
3636
if x:
37-
t = tuple(float(v) for v in str2list(x))
37+
t = tuple(float(v) for v in x.split(","))
3838
assert len(t) in [4, 6], f"BBox '{x}' must have 4 or 6 values."
3939
return t
4040

@@ -54,7 +54,9 @@ def _collection_converter(
5454
),
5555
] = None,
5656
) -> Optional[List[str]]:
57-
return str2list(val)
57+
if val:
58+
return val.split(",")
59+
return None
5860

5961

6062
def _ids_converter(
@@ -70,7 +72,9 @@ def _ids_converter(
7072
),
7173
] = None,
7274
) -> Optional[List[str]]:
73-
return str2list(val)
75+
if val:
76+
return val.split(",")
77+
return None
7478

7579

7680
def _bbox_converter(
@@ -85,7 +89,9 @@ def _bbox_converter(
8589
),
8690
] = None,
8791
) -> Optional[BBox]:
88-
return str2bbox(val)
92+
if val:
93+
return str2bbox(val)
94+
return None
8995

9096

9197
def _validate_datetime(instance, attribute, value):
+15-24
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
11
"""STAC types."""
22

3-
import sys
4-
from typing import Any, Dict, List, Literal, Optional, Union
3+
from typing import Any, Dict, List, Literal, Union
54

65
from stac_pydantic.shared import BBox
7-
8-
# Avoids a Pydantic error:
9-
# TypeError: You should use `typing_extensions.TypedDict` instead of
10-
# `typing.TypedDict` with Python < 3.12.0. Without it, there is no way to
11-
# differentiate required and optional fields when subclassed.
12-
if sys.version_info < (3, 12, 0):
13-
from typing_extensions import TypedDict
14-
else:
15-
from typing import TypedDict
6+
from typing_extensions import NotRequired, TypedDict
167

178
NumType = Union[float, int]
189

1910

20-
class Catalog(TypedDict, total=False):
11+
class Catalog(TypedDict):
2112
"""STAC Catalog."""
2213

2314
type: str
2415
stac_version: str
25-
stac_extensions: Optional[List[str]]
16+
stac_extensions: NotRequired[List[str]]
2617
id: str
27-
title: Optional[str]
18+
title: NotRequired[str]
2819
description: str
2920
links: List[Dict[str, Any]]
3021

3122

32-
class LandingPage(Catalog, total=False):
23+
class LandingPage(Catalog):
3324
"""STAC Landing Page."""
3425

3526
conformsTo: List[str]
@@ -41,7 +32,7 @@ class Conformance(TypedDict):
4132
conformsTo: List[str]
4233

4334

44-
class Collection(Catalog, total=False):
35+
class Collection(Catalog):
4536
"""STAC Collection."""
4637

4738
keywords: List[str]
@@ -52,12 +43,12 @@ class Collection(Catalog, total=False):
5243
assets: Dict[str, Any]
5344

5445

55-
class Item(TypedDict, total=False):
46+
class Item(TypedDict):
5647
"""STAC Item."""
5748

5849
type: Literal["Feature"]
5950
stac_version: str
60-
stac_extensions: Optional[List[str]]
51+
stac_extensions: NotRequired[List[str]]
6152
id: str
6253
geometry: Dict[str, Any]
6354
bbox: BBox
@@ -67,22 +58,22 @@ class Item(TypedDict, total=False):
6758
collection: str
6859

6960

70-
class ItemCollection(TypedDict, total=False):
61+
class ItemCollection(TypedDict):
7162
"""STAC Item Collection."""
7263

7364
type: Literal["FeatureCollection"]
7465
features: List[Item]
7566
links: List[Dict[str, Any]]
76-
numberMatched: Optional[int]
77-
numberReturned: Optional[int]
67+
numberMatched: NotRequired[int]
68+
numberReturned: NotRequired[int]
7869

7970

80-
class Collections(TypedDict, total=False):
71+
class Collections(TypedDict):
8172
"""All collections endpoint.
8273
https://github.com/radiantearth/stac-api-spec/tree/master/collections
8374
"""
8475

8576
collections: List[Collection]
8677
links: List[Dict[str, Any]]
87-
numberMatched: Optional[int] = None
88-
numberReturned: Optional[int] = None
78+
numberMatched: NotRequired[int]
79+
numberReturned: NotRequired[int]

0 commit comments

Comments
 (0)