stac-fastapi.types 2.4.9__tar.gz → 2.5.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.
Files changed (24) hide show
  1. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/PKG-INFO +5 -1
  2. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/setup.py +4 -0
  3. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/config.py +5 -0
  4. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/core.py +60 -29
  5. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/links.py +1 -1
  6. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/rfc3339.py +13 -3
  7. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/search.py +36 -42
  8. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/stac.py +3 -1
  9. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/version.py +1 -1
  10. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi.types.egg-info/PKG-INFO +5 -1
  11. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/README.md +0 -0
  12. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/setup.cfg +0 -0
  13. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/__init__.py +0 -0
  14. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/conformance.py +0 -0
  15. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/errors.py +0 -0
  16. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/extension.py +0 -0
  17. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi/types/requests.py +0 -0
  18. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi.types.egg-info/SOURCES.txt +0 -0
  19. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi.types.egg-info/dependency_links.txt +0 -0
  20. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi.types.egg-info/not-zip-safe +0 -0
  21. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi.types.egg-info/requires.txt +0 -0
  22. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/stac_fastapi.types.egg-info/top_level.txt +0 -0
  23. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/tests/test_limit.py +0 -0
  24. {stac-fastapi.types-2.4.9 → stac_fastapi_types-2.5.1}/tests/test_rfc3339.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.types
3
- Version: 2.4.9
3
+ Version: 2.5.1
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
@@ -37,6 +37,10 @@ setup(
37
37
  "Intended Audience :: Information Technology",
38
38
  "Intended Audience :: Science/Research",
39
39
  "Programming Language :: Python :: 3.8",
40
+ "Programming Language :: Python :: 3.9",
41
+ "Programming Language :: Python :: 3.10",
42
+ "Programming Language :: Python :: 3.11",
43
+ "Programming Language :: Python :: 3.12",
40
44
  "License :: OSI Approved :: MIT License",
41
45
  ],
42
46
  keywords="STAC FastAPI COG",
@@ -22,6 +22,11 @@ class ApiSettings(BaseSettings):
22
22
  # `pydantic.BaseSettings` instead
23
23
  default_includes: Optional[Set[str]] = None
24
24
 
25
+ stac_fastapi_title: str = "stac-fastapi"
26
+ stac_fastapi_description: str = "stac-fastapi"
27
+ stac_fastapi_version: str = "0.1"
28
+ stac_fastapi_landing_id: str = "stac-fastapi"
29
+
25
30
  app_host: str = "0.0.0.0"
26
31
  app_port: int = 8000
27
32
  reload: bool = True
@@ -1,26 +1,30 @@
1
1
  """Base clients."""
2
+
2
3
  import abc
3
- from datetime import datetime
4
4
  from typing import Any, Dict, List, Optional, Union
5
5
  from urllib.parse import urljoin
6
6
 
7
7
  import attr
8
8
  from fastapi import Request
9
9
  from stac_pydantic.links import Relations
10
- from stac_pydantic.shared import MimeTypes
10
+ from stac_pydantic.shared import BBox, MimeTypes
11
11
  from stac_pydantic.version import STAC_VERSION
12
12
  from starlette.responses import Response
13
13
 
14
14
  from stac_fastapi.types import stac as stac_types
15
+ from stac_fastapi.types.config import ApiSettings
15
16
  from stac_fastapi.types.conformance import BASE_CONFORMANCE_CLASSES
16
17
  from stac_fastapi.types.extension import ApiExtension
17
18
  from stac_fastapi.types.requests import get_base_url
19
+ from stac_fastapi.types.rfc3339 import DateTimeType
18
20
  from stac_fastapi.types.search import BaseSearchPostRequest
19
21
  from stac_fastapi.types.stac import Conformance
20
22
 
21
23
  NumType = Union[float, int]
22
24
  StacType = Dict[str, Any]
23
25
 
26
+ api_settings = ApiSettings()
27
+
24
28
 
25
29
  @attr.s # type:ignore
26
30
  class BaseTransactionsClient(abc.ABC):
@@ -101,18 +105,18 @@ class BaseTransactionsClient(abc.ABC):
101
105
 
102
106
  @abc.abstractmethod
