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.envrc
that set up the AWS configuration to support deploying Wellington and the Chicon 8 website. This searches up til it finds a.envrc
in a higher directory, and uses that.source_up
is the noisier, less-adaptable sibling. -
dotenv_if_exists
-- this loads.env
from the current working directory. 12-factor apps often have environment-driven configuration, anddocker compose
uses 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.local
for that. This comes after.env
because 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.fish
in 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