Skip to content

Commit eb1bc3c

Browse files
committed
feat: lambda build improvements + default pythonpath
1 parent 5afc4aa commit eb1bc3c

6 files changed

Lines changed: 64 additions & 46 deletions

skeleton/ipython_startup.py

Whitespace-only changes.

skeleton/justfile.jinja

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
set dotenv-load
22

3-
PYTHONPATH := "./{{ source_catalog_name }}"
43
PATHS_TO_LINT := "{{ source_catalog_name }} tests"
54
TEST_PATH := "tests"
65
ANSWERS_FILE := ".copier/.copier-answers.copier-python-project.yml"
7-
{%- if is_docker_project %}
8-
CONTAINER_NAME := "{{ project_name_snake_case }}"
9-
WORKDIR := "/code"
10-
{%- endif %}
116
{%- if is_lambda_project %}
12-
BUILD_PATH := "build"
7+
BUILD_DIR := "build"
138
CURRENT_DATETIME := `date +'%Y%m%d_%H%M%S'`
149
PYTHON_VERSION := "3.13"
1510
{%- endif %}
11+
{%- if is_docker_project %}
12+
CONTAINER_NAME := "{{ project_name_snake_case }}"
13+
WORKDIR := "/code"
14+
{%- endif %}
1615
{%- raw %}
16+
1717
[doc("Command run when 'just' is called without any arguments")]
1818
default: help
1919

@@ -28,22 +28,6 @@ all: lint_full test
2828
[group("development")]
2929
[doc("Run all checks and tests, but fail on first that returns error (lints, mypy, tests...)")]
3030
all_ff: lint_full_ff test
31-
{%- endraw -%}
32-
{%- if is_docker_project -%}
33-
{%- raw %}
34-
35-
[group("development")]
36-
[doc("Run any command inside docker (e.g. just dc bash)")]
37-
dc command *args:
38-
docker compose run --rm -w {{WORKDIR}} {{CONTAINER_NAME}} just {{command}} {{args}}
39-
40-
[group("development")]
41-
[doc("Open bash console (useful when prefixed with dc, as it opens bash inside docker)")]
42-
@bash:
43-
bash
44-
{%- endraw -%}
45-
{%- endif -%}
46-
{%- raw %}
4731

4832
[group("lint")]
4933
[doc("Run ruff lint check (code formatting)")]
@@ -90,37 +74,53 @@ alias full_lint_ff := lint_full_ff
9074

9175
[group("lint")]
9276
[doc("Run mypy check (type checking)")]
93-
mypy: _set_pythonpath
77+
mypy:
9478
uv run mypy {{PATHS_TO_LINT}} --show-error-codes --show-traceback --implicit-reexport
9579

9680
[group("development")]
97-
[doc("Open python console (useful when prefixed with dc, as it opens python console inside docker)")]
98-
ps:
99-
PYTHONPATH={{PYTHONPATH}} uv run ipython
81+
[doc("Run IPython with custom startup script")]
82+
ps startup_script="ipython_startup.py":
83+
PYTHONSTARTUP={{ startup_script }} uv run ipython
10084
alias ipython := ps
10185

102-
[doc("Helper command, sets PYTHONPATH")]
103-
_set_pythonpath path=PYTHONPATH:
104-
PYTHONPATH={{path}}
105-
10686
[group("development")]
10787
[doc("Run non-integration tests (optionally specify file=path/to/test_file.py)")]
108-
test file=TEST_PATH: _set_pythonpath
88+
test file=TEST_PATH:
10989
uv run pytest {{file}} --durations=10
90+
{%- endraw -%}
91+
{%- if is_docker_project -%}
92+
{%- raw %}
93+
94+
[group("development")]
95+
[doc("Run any command inside docker (e.g. just dc bash)")]
96+
dc command *args:
97+
docker compose run --rm -w {{WORKDIR}} {{CONTAINER_NAME}} just {{command}} {{args}}
98+
99+
[group("development")]
100+
[doc("Open bash console (useful when prefixed with dc, as it opens bash inside docker)")]
101+
@bash:
102+
bash
103+
{%- endraw -%}
104+
{%- endif -%}
105+
{%- raw %}
110106
{% endraw -%}
111107
{%- if is_lambda_project -%}
112108
{%- raw %}
113109
[group("lambda_build")]
114110
build: build_create_env
115-
cd {{ BUILD_PATH }}/pkg && \
111+
cd {{ BUILD_DIR }}/pkg && \
116112
zip -r ../{% endraw %}{{ source_catalog_name }}{% raw %}_{{ CURRENT_DATETIME }}.zip .
117113

118114
[group("lambda_build")]
119-
build_create_env: build_generate_requirements build_install
115+
build_create_env: clear_build_dir build_generate_requirements build_install copy_shim
116+
117+
[group("lambda_build")]
118+
clear_build_dir:
119+
rm -rf {{ BUILD_DIR }}/pkg
120120