103
107
  def update_collection(
104
- self, collection: stac_types.Collection, **kwargs
108
+ self, collection_id: str, collection: stac_types.Collection, **kwargs
105
109
  ) -> Optional[Union[stac_types.Collection, Response]]:
106
110
  """Perform a complete update on an existing collection.
107
111
 
108
- Called with `PUT /collections`. It is expected that this item already
109
- exists. The update should do a diff against the saved collection and
112
+ Called with `PUT /collections/{collection_id}`. It is expected that this item
113
+ already exists. The update should do a diff against the saved collection and
110
114
  perform any necessary updates. Partial updates are not supported by the
111
115
  transactions extension.
112
116
 
113
117
  Args:
114
- collection: the collection (must be complete)
115
- collection_id: the id of the collection from the resource path
118
+ collection_id: id of the existing collection to be updated
119
+ collection: the updated collection (must be complete)
116
120
 
117
121
  Returns:
118
122
  The updated collection.
@@ -214,17 +218,18 @@ class AsyncBaseTransactionsClient(abc.ABC):
214
218
 
215
219
  @abc.abstractmethod
216
220
  async def update_collection(
217
- self, collection: stac_types.Collection, **kwargs
221
+ self, collection_id: str, collection: stac_types.Collection, **kwargs
218
222
  ) -> Optional[Union[stac_types.Collection, Response]]:
219
223
  """Perform a complete update on an existing collection.
220
224
 
221
- Called with `PUT /collections`. It is expected that this item already
222
- exists. The update should do a diff against the saved collection and
225
+ Called with `PUT /collections/{collection_id}`. It is expected that this item
226
+ already exists. The update should do a diff against the saved collection and
223
227
  perform any necessary updates. Partial updates are not supported by the
224
228
  transactions extension.
225
229
 
226
230
  Args:
227
- collection: the collection (must be complete)
231
+ collection_id: id of the existing collection to be updated
232
+ collection: the updated collection (must be complete)
228
233
 
229
234
  Returns:
230
235
  The updated collection.
@@ -253,9 +258,9 @@ class LandingPageMixin(abc.ABC):
253
258
  """Create a STAC landing page (GET /)."""
254
259
 
255
260
  stac_version: str = attr.ib(default=STAC_VERSION)
256
- landing_page_id: str = attr.ib(default="stac-fastapi")
257
- title: str = attr.ib(default="stac-fastapi")
258
- description: str = attr.ib(default="stac-fastapi")
261
+ landing_page_id: str = attr.ib(default=api_settings.stac_fastapi_landing_id)
262
+ title: str = attr.ib(default=api_settings.stac_fastapi_title)
263
+ description: str = attr.ib(default=api_settings.stac_fastapi_description)
259
264
 
