diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index decf317..2147fac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,11 @@ repos: + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.12.1 + hooks: + - id: validate-pyproject + - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.12.0 hooks: - id: black language_version: python @@ -11,26 +16,18 @@ repos: - id: isort language_version: python - - repo: https://github.com/PyCQA/flake8 - rev: 3.8.3 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.238 hooks: - - id: flake8 - language_version: python - - - repo: https://github.com/PyCQA/pydocstyle - rev: 6.1.1 - hooks: - - id: pydocstyle - language_version: python - additional_dependencies: - - toml + - id: ruff + args: ["--fix"] - repo: https://github.com/pre-commit/mirrors-mypy rev: v0.991 hooks: - id: mypy language_version: python + # No reason to run if only tests have changed. They intentionally break typing. + exclude: tests/.* additional_dependencies: - types-attrs - - diff --git a/CHANGES.md b/CHANGES.md index a1c50e3..421dc67 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,10 @@ +## 0.11.0 (2023-10-18) + +* update requirements + - `rio-tiler>=6.0,<7.0` + - `fastapi>=0.100.0` + ## 0.10.0 (2023-06-02) * update `rio-tiler` requirement diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fc0f461 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Development - Contributing + +Issues and pull requests are more than welcome: https://github.com/developmentseed/tilebench/issues + +**dev install** + +```bash +git clone https://github.com/developmentseed/tilebench.git +cd tilebench +python -m pip install -e ".[dev,test]" +``` + +You can then run the tests with the following command: + +```sh +python -m pytest --cov tilebench --cov-report term-missing -s -vv +``` + +This repo is set to use `pre-commit` to run *isort*, *flake8*, *pydocstring*, *black* ("uncompromising Python code formatter") and mypy when committing new code. + +```bash +$ pre-commit install +``` diff --git a/pyproject.toml b/pyproject.toml index d0e21ab..d3dc124 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "tilebench" description = "Inspect HEAD/LIST/GET requests withing Rasterio" requires-python = ">=3.8" -license = "MIT" +license = {file = "LICENSE"} authors = [ {name = "Vincent Sarago", email = "vincent@developmentseed.com"}, ] @@ -20,13 +20,11 @@ classifiers = [ ] dynamic = ["version", "readme"] dependencies = [ - "aiofiles", - "fastapi>=0.73", + "fastapi>=0.100.0", "jinja2>=3.0,<4.0.0", - "geojson-pydantic", "loguru", "rasterio>=1.3.0", - "rio-tiler>=4.0,<6.0", + "rio-tiler>=6.0,<7.0", "wurlitzer", "uvicorn[standard]", ] @@ -92,8 +90,19 @@ known_third_party = ["rasterio", "rio_tiler", "morecantile", "geojson_pydantic", default_section = "THIRDPARTY" [tool.mypy] -no_strict_optional = "True" +no_strict_optional = true -[tool.pydocstyle] -select = "D1" -match = "(?!test).*.py" +[tool.ruff] +select = [ + "D1", # pydocstyle errors + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # flake8 + "C", # flake8-comprehensions + "B", # flake8-bugbear +] +ignore = [ + "E501", # line too long, handled by black + "B008", # do not perform function calls in argument defaults + "B905", # ignore zip() without an explicit strict= parameter, only support with python >3.10 +] diff --git a/tilebench/scripts/cli.py b/tilebench/scripts/cli.py index 3a63e42..6e91d42 100644 --- a/tilebench/scripts/cli.py +++ b/tilebench/scripts/cli.py @@ -135,7 +135,7 @@ def get_zooms(input, reader): Reader = reader or COGReader with Reader(input, tms=tms) as cog: - click.echo(json.dumps(dict(minzoom=cog.minzoom, maxzoom=cog.maxzoom))) + click.echo(json.dumps({"minzoom": cog.minzoom, "maxzoom": cog.maxzoom})) @cli.command() diff --git a/tilebench/viz.py b/tilebench/viz.py index b4586aa..77c48b0 100644 --- a/tilebench/viz.py +++ b/tilebench/viz.py @@ -9,7 +9,7 @@ import numpy import rasterio import uvicorn -from fastapi import APIRouter, FastAPI, Query +from fastapi import APIRouter, FastAPI, Path, Query from fastapi.staticfiles import StaticFiles from rasterio import windows from rasterio._path import _parse_path as parse_path @@ -20,6 +20,7 @@ from starlette.requests import Request from starlette.responses import HTMLResponse, Response from starlette.templating import Jinja2Templates +from typing_extensions import Annotated from tilebench import Timer from tilebench import profile as profiler @@ -149,10 +150,31 @@ def register_routes(self): """Register routes to the FastAPI app.""" @self.router.get(r"/tiles/{z}/{x}/{y}.png", response_class=PNGResponse) - def image(response: Response, z: int, x: int, y: int): + def image( + response: Response, + z: Annotated[ + int, + Path( + description="Identifier (Z) selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile.", + ), + ], + x: Annotated[ + int, + Path( + description="Column (X) index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix.", + ), + ], + y: Annotated[ + int, + Path( + description="Row (Y) index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix.", + ), + ], + ): """Handle /image requests.""" with self.reader(self.src_path) as src_dst: img = src_dst.tile(x, y, z) + return PNGResponse( render( numpy.zeros((1, 256, 256), dtype="uint8"), @@ -163,7 +185,27 @@ def image(response: Response, z: int, x: int, y: int): ) @self.router.get(r"/tiles/{z}/{x}/{y}") - def tile(response: Response, z: int, x: int, y: int): + def tile( + response: Response, + z: Annotated[ + int, + Path( + description="Identifier (Z) selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile.", + ), + ], + x: Annotated[ + int, + Path( + description="Column (X) index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix.", + ), + ], + y: Annotated[ + int, + Path( + description="Row (Y) index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix.", + ), + ], + ): """Handle /tiles requests.""" @profiler( @@ -272,7 +314,7 @@ def info(): response_model_exclude_none=True, response_class=GeoJSONResponse, ) - def grid(ovr_level: int = Query(...)): + def grid(ovr_level: Annotated[int, Query(description="Overview Level")]): """return geojson.""" options = {"OVERVIEW_LEVEL": ovr_level - 1} if ovr_level else {} with rasterio.open(self.src_path, **options) as src_dst: