Skip to content

Commit 0a3db1b

Browse files
authored
Dynamic Python version support with UV (#2)
1 parent 4252358 commit 0a3db1b

7 files changed

Lines changed: 99 additions & 90 deletions

File tree

.diploi/helm/app.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,19 @@ spec:
4141
command: ["/bin/sh", "-c"]
4242
args:
4343
- |
44+
set -e
45+
uv python install {{ ((.Values.envMap.PYTHON_VERSION).value | default "3.12") | quote }}
4446
uv venv .venv --clear
47+
if [ -f pyproject.toml ]; then
48+
uv sync
49+
elif [ -f requirements.txt ]; then
50+
uv pip install -r requirements.txt
51+
fi
52+
env:
53+
- name: UV_PYTHON
54+
value: {{ ((.Values.envMap.PYTHON_VERSION).value | default "3.12") | quote }}
55+
- name: UV_PYTHON_INSTALL_DIR
56+
value: /app{{ .Values.folder }}/.python
4557
workingDir: /app{{ .Values.folder }}
4658
volumeMounts:
4759
- name: app-mount
@@ -71,6 +83,10 @@ spec:
7183
{{- end }}
7284
{{- end }}
7385
env:
86+
- name: UV_PYTHON
87+
value: {{ ((.Values.envMap.PYTHON_VERSION).value | default "3.12") | quote }}
88+
- name: UV_PYTHON_INSTALL_DIR
89+
value: /app{{ .Values.folder }}/.python
7490
{{- range .Values.env }}
7591
- name: {{ .identifier }}
7692
value: {{ .value | quote }}

.python-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

Dockerfile

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,30 @@
1-
# Use a Python image with uv pre-installed
2-
FROM ghcr.io/astral-sh/uv:python3.11-bookworm
1+
FROM ghcr.io/astral-sh/uv:bookworm-slim
32

43
# This will be set by the GitHub action to the folder containing this component.
54
ARG FOLDER=/app
65

6+
# This will be set by the GitHub action if "PYTHON_VERSION" ENV is set in diploi.yaml
7+
ARG PYTHON_VERSION=3.12
8+
9+
RUN mkdir -p /.cache/uv && chown -R 1000:1000 /.cache
10+
RUN mkdir -p /.local/share/uv/python && chown -R 1000:1000 /.local/share/uv
11+
12+
COPY --chown=1000:1000 . /app
713
WORKDIR ${FOLDER}
814

9-
# Enable bytecode compilation
1015
ENV UV_COMPILE_BYTECODE=1
11-
12-
# Copy from the cache instead of linking since it's a mounted volume
1316
ENV UV_LINK_MODE=copy
14-
15-
# Ensure installed tools can be executed out of the box
16-
ENV UV_TOOL_BIN_DIR=/usr/local/bin
17-
18-
COPY . /app
19-
20-
RUN if [ -f requirements.txt ]; then \
21-
echo "requirements.txt found, installing dependencies with uv pip" && \
22-
uv venv .venv --clear && \
23-
uv pip install -r requirements.txt; \
24-
else \
25-
echo "Using uv sync for dependency installation" && \
26-
uv sync --locked --no-dev; \
27-
fi
28-
29-
RUN uv pip show --python .venv/bin/python uvicorn >/dev/null 2>&1 || \
30-
uv pip install --python .venv/bin/python uvicorn --link-mode=copy
31-
32-
# Place executables in the environment at the front of the path
3317
ENV PATH="$FOLDER/.venv/bin:$PATH"
18+
ENV UV_PYTHON=${PYTHON_VERSION}
3419

35-
# Reset the entrypoint, don't invoke `uv`
36-
ENTRYPOINT []
20+
USER 1000:1000
3721

38-
EXPOSE 8000
39-
ENV PORT=8000
40-
ENV HOST="0.0.0.0"
22+
RUN uv python install ${PYTHON_VERSION} && \
23+
uv venv .venv && \
24+
if [ -f pyproject.toml ]; then \
25+
uv sync --locked --no-dev || uv sync --no-dev; \
26+
elif [ -f requirements.txt ]; then \
27+
uv pip install -r requirements.txt; \
28+
fi
4129

42-
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]
30+
CMD ["uv", "run", "--with", "uvicorn", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]

Dockerfile.dev

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,20 @@
1-
# Use a Python image with uv pre-installed
2-
FROM ghcr.io/astral-sh/uv:python3.11-bookworm
1+
FROM ghcr.io/astral-sh/uv:bookworm-slim
32

43
# This will be set by the GitHub action to the folder containing this component.
54
ARG FOLDER=/app
65

7-
COPY --chown=1000:1000 . /app
8-
WORKDIR ${FOLDER}
6+
RUN mkdir /app && chown 1000:1000 /app
97

10-
# Enable bytecode compilation
11-
ENV UV_COMPILE_BYTECODE=1
8+
RUN mkdir -p /.cache/uv && chown -R 1000:1000 /.cache
9+
RUN mkdir -p /.local/share/uv/python && chown -R 1000:1000 /.local/share/uv
10+
RUN mkdir -p /.local/bin && chown -R 1000:1000 /.local/bin
1211

