Python
,UV
Python's UV tool is actually pretty good
I carved out some time recently to start playing with the new Python package installer and resolver, uv
.
uv
makes big promises and claims to be 10-100x faster than pip and pip-tools. From my experiments over the last few weeks, it lives up to this promise.
I’m using it locally for my virtual environments, in my Dockerfiles to rebuild my containers, and for CI using GitHub Actions. Across the board, anything I do with pip
or pip-tools
is remarkably faster.
My average GitHub Actions CI workflows dropped from ~2 minutes to 50 seconds. This cuts the minutes I use in half and, in theory, my monthly bill in half.
My goal in sharing my configs is more “show” than “tell' because I will copy and paste these for weeks and months to come.
local development
Every one of my projects has a justfile
(it’s like Make but works the same everywhere) with “bootstrap” and “lock” recipes. My “bootstrap” recipe installs everything I need to work with the project locally. I use my “lock” recipe to lock my requirements.txt file so that I’m using the exact requirements locally and in production.
justfile
before
My justfile
might look like this:
@bootstrap
python -m pip install --upgrade pip
python -m pip install --upgrade --requirement requirements.in
@lock *ARGS:
python -m piptools compile {{ ARGS }} ./requirements.in \
--resolver=backtracking \
--output-file ./requirements.txt
justfile
after
For the most part, uv
shares most of the same syntax as pip
so you can start by changing your pip
references to uv pip
:
@bootstrap
python -m pip install --upgrade pip uv
python -m uv pip install --upgrade --requirement requirements.in
@lock *ARGS:
python -m uv pip compile {{ ARGS }} ./requirements.in \
--resolver=backtracking \
--output-file ./requirements.txt
Dockerfiles
Everyone’s container setup is going to be different, but I use Docker and Orbstack, which use a Dockerfile.
Dockerfile
before
FROM python:3.12-slim-bookworm
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONPATH /srv
ENV PYTHONUNBUFFERED 1
RUN apt-get update
RUN pip install --upgrade pip
COPY requirements.txt /src/requirements.txt
RUN pip install --requirement /src/requirements.txt
WORKDIR /src/
Dockerfile
after
FROM python:3.12-slim-bookworm
ENV PATH /venv/bin:$PATH. # this is new
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONPATH /srv
ENV PYTHONUNBUFFERED 1
RUN apt-get update
RUN pip install --upgrade pip uv # this is updated
RUN python -m uv venv /venv # this is new
COPY requirements.txt /src/requirements.txt
RUN uv pip install --requirement /src/requirements.txt # this is updated
WORKDIR /src/
GitHub Actions
GitHub Actions are a little harder to explain, but my workflows started off similar to this before I made the switch to uv
:
main.yml
before
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --requirement requirements.in
- name: Collect Static Assets
run: |
python -m manage collectstatic --noinput
main.yml
after
The biggest pain point that I ran into along the way was related to GitHub Issue #1386, which has a useable workaround.
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install uv
python -m uv venv .venv
echo "VIRTUAL_ENV=.venv" >> $GITHUB_ENV
echo "$PWD/.venv/bin" >> $GITHUB_PATH
python -m uv pip install --requirement requirements.in
- name: Collect Static Assets
run: |
. .venv/bin/activate
python -m manage collectstatic --noinput
Conclusion
I hope this was helpful to anyone who is considering making the switch to uv
. I love to hear about how much time it saves you.
Updates
2024-03-08 - I modified the ENV PATH statement to prepend instead of replacing the value.
Thursday February 29, 2024