stac-fastapi.api 2.4.9__tar.gz → 2.5.0__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.
Files changed (21) hide show
  1. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/PKG-INFO +7 -1
  2. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/setup.py +7 -0
  3. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/app.py +24 -23
  4. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/models.py +5 -3
  5. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/routes.py +16 -15
  6. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/version.py +1 -1
  7. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi.api.egg-info/PKG-INFO +7 -1
  8. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi.api.egg-info/requires.txt +3 -0
  9. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/tests/test_api.py +2 -2
  10. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/README.md +0 -0
  11. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/setup.cfg +0 -0
  12. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/__init__.py +0 -0
  13. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/config.py +0 -0
  14. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/errors.py +0 -0
  15. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/middleware.py +0 -0
  16. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi/api/openapi.py +0 -0
  17. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi.api.egg-info/SOURCES.txt +0 -0
  18. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi.api.egg-info/dependency_links.txt +0 -0
  19. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi.api.egg-info/not-zip-safe +0 -0
  20. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/stac_fastapi.api.egg-info/top_level.txt +0 -0
  21. {stac-fastapi.api-2.4.9 → stac_fastapi_api-2.5.0}/tests/test_middleware.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.api
3
- Version: 2.4.9
3
+ Version: 2.5.0
4
4
  Summary: An implementation of STAC API based on the FastAPI framework.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi
6
6
  Author: Arturo Engineering
@@ -11,6 +11,10 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: Intended Audience :: Information Technology
12
12
  Classifier: Intended Audience :: Science/Research
13
13
  Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
14
18
  Classifier: License :: OSI Approved :: MIT License
15
19
  Requires-Python: >=3.8
16
20
  Description-Content-Type: text/markdown
@@ -27,6 +31,8 @@ Requires-Dist: pytest-asyncio; extra == "dev"
27
31
  Requires-Dist: pre-commit; extra == "dev"
28
32
  Requires-Dist: requests; extra == "dev"
29
33
  Requires-Dist: pystac[validation]==1.*; extra == "dev"
34
+ Provides-Extra: benchmark
35
+ Requires-Dist: pytest-benchmark; extra == "benchmark"
30
36
  Provides-Extra: docs
31
37
  Requires-Dist: mkdocs; extra == "docs"
32
38
  Requires-Dist: mkdocs-material; extra == "docs"
@@ -23,6 +23,9 @@ extra_reqs = {
23
23
  "requests",
24
24
  "pystac[validation]==1.*",
25
25
  ],
26
+ "benchmark": [
27
+ "pytest-benchmark",
28
+ ],
26
29
  "docs": ["mkdocs", "mkdocs-material", "pdocs"],
27
30
  }
28
31
 
@@ -38,6 +41,10 @@ setup(
38
41
  "Intended Audience :: Information Technology",
39
42
  "Intended Audience :: Science/Research",
40
43
  "Programming Language :: Python :: 3.8",
44
+ "Programming Language :: Python :: 3.9",
45
+ "Programming Language :: Python :: 3.10",
46
+ "Programming Language :: Python :: 3.11",
47
+ "Programming Language :: Python :: 3.12",
41
48
  "License :: OSI Approved :: MIT License",
42
49
  ],
43
50
  keywords="STAC FastAPI COG",
@@ -1,4 +1,5 @@
1
1
  """Fastapi app creation."""
2
+
2
3
  from typing import Any, Dict, List, Optional, Tuple, Type, Union
3
4
 
4
5
  import attr
@@ -83,10 +84,22 @@ class StacApi:
83
84
  converter=update_openapi,
84
85
  )
85
86
  router: APIRouter = attr.ib(default=attr.Factory(APIRouter))
