stac-fastapi.api 3.0.0__tar.gz → 3.0.0a0__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 (28) hide show
  1. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/PKG-INFO +3 -2
  2. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/setup.py +2 -1
  3. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi/api/app.py +43 -29
  4. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi/api/config.py +1 -3
  5. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi/api/models.py +60 -34
  6. stac_fastapi_api-3.0.0a0/stac_fastapi/api/openapi.py +87 -0
  7. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi/api/routes.py +9 -15
  8. stac_fastapi_api-3.0.0a0/stac_fastapi/api/version.py +2 -0
  9. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi.api.egg-info/PKG-INFO +3 -2
  10. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi.api.egg-info/requires.txt +2 -1
  11. stac_fastapi_api-3.0.0a0/tests/test_api.py +172 -0
  12. stac_fastapi_api-3.0.0a0/tests/test_app.py +188 -0
  13. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/tests/test_middleware.py +0 -30
  14. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/tests/test_models.py +12 -42
  15. stac_fastapi_api-3.0.0/stac_fastapi/api/openapi.py +0 -37
  16. stac_fastapi_api-3.0.0/stac_fastapi/api/version.py +0 -3
  17. stac_fastapi_api-3.0.0/tests/test_api.py +0 -456
  18. stac_fastapi_api-3.0.0/tests/test_app.py +0 -389
  19. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/README.md +0 -0
  20. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/setup.cfg +0 -0
  21. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi/api/__init__.py +0 -0
  22. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi/api/errors.py +0 -0
  23. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi/api/middleware.py +0 -0
  24. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi.api.egg-info/SOURCES.txt +0 -0
  25. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi.api.egg-info/dependency_links.txt +0 -0
  26. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi.api.egg-info/not-zip-safe +0 -0
  27. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/stac_fastapi.api.egg-info/top_level.txt +0 -0
  28. {stac_fastapi_api-3.0.0 → stac_fastapi_api-3.0.0a0}/tests/test_app_prefix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.api
3
- Version: 3.0.0
3
+ Version: 3.0.0a0
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
@@ -19,7 +19,7 @@ Classifier: License :: OSI Approved :: MIT License
19
19
  Requires-Python: >=3.8
20
20
  Description-Content-Type: text/markdown
21
21
  Requires-Dist: brotli_asgi
22
- Requires-Dist: stac-fastapi.types~=3.0
22
+ Requires-Dist: stac-fastapi.types
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: httpx; extra == "dev"
25
25
  Requires-Dist: pytest; extra == "dev"
@@ -27,6 +27,7 @@ Requires-Dist: pytest-cov; extra == "dev"
27
27
  Requires-Dist: pytest-asyncio; extra == "dev"
28
28
  Requires-Dist: pre-commit; extra == "dev"
29
29
  Requires-Dist: requests; extra == "dev"
30
+ Requires-Dist: pystac[validation]==1.*; extra == "dev"
30
31
  Provides-Extra: benchmark
31
32
  Requires-Dist: pytest-benchmark; extra == "benchmark"
32
33
  Provides-Extra: docs
@@ -7,7 +7,7 @@ with open("README.md") as f:
7
7
 
8
8
  install_requires = [
9
9
  "brotli_asgi",
10
- "stac-fastapi.types~=3.0",
10
+ "stac-fastapi.types",
11
11
  ]
12
12
 
