Skip to content

Commit 8ac5d72

Browse files
committed
doc: tidy up and add more examples to README
1 parent f76d0d0 commit 8ac5d72

14 files changed

Lines changed: 531 additions & 121 deletions

File tree

.projen/deps.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projenrc.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { awscdk } from 'projen';
22
import { JobPermission } from 'projen/lib/github/workflows-model';
3+
// import { NodePackageManager } from 'projen/lib/javascript';
34
const project = new awscdk.AwsCdkConstructLibrary({
45
author: 'Eoin Shanaghy',
56
authorAddress: 'eoin.shanaghy@fourtheorem.com',
7+
// packageManager: NodePackageManager.YARN_CLASSIC,
68
cdkVersion: '2.161.1',
79
constructsVersion: '10.3.0',
810
defaultReleaseBranch: 'main',
9-
jsiiVersion: '~5.5.0',
11+
jsiiVersion: '~5.9.0',
1012
name: 'uv-python-lambda',
1113
projenrcTs: true,
1214
repositoryUrl: 'https://github.com/fourTheorem/uv-python-lambda',

.yarnrc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# ~~ Generated by projen. To modify, edit .projenrc.ts and run "yarn projen".
2+
3+
{}

AGENTS.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Agent Instructions
2+
3+
## Build, test, and lint
4+
5+
- Install dependencies with `yarn install --check-files` to match CI.
6+
- Build with `npx projen build`.
7+
- Run the full test suite with `npx projen test`.
8+
- Run one test file with `npx jest test/function.test.ts`.
9+
- Run one test case with `npx jest test/function.test.ts -t "Create a function from basic_app"`.
10+
- Run lint and formatting checks with `npx biome check`.
11+
- Check a specific file with `npx biome check src/function.ts`.
12+
13+
## Project architecture
14+
15+
- This repository is a projen-managed jsii CDK construct library.
16+
- Human-authored sources primarily live in `src/`, `test/`, `resources/`, and `.projenrc.ts`.
17+
- Generated files such as `package.json`, workflow files, and packaging config should be changed through `.projenrc.ts`, then regenerated with `npx projen`.
18+
- The public API is exported from `src/index.ts`.
19+
- The main construct is `PythonFunction` in `src/function.ts`, which extends `aws-cdk-lib/aws-lambda.Function`.
20+
21+
## Packaging model
22+
23+
- `PythonFunction` is a thin orchestration layer.
24+
- It derives the Lambda handler name from `index` and `handler`.
25+
- It enforces a Python runtime.
26+
- It defaults to `Architecture.ARM_64` and `Runtime.PYTHON_3_12`.
27+
- It delegates packaging to `Bundling.bundle(...)`.
28+
29+
- `src/bundling.ts` owns packaging.
30+
- It builds a Docker builder image from `resources/`.
31+
- It caches long-lived builder containers based on runtime, architecture, build args, and root dir.
32+
- It returns Lambda code through `Code.fromCustomCommand(...)` so CDK can stage the packaged asset.
33+
34+
- The Docker-side flow lives in `resources/entrypoint.sh` and `resources/export.sh`.
35+
- `entrypoint.sh` starts the builder container, mounts an overlay filesystem over `/src`, creates a uv virtualenv, runs `uv sync --no-dev --frozen --no-editable`, creates a lock file, and stays alive.
36+
- `export.sh` runs later via `docker exec`, waits for the lock file, optionally scopes to `--package <workspace>`, exports requirements, and installs dependencies into the requested asset output directory.
37+
38+
## Workspace behavior
39+
40+
- `workspacePackage` is the key monorepo and workspace feature.
41+
- It lets bundling treat a specific uv workspace package as the Lambda entry package while still resolving dependencies from the workspace root.
42+
43+
## Test model
44+
45+
- Tests in `test/function.test.ts` are integration-style CDK packaging tests, not isolated unit tests.
46+
- They create real CDK apps and stacks.
47+
- They require Docker.
48+
- They use fixture uv projects from `test/resources/`.
49+
- They inspect staged asset contents through the `uv-python-lambda:asset-path` metadata key.
50+
51+
## Conventions for agents
52+
53+
- Do not hand-edit generated project files.
54+
- `package.json` is generated by projen.
55+
- Workflows under `.github/workflows/` are generated from `.projenrc.ts`.
56+
- Keep changes aligned with Biome formatting and import ordering.
57+
- The repo uses Biome instead of ESLint and Prettier, with single quotes and organized imports.
58+
- Preserve jsii-friendly public types.
59+
- Public configuration is expressed as exported TypeScript interfaces such as `PythonFunctionProps`, `BundlingOptions`, and `ICommandHooks` because the library is packaged for both TypeScript and Python consumers.
60+
- Bundling assumes Docker-based execution.
61+
- There is no parallel local packager path, so packaging changes usually involve `src/bundling.ts` plus shell scripts in `resources/`.
62+
- Be careful with architecture defaults.
63+
- Production code defaults Lambda functions to ARM64, while tests intentionally detect Docker host architecture to avoid slow QEMU emulation on GitHub runners.
64+
- Asset metadata is intentional.
65+
- `PythonFunction` writes `uv-python-lambda:asset-path` metadata so tests and downstream CDK asset inspection can locate the staged bundle.

API.md

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 92 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,110 @@
11
# uv-python-lambda
22

3-
CDK Construct for Python Lambda Functions using [uv](https://docs.astral.sh/uv/)
3+
CDK construct for packaging Python Lambda functions from `uv` projects.
44

5-
## Goals
5+
## Why use it
66

7-
- ⚡️ Package and deploy Lambda Functions faster with `uv`'s speed
8-
- 📦 Support workspaces in a monorepo with [uv workspaces](https://docs.astral.sh/uv/concepts/workspaces/)
7+
- Packages Lambda assets from a `uv` project.
8+
- Supports `uv` workspaces, so you can package one workspace package while resolving dependencies from the workspace root.
9+
- Keeps the usual CDK Lambda experience: `PythonFunction` extends `aws_lambda.Function`, derives the handler from `index` and `handler`
10+
- Reuses builder containers across compatible functions during synthesis to reduce repeated setup work.
911

10-
`uv-python-lambda` is based on [aws-lambda-python-alpha](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-lambda-python-alpha-readme.html) with some differences:
12+
☝️ NOTE: This construct defaults to Python 3.12 on **ARM64**.
1113

12-
- It only supports `uv` for packaging - there is no Poetry or pip support
13-
- It supports workspaces so you can build multiple Lambda functions from different uv workspaces and have their dependencies included correctly. This is useful for, but not limited to, monorepos.
14+
This library is published for TypeScript/JavaScript and Python.
1415

15-
## API
16+
## Install
1617

17-
See [API.md](API.md)
18+
TypeScript / JavaScript:
1819

19-
## Example
20+
```bash
21+
npm install uv-python-lambda aws-cdk-lib constructs
22+
```
23+
24+
Python:
25+
26+
```bash
27+
uv add uv-python-lambda aws-cdk-lib constructs
28+
```
29+
30+
Docker is required for bundling.
31+
32+
## Usage
33+
34+
TypeScript:
35+
36+
```ts
37+
import * as path from 'node:path';
38+
import { Stack, Duration } from 'aws-cdk-lib';
39+
import { PythonFunction } from 'uv-python-lambda';
40+
import type { Construct } from 'constructs';
41+
42+
export class ExampleStack extends Stack {
43+
constructor(scope: Construct, id: string) {
44+
super(scope, id);
45+
46+
new PythonFunction(this, 'Fn', {
47+
rootDir: path.join(__dirname, '..', '..', 'services', 'fetcher'),
48+
index: 'handler.py',
49+
handler: 'lambda_handler',
50+
timeout: Duration.seconds(30),
51+
});
52+
}
53+
}
54+
```
55+
56+
Python:
2057

2158
```python
22-
from uv_python_lambda import PythonFunction
23-
from constructs import Construct
59+
from pathlib import Path
2460

25-
# The root path should be relative to your CDK source file
26-
root_path = Path(__file__).parent.parent.parent
61+
from aws_cdk import Duration, Stack
62+
from constructs import Construct
63+
from uv_python_lambda import PythonFunction
2764

2865

29-
class CdkStack(Stack):
66+
class ExampleStack(Stack):
3067
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
3168
super().__init__(scope, construct_id, **kwargs)
3269

33-
fn = PythonFunction(
34-
self,
35-
"fn",
36-
root_dir=str(root_path),
37-
index="fetcher_lambda.py",
38-
workspace_package="fetcher", # Use a workspace package as the top-level Lambda entry point.
39-
handler="handle_event",
40-
bundling={
41-
"asset_excludes": [
42-
".venv/",
43-
"node_modules/",
44-
"cdk/",
45-
".git/",
46-
".idea/",
47-
"dist/",
48-
]
49-
},
50-
timeout=Duration.seconds(30),
70+
root_dir = Path(__file__).resolve().parents[2] / "services" / "fetcher"
71+
72+
PythonFunction(
73+
self,
74+
"Fn",
75+
root_dir=str(root_dir),
76+
index="handler.py",
77+
handler="lambda_handler",
78+
timeout=Duration.seconds(30),
5179
)
52-
```
80+
```
81+
82+
## Using workspaces
83+
84+
Point `rootDir` or `root_dir` at the workspace root, then set `workspacePackage` or `workspace_package` to the package that contains the Lambda entrypoint.
85+
86+
```ts
87+
new PythonFunction(this, 'WorkspaceFn', {
88+
rootDir: path.join(__dirname, '..', '..'),
89+
workspacePackage: 'fetcher',
90+
index: 'fetcher_lambda.py',
91+
handler: 'handle_event',
92+
});
93+
```
94+
95+
```python
96+
PythonFunction(
97+
self,
98+
"WorkspaceFn",
99+
root_dir=str(root_dir),
100+
workspace_package="fetcher",
101+
index="fetcher_lambda.py",
102+
handler="handle_event",
103+
)
104+
```
105+
106+
## Notes
107+
108+
- `index` can be passed as `handler.py` or `handler`.
109+
- Use `bundling` to pass Docker environment variables, asset excludes, build args, or command hooks.
110+
- See [API.md](API.md) for the full API reference.

package.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/entrypoint.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
set -euo pipefail
77

88
export LOCK_FILE=/uvbuild/uv-python-lambda.lock
9-
export UV_LINK_MODE=copy
9+
export UV_LINK_MODE=hardlink
1010
export UV_NO_INSTALLER_METADATA=1
11+
export UV_PYTHON_LAMBDA_NOFILE_LIMIT="${UV_PYTHON_LAMBDA_NOFILE_LIMIT:-1048576}"
12+
13+
ulimit -n "$UV_PYTHON_LAMBDA_NOFILE_LIMIT"
1114

1215
rm -f "$LOCK_FILE"
1316

0 commit comments

Comments
 (0)