86
- title: str = attr.ib(default="stac-fastapi")
87
- api_version: str = attr.ib(default="0.1")
87
+ title: str = attr.ib(
88
+ default=attr.Factory(
89
+ lambda self: self.settings.stac_fastapi_title, takes_self=True
90
+ )
91
+ )
92
+ api_version: str = attr.ib(
93
+ default=attr.Factory(
94
+ lambda self: self.settings.stac_fastapi_version, takes_self=True
95
+ )
96
+ )
88
97
  stac_version: str = attr.ib(default=STAC_VERSION)
89
- description: str = attr.ib(default="stac-fastapi")
98
+ description: str = attr.ib(
99
+ default=attr.Factory(
100
+ lambda self: self.settings.stac_fastapi_description, takes_self=True
101
+ )
102
+ )
90
103
  search_get_request_model: Type[BaseSearchGetRequest] = attr.ib(
91
104
  default=BaseSearchGetRequest
92
105
  )
@@ -132,9 +145,7 @@ class StacApi:
132
145
  response_model_exclude_unset=False,
133
146
  response_model_exclude_none=True,
134
147
  methods=["GET"],
135
- endpoint=create_async_endpoint(
136
- self.client.landing_page, EmptyRequest, self.response_class
137
- ),
148
+ endpoint=create_async_endpoint(self.client.landing_page, EmptyRequest),
138
149
  )
139
150
 
140
151
  def register_conformance_classes(self):
@@ -153,9 +164,7 @@ class StacApi:
153
164
  response_model_exclude_unset=True,
154
165
  response_model_exclude_none=True,
155
166
  methods=["GET"],
156
- endpoint=create_async_endpoint(
157
- self.client.conformance, EmptyRequest, self.response_class
158
- ),
167
+ endpoint=create_async_endpoint(self.client.conformance, EmptyRequest),
159
168
  )
160
169
 
161
170
  def register_get_item(self):
@@ -172,9 +181,7 @@ class StacApi:
172
181
  response_model_exclude_unset=True,
173
182
  response_model_exclude_none=True,
174
183
  methods=["GET"],
175
- endpoint=create_async_endpoint(
176
- self.client.get_item, ItemUri, GeoJSONResponse
177
- ),
184
+ endpoint=create_async_endpoint(self.client.get_item, ItemUri),
178
185
  )
179
186
 
180
187
  def register_post_search(self):
@@ -195,7 +202,7 @@ class StacApi:
195
202
  response_model_exclude_none=True,
196
203
  methods=["POST"],
197
204
  endpoint=create_async_endpoint(
198
- self.client.post_search, self.search_post_request_model, GeoJSONResponse
205
+ self.client.post_search, self.search_post_request_model
199
206
  ),
200
207
  )
201
208
 
@@ -217,7 +224,7 @@ class StacApi:
217
224
  response_model_exclude_none=True,
218
225
  methods=["GET"],
219
226
  endpoint=create_async_endpoint(
220
- self.client.get_search, self.search_get_request_model, GeoJSONResponse
227
+ self.client.get_search, self.search_get_request_model
221
228
  ),
222
229
  )
223
230
 
@@ -237,9 +244,7 @@ class StacApi:
237
244
  response_model_exclude_unset=True,
238
245
  response_model_exclude_none=True,
239
246
  methods=["GET"],
240
- endpoint=create_async_endpoint(
241
- self.client.all_collections, EmptyRequest, self.response_class
242
- ),
247
+ endpoint=create_async_endpoint(self.client.all_collections, EmptyRequest),
243
248
  )
244
249
 
245
250
  def register_get_collection(self):
@@ -256,9 +261,7 @@ class StacApi:
256
261
  response_model_exclude_unset=True,
257
262
  response_model_exclude_none=True,
258
263
  methods=["GET"],
259
- endpoint=create_async_endpoint(
260
- self.client.get_collection, CollectionUri, self.response_class
261
- ),
264
+ endpoint=create_async_endpoint(self.client.get_collection, CollectionUri),
262
265
  )
263
266
 
264
267
  def register_get_item_collection(self):
@@ -287,9 +290,7 @@ class StacApi:
287
290
  response_model_exclude_unset=True,