260
265
  def _landing_page(
261
266
  self,
@@ -289,7 +294,7 @@ class LandingPageMixin(abc.ABC):
289
294
  {
290
295
  "rel": Relations.conformance.value,
291
296
  "type": MimeTypes.json,
292
- "title": "STAC/WFS3 conformance classes implemented by this server",
297
+ "title": "STAC/OGC conformance classes implemented by this server",
293
298
  "href": urljoin(base_url, "conformance"),
294
299
  },
295
300
  {
@@ -367,6 +372,20 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
367
372
  extension_schemas=[],
368
373
  )
369
374
 
375
+ # Add Queryables link
376
+ if self.extension_is_enabled("FilterExtension"):
377
+ landing_page["links"].append(
378
+ {
379
+ # TODO: replace this with Relations.queryables.value,
380
+ "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
381
+ # TODO: replace this with MimeTypes.jsonschema,
382
+ "type": "application/schema+json",
383
+ "title": "Queryables",
384
+ "href": urljoin(base_url, "queryables"),
385
+ "method": "GET",
386
+ }
387
+ )
388
+
370
389
  # Add Collections links
371
390
  collections = self.all_collections(request=kwargs["request"])
372
391
  for collection in collections["collections"]:
@@ -436,8 +455,8 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
436
455
  self,
437
456
  collections: Optional[List[str]] = None,
438
457
  ids: Optional[List[str]] = None,
439
- bbox: Optional[List[NumType]] = None,
440
- datetime: Optional[Union[str, datetime]] = None,
458
+ bbox: Optional[BBox] = None,
459
+ datetime: Optional[DateTimeType] = None,
441
460
  limit: Optional[int] = 10,
442
461
  query: Optional[str] = None,
443
462
  token: Optional[str] = None,
@@ -499,8 +518,8 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
499
518
  def item_collection(
500
519
  self,
501
520
  collection_id: str,
502
- bbox: Optional[List[NumType]] = None,
503
- datetime: Optional[Union[str, datetime]] = None,
521
+ bbox: Optional[BBox] = None,
522
+ datetime: Optional[DateTimeType] = None,
504
523
  limit: int = 10,
505
524
  token: str = None,
506
525
  **kwargs,
@@ -564,6 +583,22 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
564
583
  conformance_classes=self.conformance_classes(),
565
584
  extension_schemas=[],
566
585
  )
586
+
587
+ # Add Queryables link
588
+ if self.extension_is_enabled("FilterExtension"):
589
+ landing_page["links"].append(
590
+ {
591
+ # TODO: replace this with Relations.queryables.value,
592
+ "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
593
+ # TODO: replace this with MimeTypes.jsonschema,
594
+ "type": "application/schema+json",
595
+ "title": "Queryables",
596
+ "href": urljoin(base_url, "queryables"),
597
+ "method": "GET",
598
+ }
599
+ )
600
+
601
+ # Add Collections links
567
602
  collections = await self.all_collections(request=kwargs["request"])
568
603
  for collection in collections["collections"]:
569
604
  landing_page["links"].append(
@@ -581,9 +616,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
581
616
  "rel": "service-desc",
582
617
  "type": "application/vnd.oai.openapi+json;version=3.0",
583
618
  "title": "OpenAPI service description",
584
- "href": urljoin(
585
- str(request.base_url), request.app.openapi_url.lstrip("/")
586
- ),
619
+ "href": urljoin(base_url, request.app.openapi_url.lstrip("/")),
587
620
  }
588
621
  )
589
622
 
@@ -593,9 +626,7 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
593
626
  "rel": "service-doc",
594
627
  "type": "text/html",
595
628
  "title": "OpenAPI service documentation",
596
- "href": urljoin(
597
- str(request.base_url), request.app.docs_url.lstrip("/")
598
- ),
629
+ "href": urljoin(base_url, request.app.docs_url.lstrip("/")),
599
630
  }
600
631
  )
601
632
 
@@ -632,8 +663,8 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
632
663
  self,
633
664
  collections: Optional[List[str]] = None,
634
665
  ids: Optional[List[str]] = None,
635
- bbox: Optional[List[NumType]] = None,
636
- datetime: Optional[Union[str, datetime]] = None,
666
+ bbox: Optional[BBox] = None,
667
+ datetime: Optional[DateTimeType] = None,
637
668
  limit: Optional[int] = 10,
638
669
  query: Optional[str] = None,
639
670
  token: Optional[str] = None,
