Skip to content

Commit

Permalink
switch to attrs (#73)
Browse files Browse the repository at this point in the history
* switch to attrs

* use attrs in api, models
  • Loading branch information
geospatial-jeff authored Jan 21, 2021
1 parent e93c9b1 commit 5a8d953
Show file tree
Hide file tree
Showing 18 changed files with 502 additions and 435 deletions.
717 changes: 393 additions & 324 deletions Pipfile.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


install_requires = [
"attrs",
"uvicorn[standard]",
"gunicorn",
"fastapi>=0.60.0",
Expand Down
24 changes: 11 additions & 13 deletions stac_api/api/app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""fastapi app creation"""
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Type

import attr
from fastapi import APIRouter, FastAPI
from fastapi.openapi.utils import get_openapi
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from stac_pydantic import ItemCollection
from stac_pydantic.api import ConformanceClasses, LandingPage
from starlette.requests import Request

from stac_api.api.extensions import FieldsExtension
Expand All @@ -24,23 +26,19 @@
from stac_api.errors import DEFAULT_STATUS_CODES, add_exception_handlers
from stac_api.models import schemas
from stac_api.utils.dependencies import READER, WRITER
from stac_pydantic import ItemCollection
from stac_pydantic.api import ConformanceClasses, LandingPage


@dataclass
@attr.s
class StacApi:
"""StacApi"""

settings: ApiSettings
client: BaseCoreClient
extensions: Optional[List[ApiExtension]] = field( # type:ignore
default_factory=list
)
exceptions: Dict[Type[Exception], int] = field(
default_factory=lambda: DEFAULT_STATUS_CODES
settings: ApiSettings = attr.ib()
client: BaseCoreClient = attr.ib()
extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list))
exceptions: Dict[Type[Exception], int] = attr.ib(
default=attr.Factory(lambda: DEFAULT_STATUS_CODES)
)
app: FastAPI = FastAPI()
app: FastAPI = attr.ib(default=attr.Factory(FastAPI))

def get_extension(self, extension: Type[ApiExtension]) -> Optional[ApiExtension]:
"""check if an api extension is enabled"""
Expand Down Expand Up @@ -205,7 +203,7 @@ async def create_db_connection(request: Request, call_next):
writer.close()
return resp

def __post_init__(self):
def __attrs_post_init__(self):
"""post-init hook"""
# inject settings
self.app.debug = self.settings.debug
Expand Down
5 changes: 2 additions & 3 deletions stac_api/api/extensions/context.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""context extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class ContextExtension(ApiExtension):
"""
stac-api context extension
Expand Down
4 changes: 2 additions & 2 deletions stac_api/api/extensions/extension.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""base api extension"""
import abc
from dataclasses import dataclass

import attr
from fastapi import FastAPI


@dataclass # type:ignore
@attr.s
class ApiExtension(abc.ABC):
"""orchestration for API extensions"""

Expand Down
26 changes: 14 additions & 12 deletions stac_api/api/extensions/fields.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
"""fields extension"""
from dataclasses import dataclass, field
from typing import Set

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class FieldsExtension(ApiExtension):
"""
stac-api fields extension
(https://github.com/radiantearth/stac-api-spec/tree/master/extensions/fields)
"""

default_includes: Set[str] = field(
default_factory=lambda: {
"id",
"type",
"geometry",
"bbox",
"links",
"assets",
"properties.datetime",
}
default_includes: Set[str] = attr.ib(
default=attr.Factory(
lambda: {
"id",
"type",
"geometry",
"bbox",
"links",
"assets",
"properties.datetime",
}
)
)

def register(self, app: FastAPI) -> None:
Expand Down
5 changes: 2 additions & 3 deletions stac_api/api/extensions/query.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""query extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class QueryExtension(ApiExtension):
"""
stac-api query extension
Expand Down
5 changes: 2 additions & 3 deletions stac_api/api/extensions/sort.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""sort extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class SortExtension(ApiExtension):
"""
stac-api query extension
Expand Down
12 changes: 6 additions & 6 deletions stac_api/api/extensions/tiles.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
"""tiles extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import HTMLResponse

from stac_api.api.extensions.extension import ApiExtension
from stac_api.api.models import ItemUri
from stac_api.api.routes import create_endpoint_with_depends
from stac_api.clients.tiles.ogc import TilesClient
from stac_api.models.ogc import TileSetResource
from starlette.requests import Request
from starlette.responses import HTMLResponse


@dataclass
@attr.s
class TilesExtension(ApiExtension):
"""titiler extension"""

client: TilesClient = TilesClient()
client: TilesClient = attr.ib(default=attr.Factory(TilesClient))

def register(self, app: FastAPI) -> None:
"""register extension with the application"""
Expand Down
7 changes: 3 additions & 4 deletions stac_api/api/extensions/transaction.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""stac-api transaction extension"""
from dataclasses import dataclass

import attr
from fastapi import APIRouter, FastAPI

from stac_api.api.extensions.extension import ApiExtension
Expand All @@ -10,14 +9,14 @@
from stac_api.models import schemas


@dataclass
@attr.s
class TransactionExtension(ApiExtension):
"""
stac-api transaction extension
(https://github.com/radiantearth/stac-api-spec/blob/master/extensions/transaction/README.md)
"""

client: BaseTransactionsClient
client: BaseTransactionsClient = attr.ib()

def register(self, app: FastAPI) -> None:
"""register extension with the application"""
Expand Down
41 changes: 20 additions & 21 deletions stac_api/api/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""api request/response models"""

import abc
from dataclasses import dataclass
from typing import Dict, Optional, Type, Union

import attr
from fastapi import Body, Path

from pydantic import BaseModel, create_model
from pydantic.fields import UndefinedType

Expand Down Expand Up @@ -43,7 +42,7 @@ def _create_request_model(model: Type[BaseModel]) -> Type[BaseModel]:
return create_model(model.__name__, **fields, __base__=model)


@dataclass # type:ignore
@attr.s # type:ignore
class APIRequest(abc.ABC):
"""Generic API Request base class"""

Expand All @@ -53,29 +52,29 @@ def kwargs(self) -> Dict:
...


@dataclass # type:ignore
@attr.s # type:ignore
class CollectionUri(APIRequest):
"""Delete collection"""

collectionId: str = Path(..., description="Collection ID")
collectionId: str = attr.ib(default=Path(..., description="Collection ID"))

def kwargs(self) -> Dict:
"""kwargs"""
return {"id": self.collectionId}


@dataclass
@attr.s
class ItemUri(CollectionUri):
"""Delete item"""

itemId: str = Path(..., description="Item ID")
itemId: str = attr.ib(default=Path(..., description="Item ID"))

def kwargs(self) -> Dict:
"""kwargs"""
return {"id": self.itemId}


@dataclass
@attr.s
class EmptyRequest(APIRequest):
"""Empty request"""

Expand All @@ -84,31 +83,31 @@ def kwargs(self) -> Dict:
return {}


@dataclass
@attr.s
class ItemCollectionUri(CollectionUri):
"""Get item collection"""

limit: int = 10
token: str = None
limit: int = attr.ib(default=10)
token: str = attr.ib(default=None)

def kwargs(self) -> Dict:
"""kwargs"""
return {"id": self.collectionId, "limit": self.limit, "token": self.token}


@dataclass
@attr.s
class SearchGetRequest(APIRequest):
"""GET search request"""

collections: Optional[str] = None
ids: Optional[str] = None
bbox: Optional[str] = None
datetime: Optional[Union[str]] = None
limit: Optional[int] = 10
query: Optional[str] = None
token: Optional[str] = None
fields: Optional[str] = None
sortby: Optional[str] = None
collections: Optional[str] = attr.ib(default=None)
ids: Optional[str] = attr.ib(default=None)
bbox: Optional[str] = attr.ib(default=None)
datetime: Optional[Union[str]] = attr.ib(default=None)
limit: Optional[int] = attr.ib(default=10)
query: Optional[str] = attr.ib(default=None)
token: Optional[str] = attr.ib(default=None)
fields: Optional[str] = attr.ib(default=None)
sortby: Optional[str] = attr.ib(default=None)

def kwargs(self) -> Dict:
"""kwargs"""
Expand Down
10 changes: 5 additions & 5 deletions stac_api/clients/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Base clients."""
import abc
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Dict, List, Optional, Type, Union

import attr
from stac_pydantic import ItemCollection
from stac_pydantic.api import ConformanceClasses, LandingPage

Expand All @@ -13,7 +13,7 @@
NumType = Union[float, int]


@dataclass # type:ignore
@attr.s # type:ignore
class BaseTransactionsClient(abc.ABC):
"""Base transactions client"""

Expand Down Expand Up @@ -52,7 +52,7 @@ def delete_collection(self, id: str, **kwargs) -> schemas.Collection:
...


@dataclass # type: ignore
@attr.s # type: ignore
class BulkTransactionsClient(abc.ABC):
"""bulk transactions client"""

Expand All @@ -68,11 +68,11 @@ def bulk_item_insert(self, items: List[Dict], chunks: Optional[int] = None) -> N
raise NotImplementedError


@dataclass # type:ignore
@attr.s # type:ignore
class BaseCoreClient(abc.ABC):
"""Base client for core endpoints defined by stac"""

extensions: List[ApiExtension] = field(default_factory=list)
extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list))

def extension_is_enabled(self, extension: Type[ApiExtension]) -> bool:
"""check if an api extension is enabled"""
Expand Down
6 changes: 3 additions & 3 deletions stac_api/clients/postgres/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Postgresql base client"""
import abc
import logging
from dataclasses import dataclass
from typing import Optional, Type

import attr
import sqlalchemy as sa
from sqlalchemy.orm import Query

Expand All @@ -15,11 +15,11 @@
logger = logging.getLogger(__name__)


@dataclass
@attr.s
class PostgresClient(abc.ABC):
"""Database CRUD operations on the defined table"""

table: Optional[Type[database.BaseModel]] = None
table: Type[database.BaseModel] = attr.ib(default=database.BaseModel)

@property
def reader_session(self):
Expand Down
Loading

0 comments on commit 5a8d953

Please sign in to comment.