-
UV
๐ฅ The best "Animated This is Fine ZOOM Background" using UV and YT-DLP
We could all use an “Animated This is Fine ZOOM Background” video in times like these.
It’s not obvious how to download a video from YouTube.
I tend to shy away from this outside of this background video explicitly created to be downloaded, so I came up with this one-liner using UV and yt-dlp, which will pull the video.
$ uv run yt-dlp --format=mp4 https://www.youtube.com/watch?v=oEg-9RvcnlY
Hat tip to Maryanne Wachter for finding and recommending this background.
-
Python
,UV
,Today I Learned
๐คท UV does everything or enough that I'm not sure what else it needs to do
UV feels like one of those old infomercials where it solves everything, which is where we have landed in the Python world.
I have had several discussions with friends about UV, and even when we talk about it during my weekly(ish) office hours, the list has grown to an ever-growing number of options.
UV started as a quicker way of installing Python packages, and now it’s easier to tell people that UV does everything and to focus on what it doesn’t do.
My favorite feature is that UV can now bootstrap a project to run on a machine that does not previously have Python installed, along with installing any packages your application might require.
Here is my incomplete list of what UV does today:
uv pip install
replaces pip installuv venv
replacespython -m venv
uv pip compile
replaces pip-tools compileuv pip sync
replaces pip-tools syncuv run
replaces pipxuv tool run
replaces pipxuv python
replaces pyenv, asdf, mise, and several other like-minded toolsuv build
- Build your Python package for pypiuv publish
- Upload your Python package to pypiastral-sh/setup-uv
brings UV to GitHub Actionsghcr.io/astral-sh/uv:latest
brings UV and Python to Docker
I copied these four from
uv --help
, which feels like poetry features.uv add
- Add dependencies to the projectuv remove
- Remove dependencies from the projectuv sync
- Update the project’s environmentuv lock
- Update the project’s lockfile
So what doesn’t UV do?
UV does a lot, but it still needs to do everything.
- UV doesn’t run custom scripts defined in our
pyproject.toml
likenpm-run-script
allows. Thank you to @command_tab for jogging my memory. - UV doesn’t convert my non-UV-based projects to UV. Converting is more about prefixing and replacing my commands to switch over.
- UV doesn’t manage, and bump version numbers like the BumpVer, and others do.
- UV doesn’t manage pre-commit like hooks. This is a long shot, but I’d love to see support via
pyproject.toml
. - UV doesn’t replace Python, nor should it.
-
Python
,Docker
,UV
,Today I Learned
๐ My notes on publishing a Python package with UV and building a custom GitHub Action for files-to-claude-xml
My new Python application files-to-claude-xml is now on PyPI, which means they are packaged and pip installable. My preferred way of running
files-to-claude-xml
is via UV’s tool run, which will install it if it still needs to be installed and then execute it.$ uv tool run files-to-claude-xml --version
Publishing on PyPi with UV
UV has both build and publish commands, so I took them for a spin today.
uv build
just worked, and a Python package was built.When I tried
uv publish
, it prompted me for some auth settings for which I had to log in to PyPI to create a token.I added those to my local ENV variables I manage with direnv.
export UV_PUBLISH_PASSWORD=<your-PyPI-token-here> export UV_PUBLISH_USERNAME=__token__
Once both were set and registered,
uv publish
published my files on PyPI.GitHub Action
To make
files-to-claude-xml
easier to run on GitHub, I created a custom action to build a_claude.xml
from the GitHub repository.To use this action, I wrote this example workflow, which runs from files-to-claude-xml-example
name: Convert Files to Claude XML on: push jobs: convert-to-xml: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Convert files to Claude XML uses: jefftriplett/files-to-claude-xml-action@main with: files: | README.md main.py output: '_claude.xml' verbose: 'true' - name: Upload XML artifact uses: actions/upload-artifact@v4 with: name: claude-xml path: _claude.xml
My GitHub action is built with a
Dockerfile
, which installsfiles-to-claude-xml
.# Dockerfile FROM ghcr.io/astral-sh/uv:bookworm-slim ENV UV_LINK_MODE=copy RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-install-project WORKDIR /app ENTRYPOINT ["uvx", "files-to-claude-xml"]
To turn a GitHub repository into a runnable GitHub Action, an
action.yml
file needs to exist in the repository. This file describes the input arguments and whichDockerfile
or command to run.# action.yml name: 'Files to Claude XML' description: 'Convert files to XML format for Claude' inputs: files: description: 'Input files to process' required: true type: list output: description: 'Output XML file path' required: false default: '_claude.xml' verbose: description: 'Enable verbose output' required: false default: 'false' version: description: 'Display the version number' required: false default: 'false' runs: using: 'docker' image: 'Dockerfile' args: - ${{ join(inputs.files, ' ') }} - --output - ${{ inputs.output }} - ${{ inputs.verbose == 'true' && '--verbose' || '' }} - ${{ inputs.version == 'true' && '--version' || '' }}
Overall, this works. Claude’s prompting helped me figure it out, which felt fairly satisfying given the goal of
files-to-claude-xml
. -
Django
,Python
,UV
โ๏ธ UV with GitHub Actions to run an RSS to README project
For my personal GitHub profile, I list my activities, affiliations, and the latest updates from some of my projects.
Historically, I have used JasonEtco/rss-to-readme GitHub Action to fetch a few RSS feeds or two and to update my README a few times a day.
Overall, I’m happy with this setup. I used it on the Django News GitHub Organization to pull in newsletter issues, jobs, and the latest videos from our various projects. When I tried to install rss-to-readme in our repo, I was getting node12 errors. (Have I mentioned how much I loathe node/npm?).
Instead of forking rss-to-readme and trying to figure out how to upgrade it, I used this as an excuse to “pair program” with Claude. We quickly built out a prototype using Python and the feedparser library.
I would share the chat log, but it’s mostly me trying out a few different ways to invoke it before I settle on the finished approach. See the source code over on GitHub if you are curious: https://github.com/django-news/.github/blob/main/fetch-rss.py
Once I had a working Python script that could fetch an RSS file and modify the README, I decided to run/deploy it using UV to see how minimal I could build out the GitHub Action.
GitHub Action
To run our
fetch-rss.py
script, we have four steps:actions/checkout
Get a git checkout of our project.astral-sh/setup-uv
Setup UV also installs Pythons for us. As a bonus, we enabled UV’s cache support, which will run much faster in the future unless we change something in our fetch-rss.py file.- Run
uv run fetch-rss.py ...
to fetch our RSS feeds and write them to disk.uv run
installs any dependencies and caches them before ourfetch-rss.py
runs. stefanzweifel/git-auto-commit-action
If our README.md file has changed, save our changes and commit them back to git and into our README.
Our
schedule.yml
GitHub Action workflow runs twice daily or whenever we push a new change to our repo. We also setworkflow_dispatch,
which gives us a button to run the script manually.# .github/workflows/schedule.yml name: Update README on: push: branches: - main schedule: # Once a day at 12 AM - cron: 0 12 * * * workflow_dispatch: jobs: update: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v3 with: enable-cache: true cache-dependency-glob: | *.py - name: Fetch our Feeds run: | # Fetch latest Django News Newsletter entries uv run fetch-rss.py \ --section=news \ --readme-path=profile/README.md \ https://django-news.com/issues.rss - uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: ":pencil: Updates README"
Results
Overall, I’m pleased with this solution. If I wanted to spend more time on it or re-use this workflow, I might turn it into a GitHub Action workflow so that we can call:
django-news/rss-to-readme
to use in other projects. For now, this is fine.I’m happy with the
astral-sh/setup-uv
anduv run
steps because they save me from having to set up Python and then install our project dependencies as separate steps.I normally shy away from running Python workflows like this in GitHub Actions because they involve a lot of slow steps. This entire workflow takes 16 to 20 seconds to run, which feels fast to me.
-
Django
,Python
,UV
,Today I Learned
๐ค UV Roundup: Five good articles and a pre-commit tip
I have written quite a bit about UV on my micro blog, and I am happy to see more and more people adopt it. I have stumbled on so many good articles recently that I wanted to share them because every article points out something new or different about why UV works well for them.
If you are new to UV, it’s a new tool written by Astral, the creators of Ruff.
I like UV because it replaces, combines, or complements a bunch of Python tools into one tool and user developer experience without forcing a UV way of doing it. UV effectively solves the question, “Why do I need another Python tool?” to do everyday Python tasks.
Some reason I like UV after using it for months:
- It’s a faster pip and is really, really fast
- It can install and manage Python versions
- It can run and install Python scripts
- It can run single-file Python scripts along with their dependencies
- It can handle project lock files
While some people don’t care about UV being fast, it’s shaved minutes off my CI builds and container rebuilds, which means it has also saved me money and energy resources.
Overall thoughts on UV
Oliver Andrich’s UV โ I am (somewhat) sold takes the approach of only using UV to set up a new Python environment. Oliver uses UV to install Python, aliases to call Python, and UV tool install to set up a few global utilities.
Using UV with Django
Anลพe Peฤar’s UV with Django shows how to use UV to set up a new project with Django.
Switching from pyenv to UV
Will Guaraldi Kahn-Greene’s Switching from pyenv to uv was relatable for me because I also use pyenv, but I plan to slowly migrate to using only UV. I’m already halfway there, but I will have pyenv for my legacy projects for years because many aren’t worth porting yet.
Using UV and managing with Ansible
Adam Johnson’s Python: my new uv setup for development taught me to use
uv cache prune
to clean up unused cache entries and shows how he manages his UV setup using Ansible.Some notes on UV
Simon Willison’s Notes on UV is an excellent summary of Oliver’s notes.
A parting UV tip
If you are a pre-commit fan hoping for a version that supports UV, the
pre-commit-uv
project does just that. I started updating my justfile recipes to bakejust lint
to the followinguv run
command, which speeds up running and installing pre-commit significantly.$ uv run --with pre-commit-uv pre-commit run --all-files pre-commit-uv
If you are attending DjangoCon US…
If you are attending DjangoCon US and want to talk UV, Django, Django News, Django Packages, hit me up while you are there.
I’ll be attending, volunteering, organizing, sponsoring, and sprinting around the venue in Durham, NC, for the next week starting this Friday.
We still have online and in-person tickets, but not much longer!
-
Django
,Python
,UV
๐ Using Claude 3.5 Sonnet to refactor one of Brian Okken's Python projects
Brian Okken posted and published his Top pytest Plugins script and then a follow-up post, Finding the top pytest plugins, which was pretty cool.
I have written a few throw-away scripts, which William Vincent wrote about and updated a few times in the Top 10 Django Third-Party Packages (2024) and The 10 Most-Used Django Packages (2024).
These efforts are powered by Hugo van Kemenade’s excellent Top PyPI Packages.
This inspired me to fork Brian’s top-pytest-plugins project, which I updated to support passing in other package names like “django” to get a rough estimate of monthly package downloads.
The refactored project is jefftriplett/top-python-packages.
Please note: Looking at the package name doesn’t scale as well for projects that have their own Trove classifiers. For a project like pytest, it works well. Many of the top packages may not even have Django in their name for a project like Django. Some projects may even actively discourage a project from using their project in their package’s name for trademark reasons. So, YMMV applies here.
Prompts
I added
uv run
support, which I have written about a lot lately.I also copied the
top_pytest.py
file into a Claude 3.5 Sonnet session, and I let it handle the whole refactor. It even handled adding the PEP 723 new package dependencies without me asking it to.In case it’s useful to anyone, here are my prompts:
## Prompt: Please update this script to use a rich table. ## Prompt: Please update the table styles to be ascii so I can copy and paste it into a markdown doc ## Prompt: Please remove the description column ## Prompt: Please change all PyTest and pytest references to Django and django ## Prompt: Please add back `if 'django' in project.lower() and 'django' != project.lower():` ## Prompt: please remove the \*# Export to markdown section. I can just pipe the output \* ## Prompt: Please add the typer library. ## Prompt: Please remove days and limit ## Prompt: Please refactor the script to allow me to pass the package name instead of django. You can default to django though. This way I can pass pytest or flask or other projects. ## Prompt: Please change the default Table box type to MARKDOWN
Outro
I don’t usually write about Claude or prompts, but the tool has been handy lately.
If you have had some similar successes, let me know. I have been exploring some rabbit holes, and it’s changing the way I approach solving problems.
-
Django
,Python
,UV
,Today I Learned
๐ UV Run Django Notes
I wanted to know how hard it would be to turn one of my django-startproject projects into a
uv run
friendly project. As it turns out, it worked, and the steps were more than reasonable.Before the PEP 723’ing…
I started with a fairly vanilla
manage.py
that Django will give you after runningpython -m manage startproject
."""Django's command-line utility for administrative tasks.""" import os import sys def main(): """Run administrative tasks.""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == "__main__": main()
shebang
Then we add
#!/usr/bin/env -S uv run
to the top of ourmanage.py
file.Next, we make our
manage.py
executable and try to run it.$ chmod +x manage.py $ ./manage.py ModuleNotFoundError: No module named 'django'
Our script ran, but Python couldn’t find Django. To tell our script to install Django, we can use
uv addโ- script
to add it.$ uv add --script manage.py django Updated `manage.py` $ ./manage.py ... Type 'manage.py help <subcommand>' for help on a specific subcommand. Available subcommands: [django] check compilemessages createcachetable dbshell diffsettings dumpdata flush inspectdb loaddata makemessages makemigrations migrate optimizemigration runserver sendtestemail shell showmigrations sqlflush sqlmigrate sqlsequencereset squashmigrations startapp startproject test testserver Note that only Django core commands are listed as settings are not properly configured (error: No module named 'environs').
Django worked as expected this time, but Python could not find a few third-party libraries I like to include in my projects.
To add these, I passed the other four to
uv add --script
which will add them to the project.$ uv add --script manage.py django-click "environs[django]" psycopg2-binary whitenoise Updated `manage.py` ... $ ./manage.py ...
Our Django app’s
manage.py
works when we run it.After the PEP 723’ing…
After we installed our dependencies in our
manage.py
file, they were added to the top of the file between the///
blocks.#!/usr/bin/env -S uv run # /// script # requires-python = ">=3.10" # dependencies = [ # "django", # "django-click", # "environs[django]", # "psycopg2-binary", # "whitenoise", # ] # /// """Django's command-line utility for administrative tasks.""" import os import sys def main(): """Run administrative tasks.""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == "__main__": main()
-
Python
,UV
๐ Python UV run with shebangs
This UV shebang trick that Simon Willison linked up is a nice pattern, and I plan to rebuild some of my one-off scripts in my dotfiles using it.
Here is a demo that will print “hello python” using the Python Branding colors using the Rich library while letting UV install and manage rich for you.
#!/usr/bin/env -S uv run # /// script # requires-python = ">=3.10" # dependencies = [ # "rich", # ] # /// from rich.console import Console from rich.theme import Theme python_theme = Theme( { "pyyellow": "#ffde57", "pyblue": "#4584b6", } ) console = Console(theme=python_theme) console.print("[pyyellow]hello[/pyyellow] [pyblue]python[/pyblue]", style="on #646464")
Assuming you have UV installed, and you save and
chmod +x
this file ashello-python.py
, then you should be able to run it via./hello-python.py.
I suspect I can more easily bootstrap new machines using this trick without fewer worries about polluting my global system packages.
-
Python
,UV
๐ UV Updates and PEP 723: Simplifying Python Packaging and Scripting
The uv: Unified Python packaging update brings fresh air to the Python community, with several improvements streamlining the development process. One exciting addition is an early preview of PEP 723, also known as Single-file scripts.
The Single-file scripts feature particularly caught my attention due to its potential to simplify the distribution and execution of small Python projects. Streamlining the process is highly appealing to someone who frequently creates GitHub Gists and shares them privately and publicly.
With this new feature, I can now instruct users to run
uv run main.py
without explaining what avenv
orvirtualenv
is, plus a long list of requirements that need to be passed topip install
.I had the opportunity to test this feature over lunch today. While adding libraries to the script was straightforward, I encountered a few hurdles when I forgot to invoke
uv run
in my virtual environment (venv). This makes sense, given that it’s a new habit, but it highlights the importance of adapting to changes in our development workflow.Overall, the UV: Unified Python packaging update and the introduction of Single-file scripts mark a significant step in simplifying Python development. As developers become more familiar with these improvements, we expect increased adoption and smoother collaboration on small-scale projects.
Bonus Example
I looked through some of my recent visits, and one I recently shared with a few conference organizer friends was a one-off script I used to read several YouTube video JSON files that I’m using to bootstrap another project. It was the first time I used DuckDB to make quick work of reading data from a bunch of JSON files using SQL.
Overall, I was happy with DuckDB and what PEP 723 might bring to the future of Python apps, even if my example only does a little.
# To run this application, use: # uv run demo-duckdb.py # # /// script # requires-python = ">=3.10" # dependencies = [ # "duckdb", # "rich", # "typer", # ] # /// import duckdb import typer from rich import print def main(): result = duckdb.sql("SELECT id,snippet FROM read_json('json/*.json')").fetchall() for row in result: id, snippet = row print("-" * 80) print(f"{id=}") print(f"{snippet['channelTitle']=}") print(f"{snippet['title']=}") print(f"{snippet['publishedAt']=}") print(snippet["description"]) print(snippet["thumbnails"].get("maxres") or snippet.get("standard")) print() if __name__ == "__main__": typer.run(main)
Overall, the future is bright with UV and PEP 723 may bring us. I’m excited to have more one-file Python apps that are easier to share and run with others.
PEP 723 also opens the door to turning a one-file Python script into a runnable Docker image that doesn’t even need Python on the machine or opens the door for Beeware and Briefcase to build standalone apps.
-
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 thevenv
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, anduv
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 avenv
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 myrequirements.txt
file to use the exact requirements locally and in production.justfile
beforeMy
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
afterFor the most part,
uv
shares most of the same syntax aspip
so you can start by changing yourpip
references touv 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
beforeFROM 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
afterFROM 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
afterThe 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 formatpackage-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. -
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
orpip-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
beforeMy
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
afterFor the most part,
uv
shares most of the same syntax aspip
so you can start by changing yourpip
references touv 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
beforeFROM 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
afterFROM 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
afterThe 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.