13
13
  extra_reqs = {
@@ -18,6 +18,7 @@ extra_reqs = {
18
18
  "pytest-asyncio",
19
19
  "pre-commit",
20
20
  "requests",
21
+ "pystac[validation]==1.*",
21
22
  ],
22
23
  "benchmark": [
23
24
  "pytest-benchmark",
@@ -18,15 +18,18 @@ from starlette.responses import JSONResponse, Response
18
18
  from stac_fastapi.api.errors import DEFAULT_STATUS_CODES, add_exception_handlers
19
19
  from stac_fastapi.api.middleware import CORSMiddleware, ProxyHeaderMiddleware
20
20
  from stac_fastapi.api.models import (
21
- APIRequest,
22
21
  CollectionUri,
23
22
  EmptyRequest,
24
23
  GeoJSONResponse,
25
24
  ItemCollectionUri,
26
25
  ItemUri,
26
+ create_request_model,
27
27
  )
28
28
  from stac_fastapi.api.openapi import update_openapi
29
29
  from stac_fastapi.api.routes import Scope, add_route_dependencies, create_async_endpoint
30
+
31
+ # TODO: make this module not depend on `stac_fastapi.extensions`
32
+ from stac_fastapi.extensions.core import FieldsExtension, TokenPaginationExtension
30
33
  from stac_fastapi.types.config import ApiSettings, Settings
31
34
  from stac_fastapi.types.core import AsyncBaseCoreClient, BaseCoreClient
32
35
  from stac_fastapi.types.extension import ApiExtension
@@ -105,10 +108,7 @@ class StacApi:
105
108
  search_post_request_model: Type[BaseSearchPostRequest] = attr.ib(
106
109
  default=BaseSearchPostRequest
107
110
  )
108
- collections_get_request_model: Type[APIRequest] = attr.ib(default=EmptyRequest)
109
- collection_get_request_model: Type[APIRequest] = attr.ib(default=CollectionUri)
110
- items_get_request_model: Type[APIRequest] = attr.ib(default=ItemCollectionUri)
111
- item_get_request_model: Type[APIRequest] = attr.ib(default=ItemUri)
111
+ pagination_extension = attr.ib(default=TokenPaginationExtension)
112
112
  response_class: Type[Response] = attr.ib(default=JSONResponse)
113
113
  middlewares: List[Middleware] = attr.ib(
114
114
  default=attr.Factory(
@@ -172,14 +172,14 @@ class StacApi:
172
172
  name="Conformance Classes",
173
173
  path="/conformance",
174
174
  response_model=(
175
- api.Conformance if self.settings.enable_response_models else None
175
+ api.ConformanceClasses if self.settings.enable_response_models else None
176
176
  ),
177
177
  responses={
178
178
  200: {
179
179
  "content": {
180
180
  MimeTypes.json.value: {},
181
181
  },
182
- "model": api.Conformance,
182
+ "model": api.ConformanceClasses,
183
183
  },
184
184
  },
185
185
  response_class=self.response_class,
@@ -211,9 +211,7 @@ class StacApi:
211
211
  response_model_exclude_unset=True,
212
212
  response_model_exclude_none=True,
213
213
  methods=["GET"],
214
- endpoint=create_async_endpoint(
215
- self.client.get_item, self.item_get_request_model
216
- ),
214
+ endpoint=create_async_endpoint(self.client.get_item, ItemUri),
217
215
  )
218
216
 
219
217
  def register_post_search(self):
@@ -222,12 +220,15 @@ class StacApi:
222
220
  Returns:
223
221
  None
224
222
  """
223
+ fields_ext = self.get_extension(FieldsExtension)
225
224
  self.router.add_api_route(
226
225
  name="Search",
227
226
  path="/search",
228
- response_model=api.ItemCollection
229
- if self.settings.enable_response_models
230
- else None,
227
+ response_model=(
228
+ (api.ItemCollection if not fields_ext else None)
229
+ if self.settings.enable_response_models
230
+ else None
231
+ ),
231
232
  responses={
232
233
  200: {
233
234
  "content": {
@@ -251,12 +252,15 @@ class StacApi:
251
252
  Returns:
252
253
  None
253
254
  """
255
+ fields_ext = self.get_extension(FieldsExtension)
254
256
  self.router.add_api_route(
255
257
  name="Search",
256
258
  path="/search",
257
- response_model=api.ItemCollection
258
- if self.settings.enable_response_models
259
- else None,
259
+ response_model=(
260
+ (api.ItemCollection if not fields_ext else None)
261
+ if self.settings.enable_response_models
262
+ else None
263
+ ),
260
264
  responses={
261
265
  200: {
262
266
  "content": {
@@ -298,9 +302,7 @@ class StacApi:
298
302
  response_model_exclude_unset=True,
299
303
  response_model_exclude_none=True,
300
304
  methods=["GET"],
301
- endpoint=create_async_endpoint(
302
- self.client.all_collections, self.collections_get_request_model
303
- ),
305
+ endpoint=create_async_endpoint(self.client.all_collections, EmptyRequest),
304
306
  )
305
307
 
306
308
  def register_get_collection(self):
@@ -327,9 +329,7 @@ class StacApi:
327
329
  response_model_exclude_unset=True,
328
330
  response_model_exclude_none=True,
329
331
  methods=["GET"],
330
- endpoint=create_async_endpoint(
331
- self.client.get_collection, self.collection_get_request_model
332
- ),
332
+ endpoint=create_async_endpoint(self.client.get_collection, CollectionUri),
333
333
  )
334
334
 
335
335
  def register_get_item_collection(self):
@@ -338,6 +338,16 @@ class StacApi:
338
338
  Returns:
339
339
  None
340
340
  """
341
+ pagination_extension = self.get_extension(self.pagination_extension)
342
+ if pagination_extension is not None:
343
+ mixins = [pagination_extension.GET]
344
+ else:
345
+ mixins = None
346
+ request_model = create_request_model(
347
+ "ItemCollectionURI",
348
+ base_model=ItemCollectionUri,
349
+ mixins=mixins,
350
+ )
341
351
  self.router.add_api_route(
342
352
  name="Get ItemCollection",
343
353
  path="/collections/{collection_id}/items",
@@ -356,9 +366,7 @@ class StacApi:
356
366
  response_model_exclude_unset=True,
357
367
  response_model_exclude_none=True,
358
368
  methods=["GET"],
359
- endpoint=create_async_endpoint(
360
- self.client.item_collection, self.items_get_request_model
361
- ),
369
+ endpoint=create_async_endpoint(self.client.item_collection, request_model),
362
370
  )
363
371
 
364
372
  def register_core(self):
@@ -431,6 +439,11 @@ class StacApi:
431
439
  """
432
440
  return add_route_dependencies(self.app.router.routes, scopes, dependencies)
433
441
 
442
+ def add_middleware(self, middleware: Middleware):
443
+ """Add a middleware class to the application."""
444
+ self.app.user_middleware.insert(0, middleware)
445
+ self.app.middleware_stack = self.app.build_middleware_stack()
446
+
434
447
  def __attrs_post_init__(self):
435
448
  """Post-init hook.
436
449
 
@@ -445,6 +458,10 @@ class StacApi:
445
458
  self.client.title = self.title
446
459
  self.client.description = self.description
447
460
 
461
+ fields_ext = self.get_extension(FieldsExtension)
462
+ if fields_ext:
463
+ self.settings.default_includes = fields_ext.default_includes
464
+
448
465
  Settings.set(self.settings)
449
466
  self.app.state.settings = self.settings
450
467
 
@@ -470,11 +487,8 @@ class StacApi:
470
487
  self.app.openapi = self.customize_openapi
471
488
 
472
489
  # add middlewares
473
- if self.middlewares and self.app.middleware_stack is not None:
474
- raise RuntimeError("Cannot add middleware after an application has started")
475
-
476
490
  for middleware in self.middlewares:
477
- self.app.user_middleware.insert(0, middleware)
491
+ self.add_middleware(middleware)
478
492
 
479
493
  # customize route dependencies
480
494
  for scopes, dependencies in self.route_dependencies:
@@ -12,14 +12,12 @@ class ApiExtensions(enum.Enum):
12
12
  Ref: https://github.com/stac-api-extensions
13
13
  """
14
14
 
15
+ context = "context"
15
16
  fields = "fields"
16
17
  filter = "filter"
17
18
  query = "query"
18
19
  sort = "sort"
19
20
  transaction = "transaction"
20
- aggregation = "aggregation"
21
- collection_search = "collection-search"
22
- free_text = "free-text"
23
21
 
24
22
 
25
23
  class AddOns(enum.Enum):
@@ -1,12 +1,12 @@
1
1
  """Api request/response models."""
2
2
 
3
+ import importlib.util
3
4
  from typing import List, Optional, Type, Union
4
5
 
5
6
  import attr
6
- from fastapi import Path, Query
7
+ from fastapi import Path
7
8
  from pydantic import BaseModel, create_model
8
9
  from stac_pydantic.shared import BBox
9
- from typing_extensions import Annotated
10
10
 
11
11
  from stac_fastapi.types.extension import ApiExtension
12
12
  from stac_fastapi.types.rfc3339 import DateTimeType
@@ -14,17 +14,10 @@ from stac_fastapi.types.search import (
14
14
  APIRequest,
15
15
  BaseSearchGetRequest,
16
16
  BaseSearchPostRequest,
17
- Limit,
18
- _bbox_converter,
19
- _datetime_converter,
17
+ str2bbox,
18
+ str_to_interval,
20
19
  )
21
20
 
22
- try:
23
- import orjson # noqa
24
- from fastapi.responses import ORJSONResponse as JSONResponse
25
- except ImportError: # pragma: nocover
26
- from starlette.responses import JSONResponse
27
-
28
21
 
29
22
  def create_request_model(
30
23
  model_name="SearchGetRequest",
@@ -87,19 +80,18 @@ def create_post_request_model(
87
80
  )
88
81
 
89
82
 
90
- @attr.s
83
+ @attr.s # type:ignore
91
84
  class CollectionUri(APIRequest):
92
85
  """Get or delete collection."""
93
86
 
94
- collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
87
+ collection_id: str = attr.ib(default=Path(..., description="Collection ID"))
95
88
 
96
89
 
97
90
  @attr.s
98
- class ItemUri(APIRequest):
91
+ class ItemUri(CollectionUri):
99
92
  """Get or delete item."""
100
93
 
101
- collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
102
- item_id: Annotated[str, Path(description="Item ID")] = attr.ib()
94
+ item_id: str = attr.ib(default=Path(..., description="Item ID"))
103
95
 
104
96
 
105
97
  @attr.s
@@ -110,29 +102,63 @@ class EmptyRequest(APIRequest):
110
102
 
111
103
 
112
104
  @attr.s
113
- class ItemCollectionUri(APIRequest):
105
+ class ItemCollectionUri(CollectionUri):
114
106
  """Get item collection."""
115
107
 
116
- collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
117
- limit: Annotated[
118
- Optional[Limit],
119
- Query(
120
- description="Limits the number of results that are included in each page of the response (capped to 10_000)." # noqa: E501
121
- ),
122
- ] = attr.ib(default=10)
123
- bbox: Optional[BBox] = attr.ib(default=None, converter=_bbox_converter)
124
- datetime: Optional[DateTimeType] = attr.ib(
125
- default=None, converter=_datetime_converter
126
- )
108
+ limit: int = attr.ib(default=10)
109
+ bbox: Optional[BBox] = attr.ib(default=None, converter=str2bbox)
110
+ datetime: Optional[DateTimeType] = attr.ib(default=None, converter=str_to_interval)
111
+
112
+
113
+ class POSTTokenPagination(BaseModel):
114
+ """Token pagination model for POST requests."""
115
+
116
+ token: Optional[str] = None
117
+
118
+
119
+ @attr.s
120
+ class GETTokenPagination(APIRequest):
121
+ """Token pagination for GET requests."""
122
+
123
+ token: Optional[str] = attr.ib(default=None)
124
+
127
125
 
126
+ class POSTPagination(BaseModel):
127
+ """Page based pagination for POST requests."""
128
128
 
129
- class GeoJSONResponse(JSONResponse):
130
- """JSON with custom, vendor content-type."""
129
+ page: Optional[str] = None
130
+
131
+
132
+ @attr.s
133
+ class GETPagination(APIRequest):
134
+ """Page based pagination for GET requests."""
135
+
136
+ page: Optional[str] = attr.ib(default=None)
137
+
138
+
139
+ # Test for ORJSON and use it rather than stdlib JSON where supported
140
+ if importlib.util.find_spec("orjson") is not None:
141
+ from fastapi.responses import ORJSONResponse
142
+
143
+ class GeoJSONResponse(ORJSONResponse):
144
+ """JSON with custom, vendor content-type."""
145
+
146
+ media_type = "application/geo+json"
147
+
148
+ class JSONSchemaResponse(ORJSONResponse):
149
+ """JSON with custom, vendor content-type."""
150
+
151
+ media_type = "application/schema+json"
152
+
153
+ else:
154
+ from starlette.responses import JSONResponse
131
155
 
132
- media_type = "application/geo+json"
156
+ class GeoJSONResponse(JSONResponse):
157
+ """JSON with custom, vendor content-type."""
133
158
 
159
+ media_type = "application/geo+json"
134
160
 
135
- class JSONSchemaResponse(JSONResponse):
136
- """JSON with custom, vendor content-type."""
161
+ class JSONSchemaResponse(JSONResponse):
162
+ """JSON with custom, vendor content-type."""
137
163
 
138
- media_type = "application/schema+json"
164
+ media_type = "application/schema+json"
@@ -0,0 +1,87 @@
1
+ """openapi."""
2
+
3
+ import warnings
4
+
5
+ from fastapi import FastAPI
6
+ from fastapi.openapi.utils import get_openapi
7
+ from starlette.requests import Request
8
+ from starlette.responses import JSONResponse, Response
9
+ from starlette.routing import Route, request_response
10
+
11
+ from stac_fastapi.api.config import ApiExtensions
12
+ from stac_fastapi.types.config import ApiSettings
13
+
14
+
15
+ class VndOaiResponse(JSONResponse):
16
+ """JSON with custom, vendor content-type."""
17
+
18
+ media_type = "application/vnd.oai.openapi+json;version=3.0"
19
+
20
+ def __init__(self, *args, **kwargs):
21
+ """Init function with deprecation warning."""
22
+ warnings.warn(
23
+ "VndOaiResponse is deprecated and will be removed in v3.0",
24
+ DeprecationWarning,
25
+ )
26
+ super().__init__(*args, **kwargs)
27
+
28
+
29
+ def update_openapi(app: FastAPI) -> FastAPI:
30
+ """Update OpenAPI response content-type.
31
+
32
+ This function modifies the openapi route to comply with the STAC API spec's required
33
+ content-type response header.
34
+ """
35
+ # Find the route for the openapi_url in the app
36
+ openapi_route: Route = next(
37
+ route for route in app.router.routes if route.path == app.openapi_url
38
+ )
39
+ # Store the old endpoint function so we can call it from the patched function
40
+ old_endpoint = openapi_route.endpoint
41
+
42
+ # Create a patched endpoint function that modifies the content type of the response
43
+ async def patched_openapi_endpoint(req: Request) -> Response:
44
+ # Get the response from the old endpoint function
45
+ response: JSONResponse = await old_endpoint(req)
46
+ # Update the content type header in place
47
+ response.headers["content-type"] = "application/vnd.oai.openapi+json;version=3.0"
48
+ # Return the updated response
49
+ return response
50
+
51
+ # When a Route is accessed the `handle` function calls `self.app`. Which is
52
+ # the endpoint function wrapped with `request_response`. So we need to wrap
53
+ # our patched function and replace the existing app with it.
54
+ openapi_route.app = request_response(patched_openapi_endpoint)
55
+
56
+ # return the patched app
57
+ return app
58
+
59
+
60
+ def config_openapi(app: FastAPI, settings: ApiSettings):
61
+ """Config openapi."""
62
+ warnings.warn(
63
+ "config_openapi is deprecated and will be removed in v3.0",
64
+ DeprecationWarning,
65
+ )
66
+
67
+ def custom_openapi():
68
+ """Config openapi."""
69
+ if app.openapi_schema:
70
+ return app.openapi_schema
71
+
72
+ openapi_schema = get_openapi(
73
+ title="Arturo STAC API", version="0.1", routes=app.routes
74
+ )
75
+
76
+ if settings.api_extension_is_enabled(ApiExtensions.fields):
77
+ openapi_schema["paths"]["/search"]["get"]["responses"]["200"]["content"][
78
+ "application/json"
79
+ ]["schema"] = {"$ref": "#/components/schemas/ItemCollection"}
80
+ openapi_schema["paths"]["/search"]["post"]["responses"]["200"]["content"][
81
+ "application/json"
82
+ ]["schema"] = {"$ref": "#/components/schemas/ItemCollection"}
83
+
84
+ app.openapi_schema = openapi_schema
85
+ return app.openapi_schema
86
+
87
+ app.openapi = custom_openapi
@@ -1,8 +1,8 @@
1
1
  """Route factories."""
2
2
 
3
- import copy
4
3
  import functools
5
4
  import inspect
5
+ import warnings
6
6
  from typing import Any, Callable, Dict, List, Optional, Type, TypedDict, Union
7
7
 
8
8
  from fastapi import Depends, params
@@ -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: Optional[Type[Response]] = None,
40
41
  ):
41
42
  """Wrap a function in a coroutine which may be used to create a FastAPI endpoint.
42
43
 
43
44
  Synchronous functions are executed asynchronously using a background thread.
44
45
  """
45
46
 
47
+ if response_class:
48
+ warnings.warn(
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
 
@@ -93,28 +100,15 @@ def add_route_dependencies(
93
100
  Allows a developer to add dependencies to a route after the route has been
94
101
  defined.
95
102
 
96
- "*" can be used for path or method to match all allowed routes.
97
-
98
103
  Returns:
99
104
  None
100
105
  """
101
106
  for scope in scopes:
102
- _scope = copy.deepcopy(scope)
103
107
  for route in routes:
104
- if scope["path"] == "*":
105
- _scope["path"] = route.path
106
-
107
- if scope["method"] == "*":
108
- _scope["method"] = list(route.methods)[0]
109
-
110
- match, _ = route.matches({"type": "http", **_scope})
108
+ match, _ = route.matches({"type": "http", **scope})
111
109
  if match != Match.FULL:
112
110
  continue
113
111
 
114
- # Ignore paths without dependants, e.g. /api, /api.html, /docs/oauth2-redirect
115
- if not hasattr(route, "dependant"):
116
- continue
117
-
118
112
  # Mimicking how APIRoute handles dependencies:
119
113
  # https://github.com/tiangolo/fastapi/blob/1760da0efa55585c19835d81afa8ca386036c325/fastapi/routing.py#L408-L412
120
114
  for depends in dependencies[::-1]:
@@ -0,0 +1,2 @@
1
+ """Library version."""
2
+ __version__ = "3.0.0a0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.api
3
- Version: 3.0.0
3
+ Version: 3.0.0a0
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
@@ -19,7 +19,7 @@ Classifier: License :: OSI Approved :: MIT License
19
19
  Requires-Python: >=3.8
20
20
  Description-Content-Type: text/markdown
21
21
  Requires-Dist: brotli_asgi
22
- Requires-Dist: stac-fastapi.types~=3.0
22
+ Requires-Dist: stac-fastapi.types
23
23
  Provides-Extra: dev
24
24
  Requires-Dist: httpx; extra == "dev"
25
25
  Requires-Dist: pytest; extra == "dev"
@@ -27,6 +27,7 @@ Requires-Dist: pytest-cov; extra == "dev"
27
27
  Requires-Dist: pytest-asyncio; extra == "dev"
28
28
  Requires-Dist: pre-commit; extra == "dev"
29
29
  Requires-Dist: requests; extra == "dev"
30
+ Requires-Dist: pystac[validation]==1.*; extra == "dev"
30
31
  Provides-Extra: benchmark
31
32
  Requires-Dist: pytest-benchmark; extra == "benchmark"
32
33
  Provides-Extra: docs
@@ -1,5 +1,5 @@
1
1
  brotli_asgi
2
- stac-fastapi.types~=3.0
2
+ stac-fastapi.types
3
3
 
4
4
  [benchmark]
5
5
  pytest-benchmark
@@ -11,6 +11,7 @@ pytest-cov
11
11
  pytest-asyncio
12
12
  pre-commit
13
13
  requests
14
+ pystac[validation]==1.*
14
15
 
15
16
  [docs]
16
17
  mkdocs