stac-fastapi.api 6.3.0__tar.gz → 6.3.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stac-fastapi-api
3
- Version: 6.3.0
3
+ Version: 6.3.1
4
4
  Summary: An implementation of STAC API based on the FastAPI framework.
5
5
  Project-URL: Homepage, https://github.com/stac-utils/stac-fastapi
6
6
  Author-email: ArturoAI <engineering@arturo.ai>
@@ -1,7 +1,7 @@
1
1
  """Fastapi app creation."""
2
2
 
3
3
  import inspect
4
- from typing import Awaitable, Callable, Dict, List, Optional, Tuple, Type, Union
4
+ from collections.abc import Awaitable, Callable
5
5
 
6
6
  import attr
7
7
  from brotli_asgi import BrotliMiddleware
@@ -78,9 +78,9 @@ class StacApi:
78
78
  """
79
79
 
80
80
  settings: ApiSettings = attr.ib()
81
- client: Union[AsyncBaseCoreClient, BaseCoreClient] = attr.ib()
82
- extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list))
83
- exceptions: Dict[Type[Exception], int] = attr.ib(
81
+ client: AsyncBaseCoreClient | BaseCoreClient = attr.ib()
82
+ extensions: list[ApiExtension] = attr.ib(default=attr.Factory(list))
83
+ exceptions: dict[type[Exception], int] = attr.ib(
84
84
  default=attr.Factory(lambda: DEFAULT_STATUS_CODES)
85
85
  )
86
86
  title: str = attr.ib(
@@ -115,14 +115,14 @@ class StacApi:
115
115
  converter=update_openapi, # type: ignore
116
116
  )
117
117
  router: APIRouter = attr.ib(default=attr.Factory(APIRouter))
118
- search_get_request_model: Type[APIRequest] = attr.ib(default=BaseSearchGetRequest)
119
- search_post_request_model: Type[BaseModel] = attr.ib(default=BaseSearchPostRequest)
120
- collections_get_request_model: Type[APIRequest] = attr.ib(default=EmptyRequest)
121
- collection_get_request_model: Type[APIRequest] = attr.ib(default=CollectionUri)
122
- items_get_request_model: Type[APIRequest] = attr.ib(default=ItemCollectionUri)
123
- item_get_request_model: Type[APIRequest] = attr.ib(default=ItemUri)
124
- response_class: Type[Response] = attr.ib(default=JSONResponse)
125
- middlewares: List[Middleware] = attr.ib(
118
+ search_get_request_model: type[APIRequest] = attr.ib(default=BaseSearchGetRequest)
119
+ search_post_request_model: type[BaseModel] = attr.ib(default=BaseSearchPostRequest)
120
+ collections_get_request_model: type[APIRequest] = attr.ib(default=EmptyRequest)
121
+ collection_get_request_model: type[APIRequest] = attr.ib(default=CollectionUri)
122
+ items_get_request_model: type[APIRequest] = attr.ib(default=ItemCollectionUri)
123
+ item_get_request_model: type[APIRequest] = attr.ib(default=ItemUri)
124
+ response_class: type[Response] = attr.ib(default=JSONResponse)
125
+ middlewares: list[Middleware] = attr.ib(
126
126
  default=attr.Factory(
127
127
  lambda: [
128
128
  Middleware(BrotliMiddleware),
@@ -138,12 +138,12 @@ class StacApi:
138
138
  ]
139
139
  )
140
140
  )
141
- route_dependencies: List[Tuple[List[Scope], List[Depends]]] = attr.ib(default=[])
142
- health_check: Union[Callable[[], Dict], Callable[[], Awaitable[Dict]]] = attr.ib(
141
+ route_dependencies: list[tuple[list[Scope], list[Depends]]] = attr.ib(default=[])
142
+ health_check: Callable[[], dict] | Callable[[], Awaitable[dict]] = attr.ib(
143
143
  default=lambda: {"status": "UP"}
144
144
  )
145
145
 
146
- def get_extension(self, extension: Type[ApiExtension]) -> Optional[ApiExtension]:
146
+ def get_extension(self, extension: type[ApiExtension]) -> ApiExtension | None:
147
147
  """Get an extension.
148
148
 
149
149
  Args:
@@ -387,7 +387,7 @@ class StacApi:
387
387
  mgmt_router.add_api_route(
388
388
  name="Ping",
389
389
  path="/_mgmt/ping",
390
- response_model=Dict,
390
+ response_model=dict,
391
391
  responses={
392
392
  200: {
393
393
  "content": {
@@ -423,7 +423,7 @@ class StacApi:
423
423
  self.app.include_router(mgmt_router, tags=["Liveliness/Readiness"])
424
424
 
425
425
  def add_route_dependencies(
426
- self, scopes: List[Scope], dependencies: List[Depends]
426
+ self, scopes: list[Scope], dependencies: list[Depends]
427
427
  ) -> None:
428
428
  """Add custom dependencies to routes.
429
429
 
@@ -1,7 +1,8 @@
1
1
  """Error handling."""
2
2
 
3
3
  import logging
4
- from typing import Callable, Dict, Type, TypedDict
4
+ from collections.abc import Callable
5
+ from typing import TypedDict
5
6
 
6
7
  from fastapi import FastAPI
7
8
  from fastapi.encoders import jsonable_encoder
@@ -69,7 +70,7 @@ def exception_handler_factory(status_code: int) -> Callable:
69
70
 
70
71
 
71
72
  def add_exception_handlers(
72
- app: FastAPI, status_codes: Dict[Type[Exception], int]
73
+ app: FastAPI, status_codes: dict[type[Exception], int]
73
74
  ) -> None:
74
75
  """Add exception handlers to the FastAPI application.
75
76
 
@@ -5,7 +5,6 @@ import re
5
5
  import typing
6
6
  import warnings
7
7
  from http.client import HTTP_PORT, HTTPS_PORT
8
- from typing import List, Optional, Tuple
9
8
 
10
9
  from starlette.middleware.cors import CORSMiddleware as _CORSMiddleware
11
10
  from starlette.types import ASGIApp, Receive, Scope, Send
@@ -29,7 +28,7 @@ class CORSMiddleware(_CORSMiddleware):
29
28
  ),
30
29
  allow_headers: typing.Sequence[str] = ("Content-Type",),
31
30
  allow_credentials: bool = False,
32
- allow_origin_regex: typing.Optional[str] = None,
31
+ allow_origin_regex: str | None = None,
33
32
  expose_headers: typing.Sequence[str] = (),
34
33
  max_age: int = 600,
35
34
  **kwargs: typing.Any,
@@ -91,7 +90,7 @@ class ProxyHeaderMiddleware:
91
90
 
92
91
  await self.app(scope, receive, send)
93
92
 
94
- def _get_forwarded_url_parts(self, scope: Scope) -> Tuple[str, str, int]:
93
+ def _get_forwarded_url_parts(self, scope: Scope) -> tuple[str, str, int]:
95
94
  proto = scope.get("scheme", "http")
96
95
  # Assume default port based on protocol, can be overridden later
97
96
  port = 443 if proto == "https" else 80
@@ -107,7 +106,7 @@ class ProxyHeaderMiddleware:
107
106
  domain, port = scope["server"]
108
107
  port = int(port)
109
108
 
110
- forwarded_port: Optional[str] = None
109
+ forwarded_port: str | None = None
111
110
  forwarding_occurred = any(
112
111
  key
113
112
  in [
@@ -144,8 +143,8 @@ class ProxyHeaderMiddleware:
144
143
  return (proto, domain, port)
145
144
 
146
145
  def _get_header_value_by_name(
147
- self, scope: Scope, header_name: str, default_value: Optional[str] = None
148
- ) -> Optional[str]:
146
+ self, scope: Scope, header_name: str, default_value: str | None = None
147
+ ) -> str | None:
149
148
  headers = scope["headers"]
150
149
  candidates = [
151
150
  value.decode() for key, value in headers if key.decode() == header_name
@@ -155,7 +154,7 @@ class ProxyHeaderMiddleware:
155
154
  @staticmethod
156
155
  def _replace_header_value_by_name(
157
156
  scope: Scope, header_name: str, new_value: str
158
- ) -> List[Tuple[str, str]]:
157
+ ) -> list[tuple[str, str]]:
159
158
  return [
160
159
  (name, value)
161
160
  for name, value in scope["headers"]
@@ -1,12 +1,11 @@
1
1
  """Api request/response models."""
2
2
 
3
- from typing import List, Literal, Optional, Type, Union, cast
3
+ from typing import Annotated, Literal, cast
4
4
 
5
5
  import attr
6
6
  from fastapi import Path, Query
7
7
  from pydantic import BaseModel, create_model
8
8
  from stac_pydantic.shared import BBox
9
- from typing_extensions import Annotated
10
9
 
11
10
  from stac_fastapi.types.extension import ApiExtension
12
11
  from stac_fastapi.types.search import (
@@ -29,14 +28,14 @@ except ImportError: # pragma: nocover
29
28
 
30
29
  def create_request_model(
31
30
  model_name="SearchGetRequest",
32
- base_model: Union[Type[BaseModel], Type[APIRequest]] = BaseSearchGetRequest,
33
- extensions: Optional[List[ApiExtension]] = None,
34
- mixins: Optional[Union[List[Type[BaseModel]], List[Type[APIRequest]]]] = None,
31
+ base_model: type[BaseModel] | type[APIRequest] = BaseSearchGetRequest,
32
+ extensions: list[ApiExtension] | None = None,
33
+ mixins: list[type[BaseModel]] | list[type[APIRequest]] | None = None,
35
34
  request_type: str = "GET",
36
- ) -> Union[Type[BaseModel], Type[APIRequest]]:
35
+ ) -> type[BaseModel] | type[APIRequest]:
37
36
  """Create a pydantic model for validating request bodies."""
38
37
  fields = {}
39
- extension_models: List[Union[Type[BaseModel], Type[APIRequest]]] = []
38
+ extension_models: list[type[BaseModel] | type[APIRequest]] = []
40
39
 
41
40
  # Check extensions for additional parameters to search
42
41
  for extension in extensions or []:
@@ -50,7 +49,7 @@ def create_request_model(
50
49
  # Handle GET requests
51
50
  if all([issubclass(m, APIRequest) for m in models]):
52
51
  get_model = attr.make_class(model_name, attrs={}, bases=tuple(models))
53
- return cast(Type[APIRequest], get_model)
52
+ return cast(type[APIRequest], get_model)
54
53
 
55
54
  # Handle POST requests
56
55
  elif all([issubclass(m, BaseModel) for m in models]):
@@ -59,15 +58,15 @@ def create_request_model(
59
58
  fields[k] = (field_info.annotation, field_info)
60
59
 
61
60
  post_model = create_model(model_name, **fields, __base__=base_model) # type: ignore
62
- return cast(Type[BaseModel], post_model)
61
+ return cast(type[BaseModel], post_model)
63
62
 
64
63
  raise TypeError("Mixed Request Model types. Check extension request types.")
65
64
 
66
65
 
67
66
  def create_get_request_model(
68
- extensions: Optional[List[ApiExtension]],
69
- base_model: Type[APIRequest] = BaseSearchGetRequest,
70
- ) -> Type[APIRequest]:
67
+ extensions: list[ApiExtension] | None,
68
+ base_model: type[APIRequest] = BaseSearchGetRequest,
69
+ ) -> type[APIRequest]:
71
70
  """Wrap create_request_model to create the GET request model."""
72
71
  model = create_request_model(
73
72
  "SearchGetRequest",
@@ -75,13 +74,13 @@ def create_get_request_model(
75
74
  extensions=extensions,
76
75
  request_type="GET",
77
76
  )
78
- return cast(Type[APIRequest], model)
77
+ return cast(type[APIRequest], model)
79
78
 
80
79
 
81
80
  def create_post_request_model(
82
- extensions: Optional[List[ApiExtension]],
83
- base_model: Type[BaseModel] = BaseSearchPostRequest,
84
- ) -> Type[BaseModel]:
81
+ extensions: list[ApiExtension] | None,
82
+ base_model: type[BaseModel] = BaseSearchPostRequest,
83
+ ) -> type[BaseModel]:
85
84
  """Wrap create_request_model to create the POST request model."""
86
85
  model = create_request_model(
87
86
  "SearchPostRequest",
@@ -89,7 +88,7 @@ def create_post_request_model(
89
88
  extensions=extensions,
90
89
  request_type="POST",
91
90
  )
92
- return cast(Type[BaseModel], model)
91
+ return cast(type[BaseModel], model)
93
92
 
94
93
 
95
94
  @attr.s
@@ -120,12 +119,12 @@ class ItemCollectionUri(APIRequest, DatetimeMixin):
120
119
 
121
120
  collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
122
121
  limit: Annotated[
123
- Optional[Limit],
122
+ Limit | None,
124
123
  Query(
125
124
  description="Limits the number of results that are included in each page of the response (capped to 10_000)." # noqa: E501
126
125
  ),
127
126
  ] = attr.ib(default=10)
128
- bbox: Optional[BBox] = attr.ib(default=None, converter=_bbox_converter) # type: ignore [misc]
127
+ bbox: BBox | None = attr.ib(default=None, converter=_bbox_converter) # type: ignore [misc]
129
128
  datetime: DateTimeQueryType = attr.ib(default=None, validator=_validate_datetime)
130
129
 
131
130
 
@@ -3,7 +3,8 @@
3
3
  import copy
4
4
  import functools
5
5
  import inspect
6
- from typing import Any, Awaitable, Callable, Dict, List, Optional, Type, TypedDict, Union
6
+ from collections.abc import Awaitable, Callable
7
+ from typing import Any, TypedDict
7
8
 
8
9
  from fastapi import Depends, FastAPI, params
9
10
  from fastapi.datastructures import DefaultPlaceholder
@@ -38,7 +39,7 @@ def sync_to_async(func):
38
39
 
39
40
  def create_async_endpoint(
40
41
  func: Callable,
41
- request_model: Union[Type[APIRequest], Type[BaseModel], Dict],
42
+ request_model: type[APIRequest] | type[BaseModel] | dict,
42
43
  ) -> Callable[[Any, Any], Awaitable[Any]]:
43
44
  """Wrap a function in a coroutine which may be used to create a FastAPI endpoint.
44
45
 
@@ -52,7 +53,7 @@ def create_async_endpoint(
52
53
 
53
54
  if isinstance(request_model, dict):
54
55
 
55
- async def _endpoint(request: Request, request_data: Dict[str, Any]):
56
+ async def _endpoint(request: Request, request_data: dict[str, Any]):
56
57
  """Endpoint."""
57
58
  return _wrap_response(await func(request_data, request=request))
58
59
 
@@ -80,7 +81,7 @@ class Scope(TypedDict, total=False):
80
81
  # https://github.com/encode/starlette/blob/6af5c515e0a896cbf3f86ee043b88f6c24200bcf/starlette/types.py#L3
81
82
  path: str
82
83
  method: str
83
- type: Optional[str]
84
+ type: str | None
84
85
 
85
86
 
86
87
  def _is_endpoint_route(route: BaseRoute) -> bool:
@@ -91,7 +92,7 @@ def _is_endpoint_route(route: BaseRoute) -> bool:
91
92
 
92
93
 
93
94
  def _apply_dependencies_to_route(
94
- route: BaseRoute, scopes: List[Scope], dependencies: List[params.Depends]
95
+ route: BaseRoute, scopes: list[Scope], dependencies: list[params.Depends]
95
96
  ) -> None:
96
97
  """Apply dependencies to a single route matching the given scopes."""
97
98
  for scope in scopes:
@@ -123,7 +124,7 @@ def _apply_dependencies_to_route(
123
124
 
124
125
 
125
126
  def add_route_dependencies(
126
- routes: List[BaseRoute], scopes: List[Scope], dependencies: List[params.Depends]
127
+ routes: list[BaseRoute], scopes: list[Scope], dependencies: list[params.Depends]
127
128
  ) -> None:
128
129
  """Add dependencies to routes.
129
130
 
@@ -158,7 +159,7 @@ def add_direct_response(app: FastAPI) -> None:
158
159
  ref: https://gist.github.com/Zaczero/00f3a2679ebc0a25eb938ed82bc63553
159
160
  """
160
161
 
161
- def wrap_endpoint(endpoint: Callable, cls: Type[Response]):
162
+ def wrap_endpoint(endpoint: Callable, cls: type[Response]):
162
163
  @functools.wraps(endpoint)
163
164
  async def wrapper(*args, **kwargs):
164
165
  content = await endpoint(*args, **kwargs)
@@ -1,3 +1,3 @@
1
1
  """Library version."""
2
2
 
3
- __version__ = "6.3.0"
3
+ __version__ = "6.3.1"