Django
,Python
,UV
Python's UV tool is even better
Last month, I wrote Python’s UV tool is actually pretty good about Astral’s new Python package installer and resolver uv
, and this is a follow-up post.
Since last month, I have added uv
to over a dozen projects, and I recently learned that you could skip the venv
step for projects that use containers or CI where the environment is already isolated.
I mistakenly thought uv
required a virtual environment (aka venv), but Josh Thomas recently pointed out that it’s unnecessary.
The trick is to pass the --system
option, and uv
will perform a system-wide install. Here’s an example:
uv pip install --system --requirement requirement.txt
Now that I have seen this, I wish pip
also used this approach to avoid developers accidentally installing third-party packages globally.
local development
Nothing has changed with my justfile
example from last month.
When I’m working with containers, I create a virtual environment (venv) because I will need most of my project requirements installed outside of the container so that my text editor and LSP can resolve dependencies. uv
’s default behavior of respecting a venv
is all we need here.
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 to use 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 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
COPY requirements.txt /src/requirements.txt
RUN uv pip install --system --requirement /src/requirements.txt # this is updated
WORKDIR /src/
GitHub Actions
GitHub Actions are a little more complicated to explain, but my workflows started 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
main.yml
after
The most significant pain point I ran into 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 --upgrade uv # this is new
python -m uv pip install --system --requirement requirements.in # this is updated
Gotchas
The only gotchas I have encountered with uv
is when I’m trying to install a Python package from a remote zip file.
Previously, I could copy and paste the GitHub repo URL, but uv
required we use the format package-name @ url-to-zip-file
requirements.in
before
# requirements.in
https://github.com/jefftriplett/django-feedreader/archive/main.zip
requirements.in
after
# requirements.in
django-feedreader @ https://github.com/jefftriplett/django-feedreader/archive/main.zip
Conclusion
This update helps remove a few steps from updating your projects, and it should shave a few minutes off of updating projects to use it.
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.
Thursday March 14, 2024