121121
[group("lambda_build")]
122122
build_generate_requirements:
123-
uv export --frozen --no-dev --no-editable -o {{ BUILD_PATH }}/requirements.txt
123+
uv export --frozen --no-dev --no-editable -o {{ BUILD_DIR }}/requirements.txt
124124

125125
[group("lambda_build")]
126126
build_install:
@@ -129,7 +129,11 @@ build_install:
129129
--no-compile-bytecode \
130130
--python-platform x86_64-manylinux2014 \
131131
--python {{ PYTHON_VERSION }} \
132-
--target {{ BUILD_PATH }}/pkg \
133-
-r {{ BUILD_PATH }}/requirements.txt
132+
--target {{ BUILD_DIR }}/pkg \
133+
-r {{ BUILD_DIR }}/requirements.txt
134+
135+
[group("lambda_build")]
136+
copy_shim:
137+
cp lambda_function.py {{ BUILD_DIR }}/pkg/ || true
134138
{% endraw -%}
135139
{%- endif -%}

skeleton/pyproject.toml.jinja

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ dev = [
1515
"fawltydeps>=0.20.0,<1.0.0",
1616
"ipython>=9.9.0,<10",
1717
"mypy>=1.19.1,<2",
18-
"pytest-asyncio>=1.3.0,<2",
1918
"pytest-cov>=7.0.0,<8",
2019
"pytest-mock>=3.15.1,<4",
2120
"pytest>=9.0.2,<10",
2221
"ruff>=0.14.14,<0.15.0",
22+
{%- if not is_lambda_project %}
23+
"pytest-asyncio>=1.3.0,<2",
24+
{%- endif %}
2325
{%- if is_docker_project %}
2426
"dotenv-linter>=0.7.0,<1",
2527
"pytest-env>=1.2.0,<2",
@@ -84,11 +86,13 @@ ignore_unused = [
8486
"fawltydeps",
8587
"ipython",
8688
"mypy",
87-
"pytest-asyncio",
8889
"pytest-cov",
8990
"pytest-mock",
9091
"pytest",
9192
"ruff",
93+
{%- if not is_lambda_project %}
94+
"pytest-asyncio",
95+
{%- endif %}
9296
{%- if is_docker_project %}
9397
"dotenv-linter",
9498
"pytest-env",
@@ -104,8 +108,11 @@ disable_error_code = "misc"
104108
105109
[tool.pytest.ini_options]
106110
python_files = ["tests.py", "test_*.py", "*_tests.py"]
107-
addopts = "--strict-markers -p no:warnings --cov=. --cov-fail-under=90 --cov-config=.coveragerc"
111+
testpaths = "tests"
112+
addopts = "--strict-markers -p no:warnings --cov={{ source_catalog_name }} --cov-fail-under=90"
113+
{%- if not is_lambda_project %}
108114
asyncio_mode = "auto"
115+
{%- endif %}
109116
{%- if is_docker_project %}
110117
111118
[tool.pytest_env]

skeleton/tests/{% if is_lambda_project %}conftest.py{% endif %}.jinja

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from dataclasses import dataclass
2-
from typing import Any
32

43
import pytest
4+
from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEventV2
55

66

77
@dataclass
@@ -18,8 +18,8 @@ def lambda_context() -> LambdaContext:
1818

1919

2020
@pytest.fixture()
21-
def api_gw_event() -> dict[str, Any]:
22-
return {
21+
def api_gw_event() -> APIGatewayProxyEventV2:
22+
event_body = {
2323
"headers": {},
2424
"body": "",
2525
"requestContext": {
@@ -35,3 +35,4 @@ def api_gw_event() -> dict[str, Any]:
3535
"resource": "/{some_id}/some_path",
3636
"rawPath": "/some_id/some_path",
3737
}
38+
return APIGatewayProxyEventV2(event_body)
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
from typing import Any
2-
1+
from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEventV2
32
from aws_lambda_powertools.utilities.typing import LambdaContext
43

54
from {{ source_catalog_name }}.lambda_function import lambda_handler
65

76

87
def test_lambda_handler(
9-
api_gw_event: dict[str, Any],
8+
api_gw_event: APIGatewayProxyEventV2,
109
lambda_context: LambdaContext,
1110
) -> None:
12-
response = lambda_handler(event=api_gw_event, context=lambda_context)
11+
response = lambda_handler(event=api_gw_event, context=lambda_context) # type: ignore
1312
assert response["statusCode"] == 200
1413
assert response["body"] == "hello world"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""This file's purpose is to expose real lambda handler from main module."""
2+
3+
from {{ source_catalog_name }}.lambda_function import lambda_handler
4+
5+
__all__ = [
6+
"lambda_handler",
7+
]

0 commit comments

Comments
 (0)