288
291
  response_model_exclude_none=True,
289
292
  methods=["GET"],
290
- endpoint=create_async_endpoint(
291
- self.client.item_collection, request_model, GeoJSONResponse
292
- ),
293
+ endpoint=create_async_endpoint(self.client.item_collection, request_model),
293
294
  )
294
295
 
295
296
  def register_core(self):
@@ -7,13 +7,15 @@ import attr
7
7
  from fastapi import Body, Path
8
8
  from pydantic import BaseModel, create_model
9
9
  from pydantic.fields import UndefinedType
10
+ from stac_pydantic.shared import BBox
10
11
 
11
12
  from stac_fastapi.types.extension import ApiExtension
13
+ from stac_fastapi.types.rfc3339 import DateTimeType
12
14
  from stac_fastapi.types.search import (
13
15
  APIRequest,
14
16
  BaseSearchGetRequest,
15
17
  BaseSearchPostRequest,
16
- str2list,
18
+ str2bbox,
17
19
  )
18
20
 
19
21
 
@@ -124,8 +126,8 @@ class ItemCollectionUri(CollectionUri):
124
126
  """Get item collection."""
125
127
 
126
128
  limit: int = attr.ib(default=10)
127
- bbox: Optional[str] = attr.ib(default=None, converter=str2list)
128
- datetime: Optional[str] = attr.ib(default=None)
129
+ bbox: Optional[BBox] = attr.ib(default=None, converter=str2bbox)
130
+ datetime: Optional[DateTimeType] = attr.ib(default=None)
129
131
 
130
132
 
131
133
  class POSTTokenPagination(BaseModel):
@@ -1,6 +1,8 @@
1
1
  """Route factories."""
2
+
2
3
  import functools
3
4
  import inspect
5
+ import warnings
4
6
  from typing import Any, Callable, Dict, List, Optional, Type, TypedDict, Union
5
7
 
6
8
  from fastapi import Depends, params
@@ -8,18 +10,16 @@ from fastapi.dependencies.utils import get_parameterless_sub_dependant
8
10
  from pydantic import BaseModel
9
11
  from starlette.concurrency import run_in_threadpool
10
12
  from starlette.requests import Request
11
- from starlette.responses import JSONResponse, Response
13
+ from starlette.responses import Response
12
14
  from starlette.routing import BaseRoute, Match
13
15
  from starlette.status import HTTP_204_NO_CONTENT
14
16
 
15
17
  from stac_fastapi.api.models import APIRequest
16
18
 
17
19
 
18
- def _wrap_response(resp: Any, response_class: Type[Response]) -> Response:
19
- if isinstance(resp, Response):
20
+ def _wrap_response(resp: Any) -> Any:
21
+ if resp is not None:
20
22
  return resp
21
- elif resp is not None:
22
- return response_class(resp)
23
23
  else: # None is returned as 204 No Content
24
24
  return Response(status_code=HTTP_204_NO_CONTENT)
25
25
 
@@ -37,12 +37,19 @@ def sync_to_async(func):
37
37
  def create_async_endpoint(
38
38
  func: Callable,
39
39
  request_model: Union[Type[APIRequest], Type[BaseModel], Dict],
40
- response_class: Type[Response] = JSONResponse,
40
+ response_class: Optional[Type[Response]] = None,
41
41
  ):
42
42
  """Wrap a function in a coroutine which may be used to create a FastAPI endpoint.
43
43
 
44
44
  Synchronous functions are executed asynchronously using a background thread.
45
45
  """
46
+
47
+ if response_class:
48
+ warnings.warns(
49
+ "`response_class` option is deprecated, please set the Response class directly in the endpoint.", # noqa: E501
50
+ DeprecationWarning,
51
+ )
52
+
46
53
  if not inspect.iscoroutinefunction(func):
47
54
  func = sync_to_async(func)
48
55
 
@@ -53,9 +60,7 @@ def create_async_endpoint(
53
60
  request_data: request_model = Depends(), # type:ignore
54
61
  ):
