From 35667641141cd92e823e35f352c81fa3647adc8f Mon Sep 17 00:00:00 2001 From: Fredrik Skold Date: Sat, 18 Jun 2022 18:33:04 +0200 Subject: [PATCH 1/6] Update precommit config, and reformat code Change language_version from python3.7 to any python Bump to black 23.11.0. Remove extra comma in CustomPathParams.__post_init__() causing reformating. Reformated all code using black: git ls-files | grep "\.py" | xargs touch pre-commit run -v --all-files --- .pre-commit-config.yaml | 20 ++++++++++---------- setup.py | 2 +- stack/app.py | 3 ++- titiler_pds/dependencies.py | 15 ++++++++++----- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e233659..69ba042 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,22 +1,22 @@ repos: - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 23.11.0 hooks: - id: black - language_version: python3.7 + language_version: python args: ["--safe"] - repo: https://github.com/PyCQA/isort - rev: 5.4.2 + rev: 5.12.0 hooks: - id: isort - language_version: python3.7 + language_version: python - repo: https://github.com/PyCQA/flake8 - rev: 3.8.3 + rev: 6.1.0 hooks: - id: flake8 - language_version: python3.7 + language_version: python args: [ # E501 let black handle all line length decisions # W503 black conflicts with "line break before operator" rule @@ -25,10 +25,10 @@ repos: ] - repo: https://github.com/PyCQA/pydocstyle - rev: 5.1.1 + rev: 6.3.0 hooks: - id: pydocstyle - language_version: python3.7 + language_version: python args: [ # Check for docstring presence only "--select=D1", @@ -37,8 +37,8 @@ repos: ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.770 + rev: v1.7.0 hooks: - id: mypy - language_version: python3.7 + language_version: python args: ["--no-strict-optional", "--ignore-missing-imports"] diff --git a/setup.py b/setup.py index ae17114..4d13bca 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ setup( name="titiler_pds", version="0.0.1", - description=u"TiTiler for AWS Public Dataset", + description="TiTiler for AWS Public Dataset", python_requires=">=3", classifiers=[ "Intended Audience :: Information Technology", diff --git a/stack/app.py b/stack/app.py index 0c1159e..2910b0b 100644 --- a/stack/app.py +++ b/stack/app.py @@ -63,7 +63,8 @@ def __init__( path=os.path.abspath(code_dir), bundling=core.BundlingOptions( image=core.BundlingDockerImage.from_asset( - os.path.abspath(code_dir), file="Dockerfile", + os.path.abspath(code_dir), + file="Dockerfile", ), command=["bash", "-c", "cp -R /var/task/. /asset-output/."], ), diff --git a/titiler_pds/dependencies.py b/titiler_pds/dependencies.py index cb41d5c..6f4f3df 100644 --- a/titiler_pds/dependencies.py +++ b/titiler_pds/dependencies.py @@ -21,7 +21,7 @@ class CustomPathParams: sceneid: str = Query(..., description="Sceneid.") scene_metadata: Dict = field(init=False) - def __post_init__(self,): + def __post_init__(self): """Define dataset URL.""" self.url = self.sceneid if re.match( @@ -49,7 +49,9 @@ def __post_init__(self,): def BandsParams( bands: str = Query( - ..., title="bands names", description="comma (',') delimited bands names.", + ..., + title="bands names", + description="comma (',') delimited bands names.", ) ) -> Sequence[str]: """Bands.""" @@ -61,7 +63,9 @@ class BandsExprParams(DefaultDependency): """Band names and Expression parameters.""" bands: Optional[str] = Query( - None, title="bands names", description="comma (',') delimited bands names.", + None, + title="bands names", + description="comma (',') delimited bands names.", ) expression: Optional[str] = Query( None, @@ -83,14 +87,15 @@ class MosaicParams: layer: str = Query(..., description="Mosaic Layer name ('{username}.{layer}')") - def __post_init__(self,): + def __post_init__(self): """Define mosaic URL.""" pattern = ( r"^(?P[a-zA-Z0-9-_]{1,32})\.(?P[a-zA-Z0-9-_]{1,32})$" ) if not re.match(pattern, self.layer): raise HTTPException( - status_code=400, detail=f"Invalid layer name: `{self.layer}`", + status_code=400, + detail=f"Invalid layer name: `{self.layer}`", ) if mosaic_config.backend == "dynamodb://": self.url = f"{mosaic_config.backend}{mosaic_config.host}:{self.layer}" From d814d156320426e1abf8fac2420f87b5ff7dc588 Mon Sep 17 00:00:00 2001 From: Fredrik Skold Date: Sun, 19 Jun 2022 01:59:32 +0200 Subject: [PATCH 2/6] Sync with titiler (v0.7.0), WIP WIP: When running this. There is an issue that the CustomPathParams with two members sceneid and scene_metadata is passed as path_dependency. The user of that dependency assumes to get a string that is interpreted to be a sceneid, not get an object. **I would need some gudience on what changes to make.** Update version of (this) package to 0.1.0 Use rio-tiler-pds and titiler 0.7.0. Advance aws-cdk version to 1.160.0 The titiler package has been split into three packages, do refer to each of them. Update python imports and usage. (When trying to build using the old Dockerfile, the output was too big to be used in the old way.) Change to build a docker imaged based on AWS python image instead of creating a bundle that is overlayed on the AWS python environment. Changed several things in stack/app.py: * One thing had changed name to apigw_integrations.HttpLambdaIntegration and required a new id argument. * Use a docker image instead of using docker image to construct a usable zip for the python runtime FROM_IMAGE. --- .gitignore | 1 + Dockerfile | 29 ++++++++++++----------- setup.py | 17 +++++++------ stack/app.py | 21 ++++++---------- titiler_pds/dependencies.py | 2 +- titiler_pds/handler.py | 2 +- titiler_pds/main.py | 4 ++-- titiler_pds/routes/landsat.py | 9 +++---- titiler_pds/routes/landsat_collection2.py | 9 +++---- titiler_pds/routes/naip.py | 8 +++---- titiler_pds/routes/sentinel.py | 7 +++--- 11 files changed, 55 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index e91eca9..1868548 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,4 @@ ENV/ .mypy_cache/ cdk.out/ +cdk.context.json diff --git a/Dockerfile b/Dockerfile index c769786..4eb8869 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,19 @@ -FROM lambci/lambda:build-python3.8 +FROM public.ecr.aws/lambda/python:3.9 -WORKDIR /tmp - -COPY setup.py setup.py -COPY titiler_pds/ titiler_pds/ +COPY setup.py ${LAMBDA_TASK_ROOT} +COPY titiler_pds/ ${LAMBDA_TASK_ROOT}/titiler_pds/ # Install dependencies -RUN pip install . rasterio==1.1.8 -t /var/task --no-binary numpy,pydantic - -# Leave module precompiles for faster Lambda startup -RUN cd /var/task && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[2-3][0-9]//'); cp $f $n; done; -RUN cd /var/task && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf -RUN cd /var/task && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f -RUN cd /var/task && find . -type d -a -name 'tests' -print0 | xargs -0 rm -rf -RUN rm -rdf /var/task/numpy/doc/ -RUN rm -rdf /var/task/stack +RUN pip3 install . rasterio==1.3a2 -t ${LAMBDA_TASK_ROOT} && \ + \ + echo "Leave module precompiles for faster Lambda startup" && \ + cd ${LAMBDA_TASK_ROOT} && find . -type f -name '*.pyc' | \ + while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[2-3][0-9]//'); cp $f $n; done && \ + \ + cd ${LAMBDA_TASK_ROOT} && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf && \ + cd ${LAMBDA_TASK_ROOT} && find . -type f -a -name '*.py' -print0 | grep -v handler.py | xargs -0 rm -f && \ + cd ${LAMBDA_TASK_ROOT} && find . -type d -a -name 'tests' -print0 | xargs -0 rm -rf && \ + rm -rdf ${LAMBDA_TASK_ROOT}/numpy/doc/ && \ + rm -rdf ${LAMBDA_TASK_ROOT}/stack +CMD [ "titiler_pds.handler.handler" ] diff --git a/setup.py b/setup.py index 4d13bca..01363e9 100644 --- a/setup.py +++ b/setup.py @@ -3,18 +3,21 @@ from setuptools import find_packages, setup inst_reqs = [ - "titiler>=0.1.0,<0.2", + "brotli-asgi>=1.1.0", + "titiler.core>=0.7.0", + "titiler.mosaic>=0.7.0", + "titiler.application>=0.7.0", "tilebench", - "rio-tiler-pds>=0.5.0,<1.0", + "rio-tiler-pds>=0.7.0,<1.0", "mangum>=0.10", ] extra_reqs = { "deploy": [ - "aws-cdk.core==1.76.0", - "aws-cdk.aws_lambda==1.76.0", - "aws-cdk.aws_apigatewayv2==1.76.0", - "aws-cdk.aws_apigatewayv2_integrations==1.76.0", + "aws-cdk.core==1.160.0", + "aws-cdk.aws_lambda==1.160.0", + "aws-cdk.aws_apigatewayv2==1.160.0", + "aws-cdk.aws_apigatewayv2_integrations==1.160.0", ], "test": ["pytest", "pytest-cov", "pytest-asyncio", "requests"], } @@ -22,7 +25,7 @@ setup( name="titiler_pds", - version="0.0.1", + version="0.1.0", description="TiTiler for AWS Public Dataset", python_requires=">=3", classifiers=[ diff --git a/stack/app.py b/stack/app.py index 2910b0b..a009bfa 100644 --- a/stack/app.py +++ b/stack/app.py @@ -35,7 +35,7 @@ def __init__( id: str, memory: int = 1024, timeout: int = 30, - runtime: aws_lambda.Runtime = aws_lambda.Runtime.PYTHON_3_8, + runtime: aws_lambda.Runtime = aws_lambda.Runtime.FROM_IMAGE, concurrent: Optional[int] = None, permissions: Optional[List[iam.PolicyStatement]] = None, env: dict = {}, @@ -59,17 +59,10 @@ def __init__( self, f"{id}-lambda", runtime=runtime, - code=aws_lambda.Code.from_asset( - path=os.path.abspath(code_dir), - bundling=core.BundlingOptions( - image=core.BundlingDockerImage.from_asset( - os.path.abspath(code_dir), - file="Dockerfile", - ), - command=["bash", "-c", "cp -R /var/task/. /asset-output/."], - ), + code=aws_lambda.Code.from_asset_image( + directory=os.path.abspath(code_dir), ), - handler="titiler_pds.handler.handler", + handler=aws_lambda.Handler.FROM_IMAGE, memory_size=memory, reserved_concurrent_executions=concurrent, timeout=core.Duration.seconds(timeout), @@ -82,8 +75,8 @@ def __init__( api = apigw.HttpApi( self, f"{id}-endpoint", - default_integration=apigw_integrations.LambdaProxyIntegration( - handler=lambda_function + default_integration=apigw_integrations.HttpLambdaIntegration( + id=f"{id}-endpoint-lambda", handler=lambda_function ), ) core.CfnOutput(self, "Endpoint", value=api.url) @@ -125,7 +118,7 @@ def __init__( "Client": settings.client, }.items(): if value: - core.Tag.add(app, key, value) + core.Tag(key, value, apply_to_launched_instances=False) LambdaStack( diff --git a/titiler_pds/dependencies.py b/titiler_pds/dependencies.py index 6f4f3df..d7be334 100644 --- a/titiler_pds/dependencies.py +++ b/titiler_pds/dependencies.py @@ -7,7 +7,7 @@ from rio_tiler_pds.landsat.utils import sceneid_parser as l8_sceneid_parser from rio_tiler_pds.sentinel.utils import s2_sceneid_parser -from titiler.dependencies import DefaultDependency +from titiler.core.dependencies import DefaultDependency from .settings import mosaic_config diff --git a/titiler_pds/handler.py b/titiler_pds/handler.py index 0c47d6c..072a752 100644 --- a/titiler_pds/handler.py +++ b/titiler_pds/handler.py @@ -9,4 +9,4 @@ logging.getLogger("mangum.lifespan").setLevel(logging.ERROR) logging.getLogger("mangum.http").setLevel(logging.ERROR) -handler = Mangum(app, lifespan="auto", log_level="error") +handler = Mangum(app, lifespan="auto") diff --git a/titiler_pds/main.py b/titiler_pds/main.py index e5a2ace..eaf3bf3 100644 --- a/titiler_pds/main.py +++ b/titiler_pds/main.py @@ -5,8 +5,8 @@ from brotli_asgi import BrotliMiddleware from tilebench.middleware import VSIStatsMiddleware -from titiler.errors import DEFAULT_STATUS_CODES, add_exception_handlers -from titiler.middleware import CacheControlMiddleware, TotalTimeMiddleware +from titiler.application.middleware import CacheControlMiddleware, TotalTimeMiddleware +from titiler.core.errors import DEFAULT_STATUS_CODES, add_exception_handlers from .routes import landsat_collection2, naip, sentinel from .settings import api_config diff --git a/titiler_pds/routes/landsat.py b/titiler_pds/routes/landsat.py index 0638544..7f73354 100644 --- a/titiler_pds/routes/landsat.py +++ b/titiler_pds/routes/landsat.py @@ -1,10 +1,11 @@ """Landsat endpoint.""" -from rio_tiler_pds.landsat.aws import L8Reader +from rio_tiler_pds.landsat.aws.landsat8 import L8Reader -from titiler.custom.routing import apiroute_factory -from titiler.dependencies import BandsExprParams -from titiler.endpoints.factory import MosaicTilerFactory, MultiBandTilerFactory +from titiler.core.dependencies import BandsExprParams +from titiler.core.factory import MultiBandTilerFactory +from titiler.core.routing import apiroute_factory +from titiler.mosaic.factory import MosaicTilerFactory from ..dependencies import CustomPathParams, MosaicParams diff --git a/titiler_pds/routes/landsat_collection2.py b/titiler_pds/routes/landsat_collection2.py index eff08ec..611bfad 100644 --- a/titiler_pds/routes/landsat_collection2.py +++ b/titiler_pds/routes/landsat_collection2.py @@ -1,10 +1,11 @@ """Landsat endpoint.""" -from rio_tiler_pds.landsat.aws import LandsatC2Reader +from rio_tiler_pds.landsat.aws.landsat_collection2 import LandsatC2Reader -from titiler.custom.routing import apiroute_factory -from titiler.dependencies import BandsExprParams -from titiler.endpoints.factory import MosaicTilerFactory, MultiBandTilerFactory +from titiler.core.dependencies import BandsExprParams +from titiler.core.factory import MultiBandTilerFactory +from titiler.core.routing import apiroute_factory +from titiler.mosaic.factory import MosaicTilerFactory from ..dependencies import CustomPathParams, MosaicParams diff --git a/titiler_pds/routes/naip.py b/titiler_pds/routes/naip.py index 73d844e..92ff5ac 100644 --- a/titiler_pds/routes/naip.py +++ b/titiler_pds/routes/naip.py @@ -1,8 +1,8 @@ """NAIP endpoint.""" -from titiler.custom.routing import apiroute_factory -from titiler.endpoints.factory import MosaicTilerFactory -from titiler.resources.enums import OptionalHeaders +from titiler.core.resources.enums import OptionalHeader +from titiler.core.routing import apiroute_factory +from titiler.mosaic.factory import MosaicTilerFactory from ..dependencies import MosaicParams @@ -20,5 +20,5 @@ path_dependency=MosaicParams, router_prefix="mosaicjson/naip", router=APIRouter(route_class=route_class), - optional_headers=[OptionalHeaders.server_timing, OptionalHeaders.x_assets], + optional_headers=[OptionalHeader.server_timing, OptionalHeader.x_assets], ) diff --git a/titiler_pds/routes/sentinel.py b/titiler_pds/routes/sentinel.py index 6dcb908..03995b0 100644 --- a/titiler_pds/routes/sentinel.py +++ b/titiler_pds/routes/sentinel.py @@ -2,9 +2,10 @@ from rio_tiler_pds.sentinel.aws import S2COGReader -from titiler.custom.routing import apiroute_factory -from titiler.dependencies import BandsExprParams -from titiler.endpoints.factory import MosaicTilerFactory, MultiBandTilerFactory +from titiler.core.dependencies import BandsExprParams +from titiler.core.factory import MultiBandTilerFactory +from titiler.core.routing import apiroute_factory +from titiler.mosaic.factory import MosaicTilerFactory from ..dependencies import CustomPathParams, MosaicParams From 6fd1003e795f20b7dd020a536b0336f44697186b Mon Sep 17 00:00:00 2001 From: Fredrik Skold Date: Thu, 14 Jul 2022 01:48:40 +0200 Subject: [PATCH 3/6] String method, could be an alternative --- titiler_pds/dependencies.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/titiler_pds/dependencies.py b/titiler_pds/dependencies.py index d7be334..9ef0c0b 100644 --- a/titiler_pds/dependencies.py +++ b/titiler_pds/dependencies.py @@ -46,6 +46,9 @@ def __post_init__(self): ): self.scene_metadata = l8_sceneid_parser(self.sceneid) + def __str__(self): + return self.sceneid + def BandsParams( bands: str = Query( From 724dd8f9e181241448109b80223d8beb01fbe3ac Mon Sep 17 00:00:00 2001 From: Fredrik Skold Date: Tue, 12 Jul 2022 00:00:55 +0200 Subject: [PATCH 4/6] Try to solve CustomPathParams Add wheel to use with corrections Update dockerfile Try to see if we could pass the correct thing. --- Dockerfile | 3 ++- .../rio_tiler_pds-0.7.0-py3-none-any.whl | Bin 0 -> 33370 bytes titiler_pds/routes/sentinel.py | 10 +++++++--- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 titiler_pds/rio_tiler_pds-0.7.0-py3-none-any.whl diff --git a/Dockerfile b/Dockerfile index 4eb8869..664bf37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,8 @@ COPY setup.py ${LAMBDA_TASK_ROOT} COPY titiler_pds/ ${LAMBDA_TASK_ROOT}/titiler_pds/ # Install dependencies -RUN pip3 install . rasterio==1.3a2 -t ${LAMBDA_TASK_ROOT} && \ +RUN pip3 install titiler_pds/rio_tiler_pds-0.7.0-py3-none-any.whl -t ${LAMBDA_TASK_ROOT} && \ + pip3 install . rasterio==1.3a2 -t ${LAMBDA_TASK_ROOT} && \ \ echo "Leave module precompiles for faster Lambda startup" && \ cd ${LAMBDA_TASK_ROOT} && find . -type f -name '*.pyc' | \ diff --git a/titiler_pds/rio_tiler_pds-0.7.0-py3-none-any.whl b/titiler_pds/rio_tiler_pds-0.7.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..661d00d440a77e30376bd35311b51b8911ab3bd3 GIT binary patch literal 33370 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 &sYhrln|>7DYpAqB>s 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 Date: Thu, 14 Jul 2022 02:07:41 +0200 Subject: [PATCH 5/6] docstring __str__ --- titiler_pds/dependencies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/titiler_pds/dependencies.py b/titiler_pds/dependencies.py index 9ef0c0b..c6e28dd 100644 --- a/titiler_pds/dependencies.py +++ b/titiler_pds/dependencies.py @@ -47,6 +47,7 @@ def __post_init__(self): self.scene_metadata = l8_sceneid_parser(self.sceneid) def __str__(self): + """to string""" return self.sceneid From c6bc1d6fc0b4ad12bc98638f2a61acc1846f6589 Mon Sep 17 00:00:00 2001 From: Fredrik Skold Date: Fri, 16 Sep 2022 20:50:50 +0200 Subject: [PATCH 6/6] another try --- titiler_pds/routes/sentinel.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/titiler_pds/routes/sentinel.py b/titiler_pds/routes/sentinel.py index 3745adc..c60ac8b 100644 --- a/titiler_pds/routes/sentinel.py +++ b/titiler_pds/routes/sentinel.py @@ -9,18 +9,13 @@ from ..dependencies import CustomPathParams, MosaicParams -from fastapi import APIRouter, Depends - - -def get_callable_dependency(value: CustomPathParams = Depends(callable_dependency)): - return value.sceneid - +from fastapi import APIRouter route_class = apiroute_factory({"AWS_NO_SIGN_REQUEST": "YES"}) scenes = MultiBandTilerFactory( reader=S2COGReader, - path_dependency=get_callable_dependency, + path_dependency=CustomPathParams, router_prefix="scenes/sentinel", router=APIRouter(route_class=route_class), )