add uv as a parallel option alongside pip
uv is presented as the recommended faster, standards-compliant drop-in; pip stays the baseline/fallback. uv command syntax verified against docs.astral.sh/uv before writing. environments.md: - new 'uv (optional, faster)' section: install from pyproject (uv pip install . / -e . / '.[dev]'), the uv sync managed-venv flow, and a note that uv.lock is local-only/gitignored (not committed) - pinning subsection: pin direct deps in pyproject via == or git @ref, with a warning that this pins direct deps only — transitive deps still float at build time (documented tradeoff) - uv equivalents added beside pip in the Local .venv and Docker tabs deploy.md: - 'Faster builds with uv' tip: uv-from-ghcr COPY, ENV UV_COMPILE_BYTECODE=1, uv pip install --system . (reads pyproject, no lock) - layer-caching shows the uv variant beside the pip one - checklist notes uv pip install + UV_COMPILE_BYTECODE; fix stray 'configs' plural -> 'config' .gitignore: ignore uv.lock (local-only, never committed). Verified in-browser; mkdocs build --strict clean (anchors resolve). Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
parent
dafc1dcacd
commit
c53d67da2f
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ __pycache__/
|
|||||||
*.py[cod]
|
*.py[cod]
|
||||||
.venv/
|
.venv/
|
||||||
venv/
|
venv/
|
||||||
|
uv.lock
|
||||||
|
|
||||||
# Playwright MCP run artifacts
|
# Playwright MCP run artifacts
|
||||||
.playwright-mcp/
|
.playwright-mcp/
|
||||||
|
|||||||
@ -40,6 +40,32 @@ CMD ["python", "-m", "yourapp"]
|
|||||||
4. `chmod -R a+rwX /app` makes the app tree writable by **any** uid — that's what
|
4. `chmod -R a+rwX /app` makes the app tree writable by **any** uid — that's what
|
||||||
"uid-agnostic" means.
|
"uid-agnostic" means.
|
||||||
|
|
||||||
|
!!! tip "Faster builds with uv (optional)"
|
||||||
|
[uv](https://docs.astral.sh/uv/) is a drop-in for pip that reads the same
|
||||||
|
`pyproject.toml` — no lockfile needed in the image. Swap the deps layer and add
|
||||||
|
`UV_COMPILE_BYTECODE` so containers don't pay the first-import `.pyc` compile
|
||||||
|
cost:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM python:3.12-slim
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # (1)!
|
||||||
|
ENV HOME=/tmp
|
||||||
|
ENV UV_COMPILE_BYTECODE=1 # (2)!
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY pyproject.toml .
|
||||||
|
RUN uv pip install --system . # (3)!
|
||||||
|
COPY . .
|
||||||
|
RUN chmod -R a+rwX /app
|
||||||
|
CMD ["python", "-m", "yourapp"]
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Pull the `uv` binary from its published image — no pip-installing uv itself.
|
||||||
|
2. Compile bytecode at build time so the container doesn't eat the first-import
|
||||||
|
`.pyc` compile cost on every cold start.
|
||||||
|
3. `--system` installs into the image's Python (no venv needed — the container
|
||||||
|
*is* the isolation); reads `pyproject.toml`, no `uv.lock` required.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
yourapp:
|
yourapp:
|
||||||
@ -92,17 +118,23 @@ All `/srv/...` paths are owned by the `services` user (uid/gid **1337**).
|
|||||||
|
|
||||||
## Layer caching
|
## Layer caching
|
||||||
|
|
||||||
Copy `requirements.txt` and `pip install` **before** `COPY . .`. Docker caches
|
Copy the deps file and install **before** `COPY . .`. Docker caches layers in
|
||||||
layers in order, so deps only reinstall when `requirements.txt` changes — not on
|
order, so deps only reinstall when the deps file changes — not on every code edit.
|
||||||
every code edit. Get this backwards and every one-line change triggers a full
|
Get this backwards and every one-line change triggers a full dependency reinstall.
|
||||||
dependency reinstall.
|
Same principle whether you use pip or uv:
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
COPY requirements.txt .
|
COPY requirements.txt . # pip baseline
|
||||||
RUN pip install --no-cache-dir -r requirements.txt # cached until deps change
|
RUN pip install --no-cache-dir -r requirements.txt # cached until deps change
|
||||||
COPY . . # changes every build
|
COPY . . # changes every build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
COPY pyproject.toml . # uv path
|
||||||
|
RUN uv pip install --system . # cached until deps change
|
||||||
|
COPY . . # changes every build
|
||||||
|
```
|
||||||
|
|
||||||
## Subprocess and browser workloads
|
## Subprocess and browser workloads
|
||||||
|
|
||||||
Bots that spawn Chrome, Xvfb, ffmpeg, or other child processes need three extra
|
Bots that spawn Chrome, Xvfb, ffmpeg, or other child processes need three extra
|
||||||
@ -133,14 +165,15 @@ services:
|
|||||||
## What your compose / Dockerfile needs
|
## What your compose / Dockerfile needs
|
||||||
|
|
||||||
- `user: "1337:1337"`
|
- `user: "1337:1337"`
|
||||||
- bind mounts for **configs + logs**
|
- bind mounts for **config + logs**
|
||||||
- named volumes for **the rest**
|
- named volumes for **the rest**
|
||||||
- secrets bind-mounted **`:ro`**
|
- secrets bind-mounted **`:ro`**
|
||||||
- `HOME=/tmp`
|
- `HOME=/tmp`
|
||||||
- `chmod -R a+rwX /app`
|
- `chmod -R a+rwX /app`
|
||||||
- deps installed **before** the code copy (layer caching)
|
- deps installed **before** the code copy (layer caching) — pip or `uv pip install`
|
||||||
- `git` in the image **if the container needs it**
|
- `git` in the image **if the container needs it**
|
||||||
- for browser/subprocess workloads: `init: true`, `shm_size`, `mem_limit`
|
- for browser/subprocess workloads: `init: true`, `shm_size`, `mem_limit`
|
||||||
|
- using uv? add `ENV UV_COMPILE_BYTECODE=1`
|
||||||
|
|
||||||
## Secrets
|
## Secrets
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,14 @@ happens, depending on where the project runs:
|
|||||||
```bash
|
```bash
|
||||||
python -m venv .venv # create it (once)
|
python -m venv .venv # create it (once)
|
||||||
source .venv/bin/activate # activate for this shell
|
source .venv/bin/activate # activate for this shell
|
||||||
pip install -r requirements.txt
|
pip install -e . # install the project from pyproject.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with [uv](#uv-optional-faster) — same `pyproject.toml`, much faster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv venv # create .venv
|
||||||
|
uv pip install -e . # install from pyproject.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
Keep `.venv/` **gitignored** — it's per-machine, never committed.
|
Keep `.venv/` **gitignored** — it's per-machine, never committed.
|
||||||
@ -55,13 +62,14 @@ happens, depending on where the project runs:
|
|||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
FROM python:3.12-slim
|
FROM python:3.12-slim
|
||||||
COPY requirements.txt .
|
COPY pyproject.toml .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir . # or: uv pip install .
|
||||||
COPY . .
|
COPY . .
|
||||||
```
|
```
|
||||||
|
|
||||||
This is how things run in production — see the [Deploy guide](deploy.md) for the
|
This is how things run in production — see the [Deploy guide](deploy.md) for the
|
||||||
full container standard (uid 1337, mounts, layer caching).
|
full container standard (uid 1337, mounts, layer caching, and the uv image
|
||||||
|
setup).
|
||||||
|
|
||||||
!!! tip "Which one?"
|
!!! tip "Which one?"
|
||||||
**Local `.venv`** for quick iteration, **Makefile** when you want repeatable
|
**Local `.venv`** for quick iteration, **Makefile** when you want repeatable
|
||||||
@ -69,6 +77,51 @@ happens, depending on where the project runs:
|
|||||||
exclusive — a project often has a `.venv` for local dev *and* a Dockerfile for
|
exclusive — a project often has a `.venv` for local dev *and* a Dockerfile for
|
||||||
deploy.
|
deploy.
|
||||||
|
|
||||||
|
## uv (optional, faster)
|
||||||
|
|
||||||
|
!!! tip "uv is the recommended fast path; pip stays the baseline"
|
||||||
|
[uv](https://docs.astral.sh/uv/) is a faster, standards-compliant drop-in for
|
||||||
|
pip. It reads the **same `pyproject.toml`** — no workflow change required, and
|
||||||
|
`pip` keeps working exactly as before. Use it wherever you'd reach for pip; the
|
||||||
|
rest of this handbook shows the pip command with the uv equivalent beside it.
|
||||||
|
|
||||||
|
Install deps straight from `pyproject.toml` (no `requirements.txt` needed):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv pip install . # install the project + its deps
|
||||||
|
uv pip install -e . # editable (dev) install
|
||||||
|
uv pip install '.[dev]' # with an extras group, e.g. dev
|
||||||
|
```
|
||||||
|
|
||||||
|
If you'd rather have uv manage the venv for you, use the managed-venv flow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv sync # create/refresh .venv from pyproject.toml + uv.lock
|
||||||
|
uv run python -m yourapp # run inside the managed env, no manual activate
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note "`uv.lock` is local-only — never committed"
|
||||||
|
`uv sync` writes a `uv.lock` for your machine's resolved environment. It is
|
||||||
|
**gitignored**, not committed — we don't ship a lockfile. Pinning happens in
|
||||||
|
`pyproject.toml` (below), not the lock.
|
||||||
|
|
||||||
|
### Pinning deps
|
||||||
|
|
||||||
|
Pin **direct** dependencies in `pyproject.toml` with `==` or a git `@ref`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[project]
|
||||||
|
dependencies = [
|
||||||
|
"requests==2.31.0",
|
||||||
|
"mylib @ git+https://git.rethinkstudios.io/rethink-public/mylib.git@<sha>",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "This pins direct deps only — transitive deps still float"
|
||||||
|
`==` / `@ref` pins the packages **you** list. Their dependencies still resolve
|
||||||
|
fresh at build time. That's an accepted tradeoff — documented on purpose — not
|
||||||
|
an oversight: we pin what we depend on directly and let the rest float.
|
||||||
|
|
||||||
## Local dev with pyenv
|
## Local dev with pyenv
|
||||||
|
|
||||||
For local work you also need the right **Python version**, not just isolated deps.
|
For local work you also need the right **Python version**, not just isolated deps.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user