55
62
  """Endpoint."""
56
- return _wrap_response(
57
- await func(request=request, **request_data.kwargs()), response_class
58
- )
63
+ return _wrap_response(await func(request=request, **request_data.kwargs()))
59
64
 
60
65
  elif issubclass(request_model, BaseModel):
61
66
 
@@ -64,9 +69,7 @@ def create_async_endpoint(
64
69
  request_data: request_model, # type:ignore
65
70
  ):
66
71
  """Endpoint."""
67
- return _wrap_response(
68
- await func(request_data, request=request), response_class
69
- )
72
+ return _wrap_response(await func(request_data, request=request))
70
73
 
71
74
  else:
72
75
 
@@ -75,9 +78,7 @@ def create_async_endpoint(
75
78
  request_data: Dict[str, Any], # type:ignore
76
79
  ):
77
80
  """Endpoint."""
78
- return _wrap_response(
79
- await func(request_data, request=request), response_class
80
- )
81
+ return _wrap_response(await func(request_data, request=request))
81
82
 
82
83
  return _endpoint
83
84
 
@@ -1,2 +1,2 @@
1
1
  """Library version."""
2
- __version__ = "2.4.9"
2
+ __version__ = "2.5.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.api
3
- Version: 2.4.9
3
+ Version: 2.5.0
4
4
  Summary: An implementation of STAC API based on the FastAPI framework.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi
6
6
  Author: Arturo Engineering
@@ -11,6 +11,10 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: Intended Audience :: Information Technology
12
12
  Classifier: Intended Audience :: Science/Research
13
13
  Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
14
18
  Classifier: License :: OSI Approved :: MIT License
15
19
  Requires-Python: >=3.8
16
20
  Description-Content-Type: text/markdown
@@ -27,6 +31,8 @@ Requires-Dist: pytest-asyncio; extra == "dev"
27
31
  Requires-Dist: pre-commit; extra == "dev"
28
32
  Requires-Dist: requests; extra == "dev"
29
33
  Requires-Dist: pystac[validation]==1.*; extra == "dev"
34
+ Provides-Extra: benchmark
35
+ Requires-Dist: pytest-benchmark; extra == "benchmark"
30
36
  Provides-Extra: docs
31
37
  Requires-Dist: mkdocs; extra == "docs"
32
38
  Requires-Dist: mkdocs-material; extra == "docs"
@@ -4,6 +4,9 @@ stac_pydantic==2.0.*
4
4
  brotli_asgi
5
5
  stac-fastapi.types
6
6
 
7
+ [benchmark]
8
+ pytest-benchmark
9
+
7
10
  [dev]
8
11
  httpx
9
12
  pytest
@@ -61,7 +61,7 @@ class TestRouteDependencies:
61
61
  def test_build_api_with_route_dependencies(self):
62
62
  routes = [
63
63
  {"path": "/collections", "method": "POST"},
64
- {"path": "/collections", "method": "PUT"},
64
+ {"path": "/collections/{collectionId}", "method": "PUT"},
65
65
  {"path": "/collections/{collectionId}", "method": "DELETE"},
66
66
  {"path": "/collections/{collectionId}/items", "method": "POST"},
67
67
  {"path": "/collections/{collectionId}/items/{itemId}", "method": "PUT"},
@@ -74,7 +74,7 @@ class TestRouteDependencies:
74
74
  def test_add_route_dependencies_after_building_api(self):
75
75
  routes = [
76
76
  {"path": "/collections", "method": "POST"},
77
- {"path": "/collections", "method": "PUT"},
77
+ {"path": "/collections/{collectionId}", "method": "PUT"},
78
78
  {"path": "/collections/{collectionId}", "method": "DELETE"},
79
79
  {"path": "/collections/{collectionId}/items", "method": "POST"},
80
80
  {"path": "/collections/{collectionId}/items/{itemId}", "method": "PUT"},