diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index fd617c26..22d38d0f 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -37,6 +37,7 @@ jobs: if: ${{ matrix.python-version == env.LATEST_PY_VERSION }} run: | uv run pre-commit run --all-files + uv run --with mypy --with types-attrs mypy -p stac_fastapi - name: install lib postgres uses: nyurik/action-setup-postgis@v2 diff --git a/CHANGES.md b/CHANGES.md index 915b624a..78442c4f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,8 @@ ### Fixed - Update pydantic and pydantic-settings versions requirements +- Improve type hints + ## [6.1.4] - 2025-12-08 diff --git a/stac_fastapi/pgstac/app.py b/stac_fastapi/pgstac/app.py index 9f3c3058..bc781d3a 100644 --- a/stac_fastapi/pgstac/app.py +++ b/stac_fastapi/pgstac/app.py @@ -7,6 +7,7 @@ import os from contextlib import asynccontextmanager +from typing import Dict, List, Set, Type, cast from brotli_asgi import BrotliMiddleware from fastapi import APIRouter, FastAPI @@ -36,6 +37,8 @@ from stac_fastapi.extensions.core.query import QueryConformanceClasses from stac_fastapi.extensions.core.sort import SortConformanceClasses from stac_fastapi.extensions.third_party import BulkTransactionExtension +from stac_fastapi.types.extension import ApiExtension +from stac_fastapi.types.search import APIRequest from starlette.middleware import Middleware from stac_fastapi.pgstac.config import Settings @@ -49,7 +52,7 @@ settings = Settings() # search extensions -search_extensions_map = { +search_extensions_map: Dict[str, ApiExtension] = { "query": QueryExtension(), "sort": SortExtension(), "fields": FieldsExtension(), @@ -58,7 +61,7 @@ } # collection_search extensions -cs_extensions_map = { +cs_extensions_map: Dict[str, ApiExtension] = { "query": QueryExtension(conformance_classes=[QueryConformanceClasses.COLLECTIONS]), "sort": SortExtension(conformance_classes=[SortConformanceClasses.COLLECTIONS]), "fields": FieldsExtension(conformance_classes=[FieldsConformanceClasses.COLLECTIONS]), @@ -70,7 +73,7 @@ } # item_collection extensions -itm_col_extensions_map = { +itm_col_extensions_map: Dict[str, ApiExtension] = { "query": QueryExtension( conformance_classes=[QueryConformanceClasses.ITEMS], ), @@ -82,7 +85,7 @@ "pagination": TokenPaginationExtension(), } -enabled_extensions = { +enabled_extensions: Set[str] = { *search_extensions_map.keys(), *cs_extensions_map.keys(), *itm_col_extensions_map.keys(), @@ -92,7 +95,7 @@ if ext := os.environ.get("ENABLED_EXTENSIONS"): enabled_extensions = set(ext.split(",")) -application_extensions = [] +application_extensions: List[ApiExtension] = [] with_transactions = os.environ.get("ENABLE_TRANSACTIONS_EXTENSIONS", "").lower() in [ "yes", @@ -123,23 +126,27 @@ application_extensions.extend(search_extensions) # /collections/{collectionId}/items model -items_get_request_model = ItemCollectionUri +items_get_request_model: Type[APIRequest] = ItemCollectionUri itm_col_extensions = [ extension for key, extension in itm_col_extensions_map.items() if key in enabled_extensions ] if itm_col_extensions: - items_get_request_model = create_request_model( - model_name="ItemCollectionUri", - base_model=ItemCollectionUri, - extensions=itm_col_extensions, - request_type="GET", + items_get_request_model = cast( + Type[APIRequest], + create_request_model( + model_name="ItemCollectionUri", + base_model=ItemCollectionUri, + extensions=itm_col_extensions, + request_type="GET", + ), ) + application_extensions.extend(itm_col_extensions) # /collections model -collections_get_request_model = EmptyRequest +collections_get_request_model: Type[APIRequest] = EmptyRequest if "collection_search" in enabled_extensions: cs_extensions = [ extension @@ -173,7 +180,7 @@ async def lifespan(app: FastAPI): router=APIRouter(prefix=settings.prefix_path), settings=settings, extensions=application_extensions, - client=CoreCrudClient(pgstac_search_model=post_request_model), + client=CoreCrudClient(pgstac_search_model=post_request_model), # type: ignore [arg-type] response_class=JSONResponse, items_get_request_model=items_get_request_model, search_get_request_model=get_request_model, @@ -191,7 +198,7 @@ async def lifespan(app: FastAPI): allow_headers=settings.cors_headers, ), ], - health_check=health_check, + health_check=health_check, # type: ignore [arg-type] ) app = api.app diff --git a/stac_fastapi/pgstac/core.py b/stac_fastapi/pgstac/core.py index 626897a3..2f292fe3 100644 --- a/stac_fastapi/pgstac/core.py +++ b/stac_fastapi/pgstac/core.py @@ -41,7 +41,7 @@ class CoreCrudClient(AsyncBaseCoreClient): pgstac_search_model: Type[PgstacSearch] = attr.ib(default=PgstacSearch) - async def all_collections( # noqa: C901 + async def all_collections( # type: ignore [override] # noqa: C901 self, request: Request, # Extensions @@ -68,7 +68,7 @@ async def all_collections( # noqa: C901 next_link: Optional[Dict[str, Any]] = None prev_link: Optional[Dict[str, Any]] = None - collections_result: Collections + collections: Collections if self.extension_is_enabled("CollectionSearchExtension"): base_args = { @@ -101,9 +101,9 @@ async def all_collections( # noqa: C901 """, req=json.dumps(clean_args), ) - collections_result = await conn.fetchval(q, *p) + collections = await conn.fetchval(q, *p) - if links := collections_result.get("links"): + if links := collections.get("links"): for link in links: if link["rel"] == "next": next_link = link @@ -112,53 +112,45 @@ async def all_collections( # noqa: C901 else: async with request.app.state.get_connection(request, "r") as conn: - cols = await conn.fetchval( - """ + cols: List[Collection] = ( + await conn.fetchval( + """ SELECT * FROM all_collections(); """ - ) - collections_result = {"collections": cols, "links": []} - - linked_collections: List[Collection] = [] - collections = collections_result["collections"] - if collections is not None and len(collections) > 0: - for c in collections: - coll = Collection(**c) - coll["links"] = await CollectionLinks( - collection_id=coll["id"], request=request - ).get_links(extra_links=coll.get("links")) - - if self.extension_is_enabled( - "FilterExtension" - ) or self.extension_is_enabled("ItemCollectionFilterExtension"): - coll["links"].append( - { - "rel": Relations.queryables.value, - "type": MimeTypes.jsonschema.value, - "title": "Queryables", - "href": urljoin( - base_url, f"collections/{coll['id']}/queryables" - ), - } ) + or [] + ) - linked_collections.append(coll) + collections = Collections(collections=cols, links=[]) + + for collection in collections["collections"]: + collection["links"] = await CollectionLinks( + collection_id=collection["id"], request=request + ).get_links(extra_links=collection.get("links")) + + if self.extension_is_enabled("FilterExtension") or self.extension_is_enabled( + "ItemCollectionFilterExtension" + ): + collection["links"].append( + { + "rel": Relations.queryables.value, + "type": MimeTypes.jsonschema.value, + "title": "Queryables", + "href": urljoin( + base_url, f"collections/{collection['id']}/queryables" + ), + } + ) - links = await CollectionSearchPagingLinks( - request=request, - next=next_link, - prev=prev_link, + collections["links"] = await CollectionSearchPagingLinks( + request=request, next=next_link, prev=prev_link ).get_links() - collections = Collections( - collections=linked_collections or [], - links=links, - numberMatched=collections_result.get( - "numberMatched", len(linked_collections) - ), - numberReturned=collections_result.get( - "numberReturned", len(linked_collections) - ), + # Make sure Collections Body has numberMatched and numberReturned + total_collections = len(collections["collections"]) + collections["numberMatched"] = collections.get("numberMatched", total_collections) + collections["numberReturned"] = collections.get( + "numberReturned", total_collections ) # If we have the `fields` extension enabled @@ -169,7 +161,7 @@ async def all_collections( # noqa: C901 return collections - async def get_collection( + async def get_collection( # type: ignore [override] self, collection_id: str, request: Request, @@ -185,8 +177,6 @@ async def get_collection( Returns: Collection. """ - collection: Optional[Dict[str, Any]] - async with request.app.state.get_connection(request, "r") as conn: q, p = render( """ @@ -194,7 +184,8 @@ async def get_collection( """, id=collection_id, ) - collection = await conn.fetchval(q, *p) + collection: Optional[Collection] = await conn.fetchval(q, *p) + if collection is None: raise NotFoundError(f"Collection {collection_id} does not exist.") @@ -215,7 +206,7 @@ async def get_collection( } ) - return Collection(**collection) + return collection async def _get_base_item( self, @@ -246,7 +237,7 @@ async def _get_base_item( return item - async def _search_base( # noqa: C901 + async def _search_base( # noqa: C901 # type: ignore [override] self, search_request: PgstacSearch, request: Request, @@ -261,8 +252,6 @@ async def _search_base( # noqa: C901 Returns: ItemCollection containing items which match the search criteria. """ - items: Dict[str, Any] - settings: Settings = request.app.state.settings search_request.conf = search_request.conf or {} @@ -280,7 +269,8 @@ async def _search_base( # noqa: C901 """, req=search_request_json, ) - items = await conn.fetchval(q, *p) + item_collection: ItemCollection = await conn.fetchval(q, *p) + except InvalidDatetimeFormatError as e: raise InvalidQueryParameter( f"Datetime parameter {search_request.datetime} is invalid." @@ -289,15 +279,16 @@ async def _search_base( # noqa: C901 # Starting in pgstac 0.9.0, the `next` and `prev` tokens are returned in spec-compliant links with method GET next_from_link: Optional[str] = None prev_from_link: Optional[str] = None - for link in items.get("links", []): + + for link in item_collection.get("links", []): if link.get("rel") == "next": - next_from_link = link.get("href").split("token=next:")[1] + next_from_link = link["href"].split("token=next:")[1] if link.get("rel") == "prev": - prev_from_link = link.get("href").split("token=prev:")[1] + prev_from_link = link["href"].split("token=prev:")[1] - next: Optional[str] = items.pop("next", next_from_link) - prev: Optional[str] = items.pop("prev", prev_from_link) - collection = ItemCollection(**items) + # NOTE: Old version of pgstac returned `next` and `prev` links directly in the response + next: Optional[str] = item_collection.pop("next", next_from_link) # type: ignore [typeddict-item] + prev: Optional[str] = item_collection.pop("prev", prev_from_link) # type: ignore [typeddict-item] fields = getattr(search_request, "fields", None) include: Set[str] = fields.include if fields and fields.include else set() @@ -323,8 +314,7 @@ async def _add_item_links( request=request, ).get_links(extra_links=feature.get("links")) - cleaned_features: List[Item] = [] - + items: List[Item] = [] if settings.use_api_hydrate: async def _get_base_item(collection_id: str) -> Dict[str, Any]: @@ -334,40 +324,40 @@ async def _get_base_item(collection_id: str) -> Dict[str, Any]: fetch_base_item=_get_base_item, request=request ) - for feature in collection.get("features") or []: - base_item = await base_item_cache.get(feature.get("collection")) + for item in item_collection.get("features", []): + base_item = await base_item_cache.get(item.get("collection")) # Exclude None values base_item = {k: v for k, v in base_item.items() if v is not None} - feature = hydrate( + item = hydrate( # type: ignore base_item, - feature, + dict(item), strip_unmatched_markers=settings.exclude_hydrate_markers, ) # Grab ids needed for links that may be removed by the fields extension. - collection_id = feature.get("collection") - item_id = feature.get("id") + collection_id = item.get("collection") + item_id = item.get("id") - feature = filter_fields(feature, include, exclude) - await _add_item_links(feature, collection_id, item_id) + item = filter_fields(item, include, exclude) + await _add_item_links(item, collection_id, item_id) + items.append(item) - cleaned_features.append(feature) else: - for feature in collection.get("features") or []: - await _add_item_links(feature) - cleaned_features.append(feature) + for item in item_collection.get("features", []): + await _add_item_links(item) + items.append(item) - collection["features"] = cleaned_features - collection["links"] = await PagingLinks( + item_collection["features"] = items + item_collection["links"] = await PagingLinks( request=request, next=next, prev=prev, ).get_links() - return collection + return item_collection - async def item_collection( + async def item_collection( # type: ignore [override] self, collection_id: str, request: Request, @@ -439,7 +429,7 @@ async def item_collection( return ItemCollection(**item_collection) - async def get_item( + async def get_item( # type: ignore [override] self, item_id: str, collection_id: str, @@ -469,9 +459,9 @@ async def get_item( f"Item {item_id} in Collection {collection_id} does not exist." ) - return Item(**item_collection["features"][0]) + return item_collection["features"][0] - async def post_search( + async def post_search( # type: ignore [override] self, search_request: PgstacSearch, request: Request, @@ -501,9 +491,9 @@ async def post_search( ) item_collection["links"] = links - return ItemCollection(**item_collection) + return item_collection - async def get_search( + async def get_search( # type: ignore [override] self, request: Request, collections: Optional[List[str]] = None, @@ -570,7 +560,7 @@ async def get_search( if fields.include or fields.exclude: return JSONResponse(item_collection) # type: ignore - return ItemCollection(**item_collection) + return item_collection def _clean_search_args( # noqa: C901 self, diff --git a/stac_fastapi/pgstac/db.py b/stac_fastapi/pgstac/db.py index 45a564ae..e0165832 100644 --- a/stac_fastapi/pgstac/db.py +++ b/stac_fastapi/pgstac/db.py @@ -68,7 +68,8 @@ async def connect_to_db( ) -> None: """Create connection pools & connection retriever on application.""" if not postgres_settings: - postgres_settings = PostgresSettings() + # NOTE: for some reason mypy fails to recognize that attributes can be set by Env + postgres_settings = PostgresSettings() # type: ignore app.state.readpool = await _create_pool(postgres_settings) diff --git a/stac_fastapi/pgstac/extensions/filter.py b/stac_fastapi/pgstac/extensions/filter.py index 6b0e1949..bf664647 100644 --- a/stac_fastapi/pgstac/extensions/filter.py +++ b/stac_fastapi/pgstac/extensions/filter.py @@ -11,7 +11,7 @@ class FiltersClient(AsyncBaseFiltersClient): """Defines a pattern for implementing the STAC filter extension.""" - async def get_queryables( + async def get_queryables( # type: ignore[override] self, request: Request, collection_id: Optional[str] = None, diff --git a/stac_fastapi/pgstac/models/links.py b/stac_fastapi/pgstac/models/links.py index 11119eb4..c96a7df6 100644 --- a/stac_fastapi/pgstac/models/links.py +++ b/stac_fastapi/pgstac/models/links.py @@ -42,6 +42,7 @@ class BaseLinks: """Create inferred links common to collections and items.""" request: Request = attr.ib() + _body: dict = attr.ib(init=False, factory=dict) @property def base_url(self): @@ -112,7 +113,8 @@ async def get_links( """ # TODO: Pass request.json() into function so this doesn't need to be coroutine if self.request.method == "POST": - self.request.postbody = await self.request.json() + self._body = await self.request.json() + # join passed in links with generated links # and update relative paths links = self.create_links() @@ -162,7 +164,7 @@ def link_next(self) -> Optional[Dict[str, Any]]: "type": MimeTypes.geojson.value, "method": method, "href": self.url, - "body": {**self.request.postbody, "token": f"next:{self.next}"}, + "body": {**self._body, "token": f"next:{self.next}"}, } return None @@ -186,7 +188,7 @@ def link_prev(self) -> Optional[Dict[str, Any]]: "type": MimeTypes.geojson.value, "method": method, "href": self.url, - "body": {**self.request.postbody, "token": f"prev:{self.prev}"}, + "body": {**self._body, "token": f"prev:{self.prev}"}, } return None diff --git a/stac_fastapi/pgstac/transactions.py b/stac_fastapi/pgstac/transactions.py index bb2588e9..bc264b10 100644 --- a/stac_fastapi/pgstac/transactions.py +++ b/stac_fastapi/pgstac/transactions.py @@ -2,7 +2,7 @@ import logging import re -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Union, cast import attr import jsonpatch @@ -64,10 +64,7 @@ def _validate_extensions( return try: - validate_extensions( - stac_object, - reraise_exception=True, - ) + validate_extensions(dict(stac_object), reraise_exception=True) except Exception as err: raise HTTPException( status_code=422, @@ -115,7 +112,7 @@ def _validate_item( class TransactionsClient(AsyncBaseTransactionsClient, ClientValidateMixIn): """Transactions extension specific CRUD operations.""" - async def create_item( + async def create_item( # type: ignore [override] self, collection_id: str, item: Union[Item, ItemCollection], @@ -123,113 +120,117 @@ async def create_item( **kwargs, ) -> Optional[Union[stac_types.Item, Response]]: """Create item.""" - item = item.model_dump(mode="json") + item_dict = cast( + Union[stac_types.Item, stac_types.ItemCollection], + item.model_dump(mode="json"), + ) - if item["type"] == "FeatureCollection": - valid_items = [] - for item in item["features"]: # noqa: B020 - self._validate_item(request, item, collection_id) - item["collection"] = collection_id - valid_items.append(item) + # Item Collection + if item_dict["type"] == "FeatureCollection": + valid_items: List[stac_types.Item] = [] + for feature in item_dict["features"]: # noqa: B020 + self._validate_item(request, feature, collection_id) + feature["collection"] = collection_id + valid_items.append(feature) async with request.app.state.get_connection(request, "w") as conn: await dbfunc(conn, "create_items", valid_items) return Response(status_code=201) - elif item["type"] == "Feature": - self._validate_item(request, item, collection_id) - item["collection"] = collection_id + # Single Item + elif item_dict["type"] == "Feature": + self._validate_item(request, item_dict, collection_id) + item_dict["collection"] = collection_id async with request.app.state.get_connection(request, "w") as conn: - await dbfunc(conn, "create_item", item) + await dbfunc(conn, "create_item", dict(item_dict)) - item["links"] = await ItemLinks( + item_dict["links"] = await ItemLinks( collection_id=collection_id, - item_id=item["id"], + item_id=item_dict["id"], request=request, - ).get_links(extra_links=item.get("links")) + ).get_links(extra_links=item_dict.get("links")) - return stac_types.Item(**item) + return item_dict - else: - raise HTTPException( - status_code=400, - detail=f"Item body type must be 'Feature' or 'FeatureCollection', not {item['type']}", - ) + raise HTTPException( + status_code=400, + detail=f"Item body type must be 'Feature' or 'FeatureCollection', not {item['type']}", + ) - async def update_item( + async def update_item( # type: ignore [override] self, request: Request, collection_id: str, item_id: str, item: Item, **kwargs, - ) -> Optional[Union[stac_types.Item, Response]]: + ) -> stac_types.Item: """Update item.""" - item = item.model_dump(mode="json") + item_dict = cast(stac_types.Item, item.model_dump(mode="json")) - self._validate_item(request, item, collection_id, item_id) - item["collection"] = collection_id + self._validate_item(request, item_dict, collection_id, item_id) + item_dict["collection"] = collection_id async with request.app.state.get_connection(request, "w") as conn: - await dbfunc(conn, "update_item", item) + await dbfunc(conn, "update_item", dict(item_dict)) - item["links"] = await ItemLinks( + item_dict["links"] = await ItemLinks( collection_id=collection_id, - item_id=item["id"], + item_id=item_dict["id"], request=request, - ).get_links(extra_links=item.get("links")) + ).get_links(extra_links=item_dict.get("links")) - return stac_types.Item(**item) + return item_dict - async def create_collection( + async def create_collection( # type: ignore [override] self, collection: Collection, request: Request, **kwargs, - ) -> Optional[Union[stac_types.Collection, Response]]: + ) -> stac_types.Collection: """Create collection.""" - collection = collection.model_dump(mode="json") + collection_dict = cast(stac_types.Collection, collection.model_dump(mode="json")) - self._validate_collection(request, collection) + self._validate_collection(request, collection_dict) async with request.app.state.get_connection(request, "w") as conn: - await dbfunc(conn, "create_collection", collection) + await dbfunc(conn, "create_collection", dict(collection_dict)) - collection["links"] = await CollectionLinks( - collection_id=collection["id"], request=request - ).get_links(extra_links=collection["links"]) + collection_dict["links"] = await CollectionLinks( + collection_id=collection_dict["id"], request=request + ).get_links(extra_links=collection_dict["links"]) - return stac_types.Collection(**collection) + return collection_dict - async def update_collection( + async def update_collection( # type: ignore [override] self, collection: Collection, request: Request, **kwargs, - ) -> Optional[Union[stac_types.Collection, Response]]: + ) -> stac_types.Collection: """Update collection.""" + collection_dict = cast(stac_types.Collection, collection.model_dump(mode="json")) - col = collection.model_dump(mode="json") - self._validate_collection(request, col) + self._validate_collection(request, collection_dict) async with request.app.state.get_connection(request, "w") as conn: - await dbfunc(conn, "update_collection", col) + await dbfunc(conn, "update_collection", dict(collection_dict)) - col["links"] = await CollectionLinks( - collection_id=col["id"], request=request - ).get_links(extra_links=col.get("links")) + collection_dict["links"] = await CollectionLinks( + collection_id=collection_dict["id"], request=request + ).get_links(extra_links=collection_dict.get("links")) - return stac_types.Collection(**col) + return collection_dict - async def delete_item( + async def delete_item( # type: ignore [override] self, item_id: str, collection_id: str, request: Request, **kwargs, - ) -> Optional[Union[stac_types.Item, Response]]: + ) -> Response: """Delete item.""" q, p = render( "SELECT * FROM delete_item(:item::text, :collection::text);", @@ -241,23 +242,26 @@ async def delete_item( return JSONResponse({"deleted item": item_id}) - async def delete_collection( - self, collection_id: str, request: Request, **kwargs - ) -> Optional[Union[stac_types.Collection, Response]]: + async def delete_collection( # type: ignore [override] + self, + collection_id: str, + request: Request, + **kwargs, + ) -> Response: """Delete collection.""" async with request.app.state.get_connection(request, "w") as conn: await dbfunc(conn, "delete_collection", collection_id) return JSONResponse({"deleted collection": collection_id}) - async def patch_item( + async def patch_item( # type: ignore [override] self, collection_id: str, item_id: str, patch: Union[PartialItem, List[PatchOperation]], request: Request, **kwargs, - ) -> Optional[Union[stac_types.Item, Response]]: + ) -> stac_types.Item: """Patch Item.""" # Get Existing Item to Patch @@ -269,7 +273,8 @@ async def patch_item( item_id=item_id, collection_id=collection_id, ) - existing = await conn.fetchval(q, *p) + existing: Optional[stac_types.Item] = await conn.fetchval(q, *p) + if existing is None: raise NotFoundError( f"Item {item_id} does not exist in collection {collection_id}." @@ -298,15 +303,15 @@ async def patch_item( request=request, ).get_links(extra_links=item.get("links")) - return stac_types.Item(**item) + return cast(stac_types.Item, item) - async def patch_collection( + async def patch_collection( # type: ignore [override] self, collection_id: str, patch: Union[PartialCollection, List[PatchOperation]], request: Request, **kwargs, - ) -> Optional[Union[stac_types.Collection, Response]]: + ) -> stac_types.Collection: """Patch Collection.""" # Get Existing Collection to Patch @@ -317,7 +322,8 @@ async def patch_collection( """, id=collection_id, ) - existing = await conn.fetchval(q, *p) + existing: Optional[stac_types.Collection] = await conn.fetchval(q, *p) + if existing is None: raise NotFoundError(f"Collection {collection_id} does not exist.") @@ -341,14 +347,14 @@ async def patch_collection( collection_id=col["id"], request=request ).get_links(extra_links=col.get("links")) - return stac_types.Collection(**col) + return cast(stac_types.Collection, col) @attr.s class BulkTransactionsClient(AsyncBaseBulkTransactionsClient, ClientValidateMixIn): """Postgres bulk transactions.""" - async def bulk_item_insert(self, items: Items, request: Request, **kwargs) -> str: + async def bulk_item_insert(self, items: Items, request: Request, **kwargs) -> str: # type: ignore [override] """Bulk item insertion using pgstac.""" collection_id = request.path_params["collection_id"] diff --git a/stac_fastapi/pgstac/utils.py b/stac_fastapi/pgstac/utils.py index e03e4a36..bbcb005d 100644 --- a/stac_fastapi/pgstac/utils.py +++ b/stac_fastapi/pgstac/utils.py @@ -1,12 +1,12 @@ """stac-fastapi utility methods.""" -from typing import Any, Dict, Optional, Set, Union +from typing import Any, Dict, Optional, Set, cast from stac_fastapi.types.stac import Item def filter_fields( # noqa: C901 - item: Union[Item, Dict[str, Any]], + item: Item, include: Optional[Set[str]] = None, exclude: Optional[Set[str]] = None, ) -> Item: @@ -58,6 +58,7 @@ def include_fields( # The key, or root key of a multi-part key, is not present in the item, # so it is ignored pass + return clean_item # For an item built up for included fields, remove excluded fields. This @@ -82,19 +83,16 @@ def exclude_fields(source: Dict[str, Any], fields: Optional[Set[str]]) -> None: # The key to remove does not exist on the source, so it is ignored pass - # Coalesce incoming type to a dict - item = dict(item) - - clean_item = include_fields(item, include) + clean_item = include_fields(dict(item), include) # If, after including all the specified fields, there are no included properties, # return just id and collection. if not clean_item: - return Item({"id": item["id"], "collection": item["collection"]}) + return Item({"id": item["id"], "collection": item["collection"]}) # type: ignore exclude_fields(clean_item, exclude) - return Item(**clean_item) + return cast(Item, clean_item) def dict_deep_update(merge_to: Dict[str, Any], merge_from: Dict[str, Any]) -> None: diff --git a/uv.lock b/uv.lock index 3567a0ee..c0c0382a 100644 --- a/uv.lock +++ b/uv.lock @@ -1142,35 +1142,35 @@ wheels = [ [[package]] name = "debugpy" -version = "1.8.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/ad/71e708ff4ca377c4230530d6a7aa7992592648c122a2cd2b321cf8b35a76/debugpy-1.8.17.tar.gz", hash = "sha256:fd723b47a8c08892b1a16b2c6239a8b96637c62a59b94bb5dab4bac592a58a8e", size = 1644129, upload-time = "2025-09-17T16:33:20.633Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/36/b57c6e818d909f6e59c0182252921cf435e0951126a97e11de37e72ab5e1/debugpy-1.8.17-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:c41d2ce8bbaddcc0009cc73f65318eedfa3dbc88a8298081deb05389f1ab5542", size = 2098021, upload-time = "2025-09-17T16:33:22.556Z" }, - { url = "https://files.pythonhosted.org/packages/be/01/0363c7efdd1e9febd090bb13cee4fb1057215b157b2979a4ca5ccb678217/debugpy-1.8.17-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:1440fd514e1b815edd5861ca394786f90eb24960eb26d6f7200994333b1d79e3", size = 3087399, upload-time = "2025-09-17T16:33:24.292Z" }, - { url = "https://files.pythonhosted.org/packages/79/bc/4a984729674aa9a84856650438b9665f9a1d5a748804ac6f37932ce0d4aa/debugpy-1.8.17-cp310-cp310-win32.whl", hash = "sha256:3a32c0af575749083d7492dc79f6ab69f21b2d2ad4cd977a958a07d5865316e4", size = 5230292, upload-time = "2025-09-17T16:33:26.137Z" }, - { url = "https://files.pythonhosted.org/packages/5d/19/2b9b3092d0cf81a5aa10c86271999453030af354d1a5a7d6e34c574515d7/debugpy-1.8.17-cp310-cp310-win_amd64.whl", hash = "sha256:a3aad0537cf4d9c1996434be68c6c9a6d233ac6f76c2a482c7803295b4e4f99a", size = 5261885, upload-time = "2025-09-17T16:33:27.592Z" }, - { url = "https://files.pythonhosted.org/packages/d8/53/3af72b5c159278c4a0cf4cffa518675a0e73bdb7d1cac0239b815502d2ce/debugpy-1.8.17-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:d3fce3f0e3de262a3b67e69916d001f3e767661c6e1ee42553009d445d1cd840", size = 2207154, upload-time = "2025-09-17T16:33:29.457Z" }, - { url = "https://files.pythonhosted.org/packages/8f/6d/204f407df45600e2245b4a39860ed4ba32552330a0b3f5f160ae4cc30072/debugpy-1.8.17-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:c6bdf134457ae0cac6fb68205776be635d31174eeac9541e1d0c062165c6461f", size = 3170322, upload-time = "2025-09-17T16:33:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/f2/13/1b8f87d39cf83c6b713de2620c31205299e6065622e7dd37aff4808dd410/debugpy-1.8.17-cp311-cp311-win32.whl", hash = "sha256:e79a195f9e059edfe5d8bf6f3749b2599452d3e9380484cd261f6b7cd2c7c4da", size = 5155078, upload-time = "2025-09-17T16:33:33.331Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c5/c012c60a2922cc91caa9675d0ddfbb14ba59e1e36228355f41cab6483469/debugpy-1.8.17-cp311-cp311-win_amd64.whl", hash = "sha256:b532282ad4eca958b1b2d7dbcb2b7218e02cb934165859b918e3b6ba7772d3f4", size = 5179011, upload-time = "2025-09-17T16:33:35.711Z" }, - { url = "https://files.pythonhosted.org/packages/08/2b/9d8e65beb2751876c82e1aceb32f328c43ec872711fa80257c7674f45650/debugpy-1.8.17-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:f14467edef672195c6f6b8e27ce5005313cb5d03c9239059bc7182b60c176e2d", size = 2549522, upload-time = "2025-09-17T16:33:38.466Z" }, - { url = "https://files.pythonhosted.org/packages/b4/78/eb0d77f02971c05fca0eb7465b18058ba84bd957062f5eec82f941ac792a/debugpy-1.8.17-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:24693179ef9dfa20dca8605905a42b392be56d410c333af82f1c5dff807a64cc", size = 4309417, upload-time = "2025-09-17T16:33:41.299Z" }, - { url = "https://files.pythonhosted.org/packages/37/42/c40f1d8cc1fed1e75ea54298a382395b8b937d923fcf41ab0797a554f555/debugpy-1.8.17-cp312-cp312-win32.whl", hash = "sha256:6a4e9dacf2cbb60d2514ff7b04b4534b0139facbf2abdffe0639ddb6088e59cf", size = 5277130, upload-time = "2025-09-17T16:33:43.554Z" }, - { url = "https://files.pythonhosted.org/packages/72/22/84263b205baad32b81b36eac076de0cdbe09fe2d0637f5b32243dc7c925b/debugpy-1.8.17-cp312-cp312-win_amd64.whl", hash = "sha256:e8f8f61c518952fb15f74a302e068b48d9c4691768ade433e4adeea961993464", size = 5319053, upload-time = "2025-09-17T16:33:53.033Z" }, - { url = "https://files.pythonhosted.org/packages/50/76/597e5cb97d026274ba297af8d89138dfd9e695767ba0e0895edb20963f40/debugpy-1.8.17-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:857c1dd5d70042502aef1c6d1c2801211f3ea7e56f75e9c335f434afb403e464", size = 2538386, upload-time = "2025-09-17T16:33:54.594Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/ce5c34fcdfec493701f9d1532dba95b21b2f6394147234dce21160bd923f/debugpy-1.8.17-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:3bea3b0b12f3946e098cce9b43c3c46e317b567f79570c3f43f0b96d00788088", size = 4292100, upload-time = "2025-09-17T16:33:56.353Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/7873cf2146577ef71d2a20bf553f12df865922a6f87b9e8ee1df04f01785/debugpy-1.8.17-cp313-cp313-win32.whl", hash = "sha256:e34ee844c2f17b18556b5bbe59e1e2ff4e86a00282d2a46edab73fd7f18f4a83", size = 5277002, upload-time = "2025-09-17T16:33:58.231Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/18c79a1cee5ff539a94ec4aa290c1c069a5580fd5cfd2fb2e282f8e905da/debugpy-1.8.17-cp313-cp313-win_amd64.whl", hash = "sha256:6c5cd6f009ad4fca8e33e5238210dc1e5f42db07d4b6ab21ac7ffa904a196420", size = 5319047, upload-time = "2025-09-17T16:34:00.586Z" }, - { url = "https://files.pythonhosted.org/packages/de/45/115d55b2a9da6de812696064ceb505c31e952c5d89c4ed1d9bb983deec34/debugpy-1.8.17-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:045290c010bcd2d82bc97aa2daf6837443cd52f6328592698809b4549babcee1", size = 2536899, upload-time = "2025-09-17T16:34:02.657Z" }, - { url = "https://files.pythonhosted.org/packages/5a/73/2aa00c7f1f06e997ef57dc9b23d61a92120bec1437a012afb6d176585197/debugpy-1.8.17-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:b69b6bd9dba6a03632534cdf67c760625760a215ae289f7489a452af1031fe1f", size = 4268254, upload-time = "2025-09-17T16:34:04.486Z" }, - { url = "https://files.pythonhosted.org/packages/86/b5/ed3e65c63c68a6634e3ba04bd10255c8e46ec16ebed7d1c79e4816d8a760/debugpy-1.8.17-cp314-cp314-win32.whl", hash = "sha256:5c59b74aa5630f3a5194467100c3b3d1c77898f9ab27e3f7dc5d40fc2f122670", size = 5277203, upload-time = "2025-09-17T16:34:06.65Z" }, - { url = "https://files.pythonhosted.org/packages/b0/26/394276b71c7538445f29e792f589ab7379ae70fd26ff5577dfde71158e96/debugpy-1.8.17-cp314-cp314-win_amd64.whl", hash = "sha256:893cba7bb0f55161de4365584b025f7064e1f88913551bcd23be3260b231429c", size = 5318493, upload-time = "2025-09-17T16:34:08.483Z" }, - { url = "https://files.pythonhosted.org/packages/16/ee/0e9a08878f1b525f85c4e47723ea1f17b1bad69672c84fa910210604e3f8/debugpy-1.8.17-cp39-cp39-macosx_15_0_x86_64.whl", hash = "sha256:f2ac8055a0c4a09b30b931100996ba49ef334c6947e7ae365cdd870416d7513e", size = 2099309, upload-time = "2025-09-17T16:34:17.935Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b5/0327b27efd8826ca92a256a3a250e80ccad6a834b4d12bd9cbd491f2da03/debugpy-1.8.17-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:eaa85bce251feca8e4c87ce3b954aba84b8c645b90f0e6a515c00394a9f5c0e7", size = 3080100, upload-time = "2025-09-17T16:34:19.885Z" }, - { url = "https://files.pythonhosted.org/packages/0f/f0/2e210fa8884d2ab452fa31ffd1402e13010eaacfa67063d0565d97ac9e0e/debugpy-1.8.17-cp39-cp39-win32.whl", hash = "sha256:b13eea5587e44f27f6c48588b5ad56dcb74a4f3a5f89250443c94587f3eb2ea1", size = 5231016, upload-time = "2025-09-17T16:34:21.887Z" }, - { url = "https://files.pythonhosted.org/packages/d6/9b/6a45fb1553d09b618c9441bcbbf72b651246b83b5618b2f95c0e4cf1b8bd/debugpy-1.8.17-cp39-cp39-win_amd64.whl", hash = "sha256:bb1bbf92317e1f35afcf3ef0450219efb3afe00be79d8664b250ac0933b9015f", size = 5262778, upload-time = "2025-09-17T16:34:24.026Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl", hash = "sha256:60c7dca6571efe660ccb7a9508d73ca14b8796c4ed484c2002abba714226cfef", size = 5283210, upload-time = "2025-09-17T16:34:25.835Z" }, +version = "1.8.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/1a/7cb5531840d7ba5d9329644109e62adee41f2f0083d9f8a4039f01de58cf/debugpy-1.8.18.tar.gz", hash = "sha256:02551b1b84a91faadd2db9bc4948873f2398190c95b3cc6f97dc706f43e8c433", size = 1644467, upload-time = "2025-12-10T19:48:07.236Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/38/0136815d2425fda176b30f0ec0b0f299d7316db46b36420e48399eca42e2/debugpy-1.8.18-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:d44e9c531f2519ec4b856ddde8f536615918f5b7886c658a81bf200c90315f77", size = 2098460, upload-time = "2025-12-10T19:48:08.924Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d9/2f00867bea3e50fee298b37602ac7aec9915bdb7227756d4cef889671c4a/debugpy-1.8.18-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:a69ef7d6050e5d26cf8e0081c6b591a41383dc18db734c4acafdd49568bb7a6f", size = 3087841, upload-time = "2025-12-10T19:48:10.326Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c1/54e50f376d394e0d3d355149d3d85b575e861d57ec0d0ff409c4bd51f531/debugpy-1.8.18-cp310-cp310-win32.whl", hash = "sha256:971965e264faed48ae961ff1e1ad2ce32d8e0cc550a4baa7643a25f1782b7125", size = 5233663, upload-time = "2025-12-10T19:48:12.668Z" }, + { url = "https://files.pythonhosted.org/packages/14/84/1142d16ee87f9bf4db5857b0b38468af602815eb73a9927436c79619beed/debugpy-1.8.18-cp310-cp310-win_amd64.whl", hash = "sha256:0701d83c4c1a74ed2c9abdabce102b1daf24cf81e1802421980871c9ee41f371", size = 5265361, upload-time = "2025-12-10T19:48:14.071Z" }, + { url = "https://files.pythonhosted.org/packages/ac/72/93167809b44a8e6971a1ff0b3e956cca4832fd7e8e47ce7b2b16be95795a/debugpy-1.8.18-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:3dae1d65e581406a4d7c1bb44391f47e621b8c87c5639b6607e6007a5d823205", size = 2207588, upload-time = "2025-12-10T19:48:15.44Z" }, + { url = "https://files.pythonhosted.org/packages/05/8b/0f5a54b239dac880ccc16e0b29fdecfb444635f2495cc3705548e24938ab/debugpy-1.8.18-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:8804d1288e6006629a87d53eb44b7b66e695d428ac529ffd75bfc7d730a9c821", size = 3170762, upload-time = "2025-12-10T19:48:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e4/7631d0ecd102085aa1cf5eb38f50e00036dec2c4571f236d2189ed842ee3/debugpy-1.8.18-cp311-cp311-win32.whl", hash = "sha256:ded8a5a413bd0a249b3c0be9f43128f437755180ac431222a6354c7d76a76a54", size = 5158530, upload-time = "2025-12-10T19:48:18.701Z" }, + { url = "https://files.pythonhosted.org/packages/c0/51/97674a4af4dc960a4eb0882b6c41c111e6a0a79c6b275df202f392e751cb/debugpy-1.8.18-cp311-cp311-win_amd64.whl", hash = "sha256:df6c1243dedcb6bf9a5dc1c5668009e2b5508b8525f27d9821be91da57827743", size = 5182452, upload-time = "2025-12-10T19:48:20.328Z" }, + { url = "https://files.pythonhosted.org/packages/83/01/439626e3572a33ac543f25bc1dac1e80bc01c7ce83f3c24dc4441302ca13/debugpy-1.8.18-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:530c38114725505a7e4ea95328dbc24aabb9be708c6570623c8163412e6d1d6b", size = 2549961, upload-time = "2025-12-10T19:48:21.73Z" }, + { url = "https://files.pythonhosted.org/packages/cd/73/1eeaa15c20a2b627be57a65bc1ebf2edd8d896950eac323588b127d776f2/debugpy-1.8.18-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:a114865099283cbed4c9330cb0c9cb7a04cfa92e803577843657302d526141ec", size = 4309855, upload-time = "2025-12-10T19:48:23.41Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6f/2da8ded21ae55df7067e57bd7f67ffed7e08b634f29bdba30c03d3f19918/debugpy-1.8.18-cp312-cp312-win32.whl", hash = "sha256:4d26736dfabf404e9f3032015ec7b0189e7396d0664e29e5bdbe7ac453043c95", size = 5280577, upload-time = "2025-12-10T19:48:25.386Z" }, + { url = "https://files.pythonhosted.org/packages/f5/8e/ebe887218c5b84f9421de7eb7bb7cdf196e84535c3f504a562219297d755/debugpy-1.8.18-cp312-cp312-win_amd64.whl", hash = "sha256:7e68ba950acbcf95ee862210133681f408cbb78d1c9badbb515230ec55ed6487", size = 5322458, upload-time = "2025-12-10T19:48:28.049Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3f/45af037e91e308274a092eb6a86282865fb1f11148cdb7616e811aae33d7/debugpy-1.8.18-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:75d14dd04b617ee38e46786394ec0dd5e1ac5e3d10ffb034fd6c7b72111174c2", size = 2538826, upload-time = "2025-12-10T19:48:29.434Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f4/2de6bf624de05134d1bbe0a8750d484363cd212c3ade3d04f5c77d47d0ce/debugpy-1.8.18-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:1b224887af5121fa702f9f542968170d104e3f9cac827d85fdefe89702dc235c", size = 4292542, upload-time = "2025-12-10T19:48:30.836Z" }, + { url = "https://files.pythonhosted.org/packages/93/54/89de7ef84d5ac39fc64a773feaedd902536cc5295814cd22d19c6d9dea35/debugpy-1.8.18-cp313-cp313-win32.whl", hash = "sha256:636a5445a3336e4aba323a3545ca2bb373b04b0bc14084a4eb20c989db44429f", size = 5280460, upload-time = "2025-12-10T19:48:32.696Z" }, + { url = "https://files.pythonhosted.org/packages/4f/59/651329e618406229edbef6508a5aa05e43cd027f042740c5b27e46854b23/debugpy-1.8.18-cp313-cp313-win_amd64.whl", hash = "sha256:6da217ac8c1152d698b9809484d50c75bef9cc02fd6886a893a6df81ec952ff8", size = 5322399, upload-time = "2025-12-10T19:48:35.057Z" }, + { url = "https://files.pythonhosted.org/packages/36/59/5e8bf46a66ca9dfcd0ce4f35c07085aeb60d99bf5c52135973a4e197ed41/debugpy-1.8.18-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:be7f622d250fe3429571e84572eb771023f1da22c754f28d2c60a10d74a4cc1b", size = 2537336, upload-time = "2025-12-10T19:48:36.463Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5a/3b37cc266a69da83a4febaa4267bb2062d4bec5287036e2f23d9a30a788c/debugpy-1.8.18-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:df8bf7cd78019d5d155213bf5a1818b36403d0c3758d669e76827d4db026b840", size = 4268696, upload-time = "2025-12-10T19:48:37.855Z" }, + { url = "https://files.pythonhosted.org/packages/de/4b/1e13586444440e5754b70055449b70afa187aaa167fa4c20c0c05d9c3b80/debugpy-1.8.18-cp314-cp314-win32.whl", hash = "sha256:32dd56d50fe15c47d0f930a7f0b9d3e5eb8ed04770bc6c313fba6d226f87e1e8", size = 5280624, upload-time = "2025-12-10T19:48:39.28Z" }, + { url = "https://files.pythonhosted.org/packages/7a/21/f8c12baa16212859269dc4c3e4b413778ec1154d332896d3c4cca96ac660/debugpy-1.8.18-cp314-cp314-win_amd64.whl", hash = "sha256:714b61d753cfe3ed5e7bf0aad131506d750e271726ac86e3e265fd7eeebbe765", size = 5321982, upload-time = "2025-12-10T19:48:41.086Z" }, + { url = "https://files.pythonhosted.org/packages/bb/48/3cf2a034108c30ae523bf764370155ec4eee8979e5c05ad6c412a346876f/debugpy-1.8.18-cp39-cp39-macosx_15_0_x86_64.whl", hash = "sha256:63424eb602ccb2c158fbd40437404d29ce0da5f9552e8bab53fb265e19e686ee", size = 2099749, upload-time = "2025-12-10T19:48:49.788Z" }, + { url = "https://files.pythonhosted.org/packages/90/e3/7ae3155d319417a04ccc2dcba8d8a3da4166e24a2decf4b7b3c055dd6528/debugpy-1.8.18-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:46e4aa316f9c16fa7145f192bf0fd1c5c43effca13b8767270a99e7e7ac464f5", size = 3080539, upload-time = "2025-12-10T19:48:51.261Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/6b6485f23047ac5902c206abb22eda0ecab1783ad7b3be6fd589cf9a5719/debugpy-1.8.18-cp39-cp39-win32.whl", hash = "sha256:2721237f9456394943f75c4b6f7cf2aed6ab9c59b7beca4bf553621d37000115", size = 5234457, upload-time = "2025-12-10T19:48:52.779Z" }, + { url = "https://files.pythonhosted.org/packages/15/36/7d70aab85671e0af154faf1d70b39bcee42d7ed9cbada5d42bfd186a19fb/debugpy-1.8.18-cp39-cp39-win_amd64.whl", hash = "sha256:cab3abf0ee2328269c380f7a8a1c41ea1d80d6507404db9b005c8432bc6224a1", size = 5266103, upload-time = "2025-12-10T19:48:54.291Z" }, + { url = "https://files.pythonhosted.org/packages/dc/0d/bf7ac329c132436c57124202b5b5ccd6366e5d8e75eeb184cf078c826e8d/debugpy-1.8.18-py2.py3-none-any.whl", hash = "sha256:ab8cf0abe0fe2dfe1f7e65abc04b1db8740f9be80c1274acb625855c5c3ece6e", size = 5286576, upload-time = "2025-12-10T19:48:56.071Z" }, ] [[package]] @@ -1223,7 +1223,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.124.0" +version = "0.124.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1232,9 +1232,9 @@ dependencies = [ { name = "starlette", version = "0.50.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/9c/11969bd3e3bc4aa3a711f83dd3720239d3565a934929c74fc32f6c9f3638/fastapi-0.124.0.tar.gz", hash = "sha256:260cd178ad75e6d259991f2fd9b0fee924b224850079df576a3ba604ce58f4e6", size = 357623, upload-time = "2025-12-06T13:11:35.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/b7/4dbca3f9d847ba9876dcb7098c13a4c6c86ee8db148c923fab78e27748d3/fastapi-0.124.2.tar.gz", hash = "sha256:72e188f01f360e2f59da51c8822cbe4bca210c35daaae6321b1b724109101c00", size = 361867, upload-time = "2025-12-10T12:10:10.676Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/29/9e1e82e16e9a1763d3b55bfbe9b2fa39d7175a1fd97685c482fa402e111d/fastapi-0.124.0-py3-none-any.whl", hash = "sha256:91596bdc6dde303c318f06e8d2bc75eafb341fc793a0c9c92c0bc1db1ac52480", size = 112505, upload-time = "2025-12-06T13:11:34.392Z" }, + { url = "https://files.pythonhosted.org/packages/25/c5/8a5231197b81943b2df126cc8ea2083262e004bee3a39cf85a471392d145/fastapi-0.124.2-py3-none-any.whl", hash = "sha256:6314385777a507bb19b34bd064829fddaea0eea54436deb632b5de587554055c", size = 112711, upload-time = "2025-12-10T12:10:08.855Z" }, ] [[package]] @@ -1597,7 +1597,8 @@ dependencies = [ { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "ipython", version = "9.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jupyter-client" }, + { name = "jupyter-client", version = "8.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "jupyter-client", version = "8.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "jupyter-core", version = "5.8.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "jupyter-core", version = "5.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "matplotlib-inline" }, @@ -1797,20 +1798,42 @@ wheels = [ name = "jupyter-client" version = "8.6.3" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] dependencies = [ { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "jupyter-core", version = "5.8.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "jupyter-core", version = "5.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "python-dateutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, + { name = "python-dateutil", marker = "python_full_version < '3.10'" }, + { name = "pyzmq", marker = "python_full_version < '3.10'" }, + { name = "tornado", marker = "python_full_version < '3.10'" }, + { name = "traitlets", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" }, ] +[[package]] +name = "jupyter-client" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "jupyter-core", version = "5.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.10'" }, + { name = "pyzmq", marker = "python_full_version >= '3.10'" }, + { name = "tornado", marker = "python_full_version >= '3.10'" }, + { name = "traitlets", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/27/d10de45e8ad4ce872372c4a3a37b7b35b6b064f6f023a5c14ffcced4d59d/jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d", size = 344691, upload-time = "2025-12-09T18:37:01.953Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0", size = 106215, upload-time = "2025-12-09T18:37:00.024Z" }, +] + [[package]] name = "jupyter-core" version = "5.8.1" @@ -2329,7 +2352,8 @@ name = "nbclient" version = "0.10.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jupyter-client" }, + { name = "jupyter-client", version = "8.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "jupyter-client", version = "8.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "jupyter-core", version = "5.8.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "jupyter-core", version = "5.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "nbformat" }, @@ -3348,16 +3372,16 @@ wheels = [ [[package]] name = "pymdown-extensions" -version = "10.18" +version = "10.19" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "markdown", version = "3.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/95/e4fa281e3f13b3d9c4aaebb21ef44879840325fa418276dd921209a5e9f9/pymdown_extensions-10.18.tar.gz", hash = "sha256:20252abe6367354b24191431617a072ee6be9f68c5afcc74ea5573508a61f9e5", size = 847697, upload-time = "2025-12-07T17:22:12.857Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/4e/e73e88f4f2d0b26cbd2e100074107470984f0a6055869805fc181b847ac7/pymdown_extensions-10.19.tar.gz", hash = "sha256:01bb917ea231f9ce14456fa9092cdb95ac3e5bd32202a3ee61dbd5ad2dd9ef9b", size = 847701, upload-time = "2025-12-11T18:20:46.093Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/a4/aa2bada4a2fd648f40f19affa55d2c01dc7ff5ea9cffd3dfdeb6114951db/pymdown_extensions-10.18-py3-none-any.whl", hash = "sha256:090bca72be43f7d3186374e23c782899dbef9dc153ef24c59dcd3c346f9ffcae", size = 266703, upload-time = "2025-12-07T17:22:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/fa9edaceb3805e03ac9faf68ca1ddc660a75b49aee5accb493511005fef5/pymdown_extensions-10.19-py3-none-any.whl", hash = "sha256:dc5f249fc3a1b6d8a6de4634ba8336b88d0942cee75e92b18ac79eaf3503bf7c", size = 266670, upload-time = "2025-12-11T18:20:44.736Z" }, ] [[package]] @@ -4246,28 +4270,28 @@ wheels = [ [[package]] name = "stac-fastapi-api" -version = "6.1.3" +version = "6.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "brotli-asgi" }, { name = "stac-fastapi-types" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c8/b0/5e718656cf39fa60bc313fa85d5003874c1511109ebdfaa216acd0da7d79/stac_fastapi_api-6.1.3.tar.gz", hash = "sha256:9767899c98d49cc314613295241a4b8fce097b1bf198f7ca3376917d387571b0", size = 11458, upload-time = "2025-12-09T15:45:00.19Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/f3/c5efa4091490e7b9d3cc1ae0fad315468f3f8b244bfc08feace895c5bb88/stac_fastapi_api-6.1.4.tar.gz", hash = "sha256:fbd8e678214d4891e436c80ecc1f3a8d0e1e86dcf948a677c91c816eb13f5c55", size = 11511, upload-time = "2025-12-12T09:47:31.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/01/0dfdd6e6bd77a2bdc75b951d4ce0a20003dc5b526793fa3b61a9bd5e1b94/stac_fastapi_api-6.1.3-py3-none-any.whl", hash = "sha256:55be37148924f90a695e18c22653f55076df1992fe243c53ec9c01d78c133934", size = 13711, upload-time = "2025-12-09T15:45:01.137Z" }, + { url = "https://files.pythonhosted.org/packages/06/fe/c5f1a120db2010464894f55e5c15b54738f90145b9875cd209241fbeb991/stac_fastapi_api-6.1.4-py3-none-any.whl", hash = "sha256:534a755f9c14776b996380601f2cc276886f2f99607d05fd6b3ed17bf94ab367", size = 13763, upload-time = "2025-12-12T09:47:32.709Z" }, ] [[package]] name = "stac-fastapi-extensions" -version = "6.1.3" +version = "6.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "stac-fastapi-api" }, { name = "stac-fastapi-types" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/7d/e5e4bfd6938dc2159a871af34c1ee6828a47129e1ef74e056c73a0abc5c4/stac_fastapi_extensions-6.1.3.tar.gz", hash = "sha256:75afda6cd2bf110808625dc9228cf04419d80fbe6c8a9f28ebe41283dc06e06d", size = 16648, upload-time = "2025-12-09T15:44:58.044Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/c7/3ba1376b009a75285e6e65c237c97a25016c23f8ad9a7ab95afc9f7dce8a/stac_fastapi_extensions-6.1.4.tar.gz", hash = "sha256:367bb18b654b3a5899f7a0eac97c62b8dc3484c5c67df7c30aec936a1724f91f", size = 16682, upload-time = "2025-12-12T09:47:29.268Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/81/246c441e7ce800bddf2c8903f3cf21653b561b1727e8195bf539e55903e5/stac_fastapi_extensions-6.1.3-py3-none-any.whl", hash = "sha256:f4de0bd8c899fd35454e62ab822b41b2593413c97feba4df85872c7f53c6a435", size = 33999, upload-time = "2025-12-09T15:44:58.985Z" }, + { url = "https://files.pythonhosted.org/packages/8d/04/0de9670c7d9648b37728eaa49cf3e041c487fbce3573483d57e04101a23a/stac_fastapi_extensions-6.1.4-py3-none-any.whl", hash = "sha256:a465b8f93b7a220bdba831377df277efafa8ffe303f14bf3f7d4322bf250b15c", size = 34019, upload-time = "2025-12-12T09:47:30.291Z" }, ] [[package]] @@ -4380,7 +4404,7 @@ docs = [ [[package]] name = "stac-fastapi-types" -version = "6.1.3" +version = "6.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -4390,9 +4414,9 @@ dependencies = [ { name = "pydantic-settings", version = "2.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "stac-pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/45/a732273887b48e14ad0cf14eafc54d0d459ff9fec6ecc448e7cc432e00ba/stac_fastapi_types-6.1.3.tar.gz", hash = "sha256:9213dee790628d954ca5defdd765eb84b625fbe725daba32cea2dc63c17db64d", size = 10623, upload-time = "2025-12-09T15:44:56.981Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/94/8fbb1e47f00e7f823874d9dbd9ef81c6fe2f3e41d44609ce5ca8a50c18b4/stac_fastapi_types-6.1.4.tar.gz", hash = "sha256:d3e728c519d836394df68600c893e763c7a0a9cb76b0729c61690268baf06902", size = 10633, upload-time = "2025-12-12T09:47:28.007Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/23/60c7383698a13b5a3a7f21703cb8b25a74870a02b6f955f70274ea8ca1f8/stac_fastapi_types-6.1.3-py3-none-any.whl", hash = "sha256:a5821757420768e5ae6f9e5c1f51a0708d315003ab7fd6a0a2f50e3980404237", size = 13562, upload-time = "2025-12-09T15:44:55.992Z" }, + { url = "https://files.pythonhosted.org/packages/4f/15/49af37f4bdafe545b8b61e71c7b81ab84e8e395040234f884db88352b8c4/stac_fastapi_types-6.1.4-py3-none-any.whl", hash = "sha256:7cbe567b008ec4d1c692427b1167648c2a8f38197e4962ae9f430c0a5156e55f", size = 13558, upload-time = "2025-12-12T09:47:27.15Z" }, ] [[package]] @@ -4569,21 +4593,21 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.2" +version = "6.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/2e/3d22d478f27cb4b41edd4db7f10cd7846d0a28ea443342de3dba97035166/tornado-6.5.3.tar.gz", hash = "sha256:16abdeb0211796ffc73765bc0a20119712d68afeeaf93d1a3f2edf6b3aee8d5a", size = 513348, upload-time = "2025-12-11T04:16:42.225Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" }, - { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" }, - { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" }, - { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" }, - { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" }, - { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" }, - { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e9/bf22f66e1d5d112c0617974b5ce86666683b32c09b355dfcd59f8d5c8ef6/tornado-6.5.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2dd7d7e8d3e4635447a8afd4987951e3d4e8d1fb9ad1908c54c4002aabab0520", size = 443860, upload-time = "2025-12-11T04:16:26.638Z" }, + { url = "https://files.pythonhosted.org/packages/ca/9c/594b631f0b8dc5977080c7093d1e96f1377c10552577d2c31bb0208c9362/tornado-6.5.3-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5977a396f83496657779f59a48c38096ef01edfe4f42f1c0634b791dde8165d0", size = 442118, upload-time = "2025-12-11T04:16:28.32Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/685b869f5b5b9d9547571be838c6106172082751696355b60fc32a4988ed/tornado-6.5.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f72ac800be2ac73ddc1504f7aa21069a4137e8d70c387172c063d363d04f2208", size = 445700, upload-time = "2025-12-11T04:16:29.64Z" }, + { url = "https://files.pythonhosted.org/packages/91/4c/f0d19edf24912b7f21ae5e941f7798d132ad4d9b71441c1e70917a297265/tornado-6.5.3-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43c4fc4f5419c6561cfb8b884a8f6db7b142787d47821e1a0e1296253458265", size = 445041, upload-time = "2025-12-11T04:16:30.799Z" }, + { url = "https://files.pythonhosted.org/packages/eb/2b/e02da94f4a4aef2bb3b923c838ef284a77548a5f06bac2a8682b36b4eead/tornado-6.5.3-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de8b3fed4b3afb65d542d7702ac8767b567e240f6a43020be8eaef59328f117b", size = 445270, upload-time = "2025-12-11T04:16:32.316Z" }, + { url = "https://files.pythonhosted.org/packages/58/e2/7a7535d23133443552719dba526dacbb7415f980157da9f14950ddb88ad6/tornado-6.5.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dbc4b4c32245b952566e17a20d5c1648fbed0e16aec3fc7e19f3974b36e0e47c", size = 445957, upload-time = "2025-12-11T04:16:33.913Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1f/9ff92eca81ff17a86286ec440dcd5eab0400326eb81761aa9a4eecb1ffb9/tornado-6.5.3-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:db238e8a174b4bfd0d0238b8cfcff1c14aebb4e2fcdafbf0ea5da3b81caceb4c", size = 445371, upload-time = "2025-12-11T04:16:35.093Z" }, + { url = "https://files.pythonhosted.org/packages/70/b1/1d03ae4526a393b0b839472a844397337f03c7f3a1e6b5c82241f0e18281/tornado-6.5.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:892595c100cd9b53a768cbfc109dfc55dec884afe2de5290611a566078d9692d", size = 445348, upload-time = "2025-12-11T04:16:36.679Z" }, + { url = "https://files.pythonhosted.org/packages/4b/7d/7c181feadc8941f418d0d26c3790ee34ffa4bd0a294bc5201d44ebd19c1e/tornado-6.5.3-cp39-abi3-win32.whl", hash = "sha256:88141456525fe291e47bbe1ba3ffb7982549329f09b4299a56813923af2bd197", size = 446433, upload-time = "2025-12-11T04:16:38.332Z" }, + { url = "https://files.pythonhosted.org/packages/34/98/4f7f938606e21d0baea8c6c39a7c8e95bdf8e50b0595b1bb6f0de2af7a6e/tornado-6.5.3-cp39-abi3-win_amd64.whl", hash = "sha256:ba4b513d221cc7f795a532c1e296f36bcf6a60e54b15efd3f092889458c69af1", size = 446842, upload-time = "2025-12-11T04:16:39.867Z" }, + { url = "https://files.pythonhosted.org/packages/7a/27/0e3fca4c4edf33fb6ee079e784c63961cd816971a45e5e4cacebe794158d/tornado-6.5.3-cp39-abi3-win_arm64.whl", hash = "sha256:278c54d262911365075dd45e0b6314308c74badd6ff9a54490e7daccdd5ed0ea", size = 445863, upload-time = "2025-12-11T04:16:41.099Z" }, ] [[package]] @@ -4627,11 +4651,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.6.1" +version = "2.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/1d/0f3a93cca1ac5e8287842ed4eebbd0f7a991315089b1a0b01c7788aa7b63/urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f", size = 432678, upload-time = "2025-12-08T15:25:26.773Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/56/190ceb8cb10511b730b564fb1e0293fa468363dbad26145c34928a60cb0c/urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b", size = 131138, upload-time = "2025-12-08T15:25:25.51Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, ] [[package]]