I have adopted uv for a lot of Python development. I'm also a heavy user of direnv, which I like as a tool for setting up project-specific environments.
Much like Hynek describes, I've found uv sync to be fast enough to put into the chdir path for new directories. Here's how I'm doing it.
Direnv Libraries
First, it turns out you can pretty easily define custom direnv functions like the built-in ones (layout python, etc...). You do this by adding functions to ~/.config/direnv/direnvrc or in ~/.config/direnv/lib/ as shell scripts. I use this extensively to make my .envrc files easier to maintain and smaller. Now that I'm using uv here is my default for python:
function use_standard-python() {
source_up_if_exists
dotenv_if_exists
source_env_if_exists .envrc.local
use venv
uv sync
}
What does that even mean?
Let me explain each of these commands and why they are there:
-
source_up_if_exists-- this direnv stdlib function is here because I often group my projects into directories with common configuration. For example, when working on Chicon 8, I had a top level.envrcthat set up the AWS configuration to support deploying Wellington and the Chicon 8 website. This searches up til it finds a.envrcin a higher directory, and uses that.source_upis the noisier, less-adaptable sibling. -
dotenv_if_exists-- this loads.envfrom the current working directory. 12-factor apps often have environment-driven configuration, anddocker composeuses them relatively seamlessly as well. Doing this makes it easier to run commands from my shell that behave like my development environment. -
source_env_if_exists .envrc.local-- sometimes you need more complex functionality in a project than just environment variables. Having this here lets me use.envrc.localfor that. This comes after.envbecause sometimes you want to change those values. -
use venv-- this is a function that activates the project.venv(creating it if needed); I'm old and set in my ways, and I prefer. .venv/bin/activate.fishin my shell to the more newfangled "prefix it with a runner" mode. -
uv sync-- this is a super fast, "install my development and main dependencies" command. This was way, way too slow withpip,pip-tools,poetry,pdm, orhatch, but withuv, I don't mind having this in my.envrc
A sidebar on use venv
use venv is a custom direnv function I have as well, and mine is a bit complicated because it evolved as I was choosing a way to set up virtualenvs. At the present, though, it can be written very simply:
function use_venv() {
uv venv
source .venv/bin/activate
}
Using it in a sentence
With this set up in direnv's configuration, all I need in my .envrc file is this:
use standard-python
I've been using this pattern for a while now; it lets me upgrade how I do default Python setups, with project specific settings, easily.
You can reply to this post via Mastodon:
Waiting to load comments