@@ -699,8 +730,8 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
699
730
  async def item_collection(
700
731
  self,
701
732
  collection_id: str,
702
- bbox: Optional[List[NumType]] = None,
703
- datetime: Optional[Union[str, datetime]] = None,
733
+ bbox: Optional[BBox] = None,
734
+ datetime: Optional[DateTimeType] = None,
704
735
  limit: int = 10,
705
736
  token: str = None,
706
737
  **kwargs,
@@ -10,7 +10,7 @@ from stac_pydantic.shared import MimeTypes
10
10
  # These can be inferred from the item/collection so they aren't included in the database
11
11
  # Instead they are dynamically generated when querying the database using the
12
12
  # classes defined below
13
- INFERRED_LINK_RELS = ["self", "item", "parent", "collection", "root"]
13
+ INFERRED_LINK_RELS = ["self", "item", "parent", "collection", "root", "items"]
14
14
 
15
15
 
16
16
  def filter_links(links: List[Dict]) -> List[Dict]:
@@ -1,7 +1,7 @@
1
1
  """rfc3339."""
2
2
  import re
3
3
  from datetime import datetime, timezone
4
- from typing import Optional, Tuple
4
+ from typing import Optional, Tuple, Union
5
5
 
6
6
  import iso8601
7
7
  from pystac.utils import datetime_to_str
@@ -11,6 +11,13 @@ RFC33339_PATTERN = (
11
11
  r"(Z|([-+])(\d\d):(\d\d))$"
12
12
  )
13
13
 
14
+ DateTimeType = Union[
15
+ datetime,
16
+ Tuple[datetime, datetime],
17
+ Tuple[datetime, None],
18
+ Tuple[None, datetime],
19
+ ]
20
+
14
21
 
15
22
  def rfc3339_str_to_datetime(s: str) -> datetime:
16
23
  """Convert a string conforming to RFC 3339 to a :class:`datetime.datetime`.
@@ -40,7 +47,7 @@ def rfc3339_str_to_datetime(s: str) -> datetime:
40
47
 
41
48
  def str_to_interval(
42
49
  interval: str,
43
- ) -> Optional[Tuple[Optional[datetime], Optional[datetime]]]:
50
+ ) -> Optional[DateTimeType]:
44
51
  """Extract a tuple of datetimes from an interval string.
45
52
 
46
53
  Interval strings are defined by
@@ -59,7 +66,10 @@ def str_to_interval(
59
66
  raise ValueError("Empty interval string is invalid.")
60
67
 
61
68
  values = interval.split("/")
62
- if len(values) != 2:
69
+ if len(values) == 1:
70
+ # Single date for == date case
71
+ return rfc3339_str_to_datetime(values[0])
72
+ elif len(values) > 2:
63
73
  raise ValueError(
64
74
  f"Interval string '{interval}' contains more than one forward slash."
65
75
  )
@@ -12,6 +12,7 @@ from typing import Any, Callable, Dict, Generator, List, Optional, Union
12
12
 
13
13
  import attr
14
14
  from geojson_pydantic.geometries import (
15
+ GeometryCollection,
15
16
  LineString,
16
17
  MultiLineString,
17
18
  MultiPoint,
@@ -20,13 +21,13 @@ from geojson_pydantic.geometries import (
20
21
  Polygon,
21
22
  _GeometryBase,
22
23
  )
23
- from pydantic import BaseModel, ConstrainedInt, validator
24
+ from pydantic import BaseModel, ConstrainedInt, Field, validator
24
25
  from pydantic.errors import NumberNotGtError
25
26
  from pydantic.validators import int_validator
26
27
  from stac_pydantic.shared import BBox
27
28
  from stac_pydantic.utils import AutoValueEnum
28
29
 
29
- from stac_fastapi.types.rfc3339 import rfc3339_str_to_datetime, str_to_interval
30
+ from stac_fastapi.types.rfc3339 import DateTimeType, str_to_interval
30
31
 
31
32
  # Be careful: https://github.com/samuelcolvin/pydantic/issues/1423#issuecomment-642797287
32
33
  NumType = Union[float, int]
@@ -82,6 +83,14 @@ def str2list(x: str) -> Optional[List]:
82
83
  return x.split(",")
83
84
 
84
85
 
86
+ def str2bbox(x: str) -> Optional[BBox]:
87
+ """Convert string to BBox based on , delimiter."""
88
+ if x:
89
+ t = tuple(float(v) for v in str2list(x))
90
+ assert len(t) == 4
91
+ return t
92
+
93
+
85
94
  @attr.s # type:ignore
86
95
  class APIRequest(abc.ABC):
87
96
  """Generic API Request base class."""
@@ -98,9 +107,9 @@ class BaseSearchGetRequest(APIRequest):
98
107
 
99
108
  collections: Optional[str] = attr.ib(default=None, converter=str2list)
100
109
  ids: Optional[str] = attr.ib(default=None, converter=str2list)
101
- bbox: Optional[str] = attr.ib(default=None, converter=str2list)
102
- intersects: Optional[str] = attr.ib(default=None)
103
- datetime: Optional[str] = attr.ib(default=None)
110
+ bbox: Optional[BBox] = attr.ib(default=None, converter=str2bbox)
111
+ intersects: Optional[str] = attr.ib(default=None, converter=str2list)
112
+ datetime: Optional[DateTimeType] = attr.ib(default=None, converter=str_to_interval)
104
113
  limit: Optional[int] = attr.ib(default=10)
105
114
 
106
115
 
@@ -119,22 +128,28 @@ class BaseSearchPostRequest(BaseModel):
119
128
  ids: Optional[List[str]]
120
129
  bbox: Optional[BBox]
121
130
  intersects: Optional[
122
- Union[Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon]
131
+ Union[
132
+ Point,
133
+ MultiPoint,
134
+ LineString,
135
+ MultiLineString,
136
+ Polygon,
137
+ MultiPolygon,
138
+ GeometryCollection,
139
+ ]
123
140
  ]
124
- datetime: Optional[str]
125
- limit: Optional[Limit] = 10
141
+ datetime: Optional[DateTimeType]
142
+ limit: Optional[Limit] = Field(default=10)
126
143
 
127
144
  @property
128
145
  def start_date(self) -> Optional[datetime]:
129
146
  """Extract the start date from the datetime string."""
130
- interval = str_to_interval(self.datetime)
131
- return interval[0] if interval else None
147
+ return self.datetime[0] if self.datetime else None
132
148
 
133
149
  @property
134
150
  def end_date(self) -> Optional[datetime]:
135
151
  """Extract the end date from the datetime string."""
136
- interval = str_to_interval(self.datetime)
137
- return interval[1] if interval else None
152
+ return self.datetime[1] if self.datetime else None
138
153
 
139
154
  @validator("intersects")
140
155
  def validate_spatial(cls, v, values):
@@ -143,10 +158,12 @@ class BaseSearchPostRequest(BaseModel):
143
158
  raise ValueError("intersects and bbox parameters are mutually exclusive")
144
159
  return v
145
160
 
146
- @validator("bbox")
147
- def validate_bbox(cls, v: BBox):
161
+ @validator("bbox", pre=True)
162
+ def validate_bbox(cls, v: Union[str, BBox]) -> BBox:
148
163
  """Check order of supplied bbox coordinates."""
149
164
  if v:
165
+ if type(v) == str:
166
+ v = str2bbox(v)
150
167
  # Validate order
151
168
  if len(v) == 4:
152
169
  xmin, ymin, xmax, ymax = v
@@ -173,34 +190,11 @@ class BaseSearchPostRequest(BaseModel):
173
190
 
174
191
  return v
175
192
 
176
- @validator("datetime")
177
- def validate_datetime(cls, v):
178
- """Validate datetime."""
179
- if "/" in v:
180
- values = v.split("/")
181
- else:
182
- # Single date is interpreted as end date
183
- values = ["..", v]
184
-
185
- dates = []
186
- for value in values:
187
- if value == ".." or value == "":
188
- dates.append("..")
189
- continue
190
-
191
- # throws ValueError if invalid RFC 3339 string
192
- dates.append(rfc3339_str_to_datetime(value))
193
-
194
- if dates[0] == ".." and dates[1] == "..":
195
- raise ValueError(
196
- "Invalid datetime range, both ends of range may not be open"
197
- )
198
-
199
- if ".." not in dates and dates[0] > dates[1]:
200
- raise ValueError(
201
- "Invalid datetime range, must match format (begin_date, end_date)"
202
- )
203
-
193
+ @validator("datetime", pre=True)
194
+ def validate_datetime(cls, v: Union[str, DateTimeType]) -> DateTimeType:
195
+ """Parse datetime."""
196
+ if type(v) == str:
197
+ v = str_to_interval(v)
204
198
  return v
205
199
 
206
200
  @property
@@ -2,6 +2,8 @@
2
2
  import sys
3
3
  from typing import Any, Dict, List, Literal, Optional, Union
4
4
 
5
+ from stac_pydantic.shared import BBox
6
+
5
7
  # Avoids a Pydantic error:
6
8
  # TypeError: You should use `typing_extensions.TypedDict` instead of
7
9
  # `typing.TypedDict` with Python < 3.9.2. Without it, there is no way to
@@ -64,7 +66,7 @@ class Item(TypedDict, total=False):
64
66
  stac_extensions: Optional[List[str]]
65
67
  id: str
66
68
  geometry: Dict[str, Any]
67
- bbox: List[NumType]
69
+ bbox: BBox
68
70
  properties: Dict[str, Any]
69
71
  links: List[Dict[str, Any]]
70
72
  assets: Dict[str, Any]
@@ -1,2 +1,2 @@
1
1
  """Library version."""
2
- __version__ = "2.4.9"
2
+ __version__ = "2.5.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.types
3
- Version: 2.4.9
3
+ Version: 2.5.1
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