astral-sh / astral-sh/uv-aws-lambda-example

Improvement: run tests with pytest

Open
#1 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dockerfile
Stars
64
Forks
6
PR merge metrics
No merged PRs in 30d

Description

Thank you @charliermarsh for this example, I found it during an experimentation with `uv` and lambdas.

In my workflow, I usually run tests before deployment.
Based on your example, I'd have to add few lines to the `Dockerfile`:

```dockerfile
# Place executables in the environment at the front of the path
ENV PATH="${LAMBDA_TASK_ROOT}/bin:$PATH" # actually taken from uv + docker example
# Add PYTHONPATH to point to Lambda task root where packages are installed
ENV PYTHONPATH="${LAMBDA_TASK_ROOT}:${PYTHONPATH}"
```

These are needed when I want to run `RUN pytest`. Of course, I have a `tests` folder copied in the lambda's root folder.

A fully working example involves:
- adding `pytest` as dev dependency
- adding a `tests` folder with a basic test

This is the working Dockerfile:

```dockerfile
FROM ghcr.io/astral-sh/uv:latest AS uv

# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder

# Enable bytecode compilation.
ENV UV_COMPILE_BYTECODE=1

# Enable copy mode to support bind mount caching.
ENV UV_LINK_MODE=copy

# Bundle the dependencies into the Lambda task root via `uv pip install --target`.
#
# Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`).
# This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock`
# files change, but remains robust to changes in the application code.
RUN --mount=from=uv,source=/uv,target=/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv export --frozen --no-emit-workspace --dev --no-editable -o requirements.txt && \ # ⚠️ install dev group
uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

FROM public.ecr.aws/lambda/python:3.13

# Copy the runtime dependencies from the builder stage.
COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT}

# Copy the application code.
COPY ./app ${LAMBDA_TASK_ROOT}/app
COPY ./tests ${LAMBDA_TASK_ROOT}/tests # ⚠️ copy the tests to run

# ⚠️ the path modification mentioned above
# Place executables in the environment at the front of the path
ENV PATH="${LAMBDA_TASK_ROOT}/bin:$PATH"
# Add PYTHONPATH to point to Lambda task root where packages are installed
ENV PYTHONPATH="${LAMBDA_TASK_ROOT}:${PYTHONPATH}"

RUN pytest tests # ⚠️ run the tests

# Set the AWS Lambda handler.
CMD ["app.main.handler"]
```

I recognize the downside of the modifications I propose:
- you get the `dev` dependencies installed, which usually isn't something everyone desire; maybe `--only-dev` would help in this case
- your build process is slower
- the `tests` folder can be considerably big, depending maybe on fixtures; it should be deleted after the test session
Probably a smarter multi-stage build would solve the first and third downsides... 🤔 But it's more related to `Docker` than to `uv` 😌

Do you think it would be valuable to add this case to the example?
Thanks again 🤗

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.