diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index e07d65260..0f159d6c2 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -40,6 +40,9 @@ jobs: with: python-version: "3.8" + - name: Lint code + uses: pre-commit/action@v2.0.0 + - name: Install pipenv run: | python -m pip install --upgrade pipenv wheel diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 49ac69da0..b043026d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,35 +1,34 @@ repos: - - - repo: https://github.com/pre-commit/mirrors-isort - rev: v4.3.21 - hooks: - - id: isort - language_version: python3.7 + - repo: https://github.com/PyCQA/isort + rev: 5.4.2 + hooks: + - id: isort + language_version: python3.8 - repo: https://github.com/psf/black rev: stable hooks: - id: black args: ['--safe'] - language_version: python3.7 + language_version: python3.8 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: - id: flake8 - language_version: python3.7 + language_version: python3.8 args: [ # E501 let black handle all line length decisions # W503 black conflicts with "line break before operator" rule # E203 black conflicts with "whitespace before ':'" rule - '--ignore=E501,W503,E203'] + '--ignore=E501,W503,E203,C901'] - repo: https://github.com/chewse/pre-commit-mirrors-pydocstyle # 2.1.1 rev: 22d3ccf6cf91ffce3b16caa946c155778f0cb20f hooks: - id: pydocstyle - language_version: python3.7 + language_version: python3.8 args: [ # Check for docstring presence only '--select=D1', @@ -40,5 +39,5 @@ repos: rev: v0.770 hooks: - id: mypy - language_version: python3.7 + language_version: python3.8 args: [--no-strict-optional, --ignore-missing-imports] \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py index 4c69dec3f..4c28c884e 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -72,7 +72,9 @@ def run_migrations_online(): configuration = config.get_section(config.config_ini_section) configuration["sqlalchemy.url"] = get_connection_url() connectable = engine_from_config( - configuration, prefix="sqlalchemy.", poolclass=pool.NullPool, + configuration, + prefix="sqlalchemy.", + poolclass=pool.NullPool, ) with connectable.connect() as connection: diff --git a/alembic/versions/131aab4d9e49_create_tables.py b/alembic/versions/131aab4d9e49_create_tables.py index f6dd772cf..2d197285c 100644 --- a/alembic/versions/131aab4d9e49_create_tables.py +++ b/alembic/versions/131aab4d9e49_create_tables.py @@ -6,10 +6,10 @@ """ # noqa import sqlalchemy as sa +from geoalchemy2.types import Geometry from sqlalchemy.dialects.postgresql import JSONB from alembic import op -from geoalchemy2.types import Geometry # revision identifiers, used by Alembic. revision = "131aab4d9e49" diff --git a/stac_api/__init__.py b/stac_api/__init__.py index e69de29bb..53b94283c 100644 --- a/stac_api/__init__.py +++ b/stac_api/__init__.py @@ -0,0 +1 @@ +"""stac_api""" diff --git a/stac_api/api/__init__.py b/stac_api/api/__init__.py index 5a91d06be..de7fac8e3 100644 --- a/stac_api/api/__init__.py +++ b/stac_api/api/__init__.py @@ -1,2 +1,4 @@ """api module""" from .routes import create_endpoint_from_model, create_endpoint_with_depends + +__all__ = ("create_endpoint_from_model", "create_endpoint_with_depends") diff --git a/stac_api/api/extensions/__init__.py b/stac_api/api/extensions/__init__.py index 019c5b376..00c31470b 100644 --- a/stac_api/api/extensions/__init__.py +++ b/stac_api/api/extensions/__init__.py @@ -1,9 +1,16 @@ """stac_api.api.extensions""" -from .context import ContextExtension # noqa +from .context import ContextExtension +from .fields import FieldsExtension +from .query import QueryExtension +from .sort import SortExtension +from .tiles import TilesExtension +from .transaction import TransactionExtension -# from .extension import ApiExtension # noqa -from .fields import FieldsExtension # noqa -from .query import QueryExtension # noqa -from .sort import SortExtension # noqa -from .tiles import TilesExtension # noqa -from .transaction import TransactionExtension # noqa +__all__ = ( + "ContextExtension", + "FieldsExtension", + "QueryExtension", + "SortExtension", + "TilesExtension", + "TransactionExtension", +) diff --git a/stac_api/api/routes.py b/stac_api/api/routes.py index 2fee656cf..9e1388fdc 100644 --- a/stac_api/api/routes.py +++ b/stac_api/api/routes.py @@ -2,9 +2,9 @@ from typing import Callable, Type from fastapi import Depends +from pydantic import BaseModel from starlette.requests import Request -from pydantic import BaseModel from stac_api.api.models import APIRequest @@ -18,7 +18,8 @@ def create_endpoint_from_model( """ def _endpoint( - request: Request, request_data: request_model, # type:ignore + request: Request, + request_data: request_model, # type:ignore ): """endpoint""" resp = func(request_data, request=request) @@ -28,14 +29,16 @@ def _endpoint( def create_endpoint_with_depends( - func: Callable, request_model: Type[APIRequest], + func: Callable, + request_model: Type[APIRequest], ) -> Callable: """ Create a fastapi endpoint where request model is a dataclass. This works best for validating query/patm params. """ def _endpoint( - request: Request, request_data: request_model = Depends(), # type:ignore + request: Request, + request_data: request_model = Depends(), # type:ignore ): """endpoint""" resp = func( diff --git a/stac_api/clients/postgres/core.py b/stac_api/clients/postgres/core.py index 3504e2d02..206d8161c 100644 --- a/stac_api/clients/postgres/core.py +++ b/stac_api/clients/postgres/core.py @@ -6,7 +6,9 @@ from urllib.parse import urlencode, urljoin import attr +import geoalchemy2 as ga import sqlalchemy as sa +from sqlakeyset import get_page from sqlalchemy import func from sqlalchemy.orm import Session as SqlSession from stac_pydantic import ItemCollection @@ -14,8 +16,6 @@ from stac_pydantic.api.extensions.paging import PaginationLink from stac_pydantic.shared import Link, MimeTypes, Relations -import geoalchemy2 as ga -from sqlakeyset import get_page from stac_api.api.extensions import ContextExtension, FieldsExtension from stac_api.clients.base import BaseCoreClient from stac_api.clients.postgres.session import Session diff --git a/stac_api/clients/postgres/session.py b/stac_api/clients/postgres/session.py index 3c3d9efd5..5f08cf4b4 100644 --- a/stac_api/clients/postgres/session.py +++ b/stac_api/clients/postgres/session.py @@ -5,12 +5,12 @@ from typing import Iterator import attr +import psycopg2 import sqlalchemy as sa import sqlalchemy.exc +from fastapi_utils.session import FastAPISessionMaker as _FastAPISessionMaker from sqlalchemy.orm import Session as SqlSession -import psycopg2 -from fastapi_utils.session import FastAPISessionMaker as _FastAPISessionMaker from stac_api import errors logger = logging.getLogger(__name__) @@ -35,16 +35,20 @@ def context_session(self) -> Iterator[SqlSession]: @attr.s class Session: + """Database session management""" + reader_conn_string: str = attr.ib() writer_conn_string: str = attr.ib() @classmethod def create_from_env(cls): + """create from environment""" return cls( reader_conn_string=os.environ["READER_CONN_STRING"], writer_conn_string=os.environ["WRITER_CONN_STRING"], ) def __attrs_post_init__(self): + """post init""" self.reader: FastAPISessionMaker = FastAPISessionMaker(self.reader_conn_string) self.writer: FastAPISessionMaker = FastAPISessionMaker(self.writer_conn_string) diff --git a/stac_api/models/__init__.py b/stac_api/models/__init__.py index e69de29bb..fd9898f9f 100644 --- a/stac_api/models/__init__.py +++ b/stac_api/models/__init__.py @@ -0,0 +1 @@ +"""stac_api.models""" diff --git a/stac_api/models/database.py b/stac_api/models/database.py index 67621116d..2255ac153 100644 --- a/stac_api/models/database.py +++ b/stac_api/models/database.py @@ -4,15 +4,15 @@ from datetime import datetime from typing import Optional +import geoalchemy2 as ga import sqlalchemy as sa from shapely.geometry import shape from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.ext.declarative import declarative_base +from stac_pydantic.shared import DATETIME_RFC339 -import geoalchemy2 as ga from stac_api import config from stac_api.models import schemas -from stac_pydantic.shared import DATETIME_RFC339 BaseModel = declarative_base() diff --git a/stac_api/models/decompose.py b/stac_api/models/decompose.py index a94698132..5d7b9f2fd 100644 --- a/stac_api/models/decompose.py +++ b/stac_api/models/decompose.py @@ -6,10 +6,11 @@ import geoalchemy2 as ga from pydantic import BaseModel from pydantic.utils import GetterDict +from stac_pydantic.shared import DATETIME_RFC339 + from stac_api import config from stac_api.errors import DatabaseError from stac_api.models.links import CollectionLinks, ItemLinks, filter_links -from stac_pydantic.shared import DATETIME_RFC339 def resolve_links(links: list, base_url: str) -> List[Dict]: diff --git a/stac_api/models/schemas.py b/stac_api/models/schemas.py index 1f7693899..ac2a5840f 100644 --- a/stac_api/models/schemas.py +++ b/stac_api/models/schemas.py @@ -8,14 +8,11 @@ from typing import Any, Callable, Dict, List, Optional, Set, Union import sqlalchemy as sa -from shapely.geometry import Polygon as ShapelyPolygon -from shapely.geometry import shape - from geojson_pydantic.geometries import Polygon from pydantic import Field, ValidationError, root_validator from pydantic.error_wrappers import ErrorWrapper -from stac_api import config -from stac_api.models.decompose import CollectionGetter, ItemGetter +from shapely.geometry import Polygon as ShapelyPolygon +from shapely.geometry import shape from stac_pydantic import Collection as CollectionBase from stac_pydantic import Item as ItemBase from stac_pydantic.api import Search @@ -24,6 +21,9 @@ from stac_pydantic.shared import Link from stac_pydantic.utils import AutoValueEnum +from stac_api import config +from stac_api.models.decompose import CollectionGetter, ItemGetter + # Be careful: https://github.com/samuelcolvin/pydantic/issues/1423#issuecomment-642797287 NumType = Union[float, int] diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..9bc3156db --- /dev/null +++ b/tox.ini @@ -0,0 +1,17 @@ +# Linter configs +[flake8] +ignore = D203 +exclude = .git,__pycache__,docs/source/conf.py,old,build,dist +max-complexity = 12 +max-line-length = 90 + +[mypy] +no_strict_optional = true +ignore_missing_imports = True + +[tool:isort] +profile=black +known_arturo=arturo +known_first_party = stac_api +known_third_party = rasterio,stac-pydantic,sqlalchemy,geoalchemy2,fastapi +sections=FUTURE,STDLIB,THIRDPARTY,ARTURO,FIRSTPARTY,LOCALFOLDER \ No newline at end of file