13-
# Ensure installed tools can be executed out of the box
14-
ENV UV_TOOL_BIN_DIR=/usr/local/bin
12+
WORKDIR /app
1513

16-
# Fix uv + non-root environment
17-
ENV HOME=/tmp
18-
ENV XDG_DATA_HOME=/tmp/.local/share
19-
ENV XDG_CACHE_HOME=/tmp/.cache
20-
21-
USER root
22-
RUN apt-get update && apt-get install -y nodejs npm \
23-
&& npm install -g nodemon \
24-
&& rm -rf /var/lib/apt/lists/*
25-
26-
# Allow uv to use cache
27-
RUN mkdir -p /.cache/uv && chown 1000:1000 /.cache /.cache/uv
14+
ENV UV_LINK_MODE=copy
15+
ENV UV_PYTHON_INSTALL_DIR=$FOLDER/.python
16+
ENV PATH="$FOLDER/.venv/bin:$PATH"
2817

2918
USER 1000:1000
3019

31-
# Reset the entrypoint, don't invoke `uv`
32-
ENTRYPOINT []
33-
34-
EXPOSE 8000
35-
ENV PORT=8000
36-
ENV HOST="0.0.0.0"
37-
38-
CMD ["/usr/local/bin/nodemon", \
39-
"--delay", "1", \
40-
"--watch", "pyproject.toml", \
41-
"--watch", "requirements.txt", \
42-
"--watch", ".venv/lib/*", \
43-
"--watch", ".venv/lib64/*", \
44-
"--exec", "sh -c 'if [ -f pyproject.toml ]; then uv run --isolated --with . --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir src --reload-exclude \".venv/**\"; elif [ -f requirements.txt ]; then uv run --isolated --with-requirements requirements.txt --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir src --reload-exclude \".venv/**\"; else uv run --isolated --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir src --reload-exclude \".venv/**\"; fi'", \
45-
"--"]
20+
CMD ["uv", "run", "--with", "uvicorn", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--reload-dir", "src"]

README.md

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,27 @@ Link to the full guide https://diploi.com/blog/hosting_fastapi_apps
2020

2121
### Development
2222

23-
During development, the container installs Node.js and `nodemon` to enable automatic reloads when files change. The development server is started with:
23+
The development server is started with:
2424

2525
```sh
26-
nodemon --delay 1 --watch pyproject.toml --watch requirements.txt --watch ".venv/lib/*" --watch ".venv/lib64/*" --exec "sh -c 'if [ -f pyproject.toml ]; then uv run --isolated --with . --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir src --reload-exclude \".venv/**\"; elif [ -f requirements.txt ]; then uv run --isolated --with-requirements requirements.txt --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir src --reload-exclude \".venv/**\"; else uv run --isolated --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir src --reload-exclude \".venv/**\"; fi'"
26+
uv run --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir src --reload-dir .venv/lib
2727
```
2828

29-
This will:
30-
- Use nodemon to watch for changes to `pyproject.toml`, `requirements.txt`, and the virtual environment, restarting the server when changes are detected.
31-
- Detect the dependency style and run in an isolated Python environment: with `pyproject.toml` using `uv run --with .`, with `requirements.txt` using `uv run --with-requirements`, or with no config file using uvicorn only.
32-
- Start the FastAPI app using uvicorn on all network interfaces at port 8000.
33-
- Enable hot-reload via uvicorn `--reload`, so the server automatically restarts when Python source files change.
29+
This can be changed with the `containerCommands.developmentStart` field in `diploi.yaml`.
3430

3531
### Production
3632

37-
Builds a production-ready image. During the build, dependencies are installed with `uv sync`. When the container starts, it runs:
33+
Builds a production-ready image. During the build, dependencies are installed with `uv sync` or `uv pip install`. When the container starts, it runs:
3834

3935
```sh
40-
uvicorn src.main:app --host 0.0.0.0 --port 8000 --proxy-headers
36+
uv run --with uvicorn uvicorn src.main:app --host 0.0.0.0 --port 8000 --proxy-headers
4137
```
4238

43-
This uses uvicorn to serve your application on port 8000.
39+
This can be changed with the `containerCommands.productionStart` field in `diploi.yaml`.
4440

4541
## Links
4642

4743
- [Adding FastAPI to a project](https://docs.diploi.com/building/components/fastapi)
4844
- [FastAPI documentation](https://fastapi.tiangolo.com/)
4945
- [Python documentation](https://docs.python.org/)
5046
- [uv documentation](https://docs.astral.sh/uv/)
51-

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name = "hello-world"
33
version = "0.1.0"
44
description = "Add your description here"
55
readme = "README.md"
6-
requires-python = ">=3.11"
6+
requires-python = ">=3.10"
77
dependencies = [
8-
"fastapi>=0.135.1",
9-
"uvicorn>=0.40.0",
10-
]
8+
"fastapi>=0.135.2",
9+
"uvicorn>=0.42.0",
10+
]

0 commit comments

Comments
 (0)