Hace un tiempo os hablé sobre Poetry y cómo nos podía ayudar a mejorar nuestra experiencia desarrollando en Python. Hoy vengo a hablaros de uv, una herramienta que va mucho más allá que Poetry, ¡hasta el punto de poder programar en Python sin ni siquiera tener el lenguaje instalado en nuestra máquina!
¿Qué es uv?
uv es una herramienta súper rápida para gestionar versiones de Python, entornos virtuales y dependencias en proyectos Python. Desarrollada en Rust y por los mismos creadores de ruff, uv busca reemplazar herramientas clásicas como pip, Poetry, virtualenv o pip-tools con una única solución todo en uno.
uv aprovecha al máximo la velocidad del lenguaje Rust para resolver e instalar dependencias de manera mucho más eficiente, manteniendo compatibilidad con los archivos pyproject.toml
. Además, es capaz de crear y gestionar entornos virtuales de forma automática, lo que simplifica enormemente la configuración de proyectos Python desde cero.
uv está diseñado para ser una herramienta “todo en uno”, reduciendo el tiempo de espera y la complejidad del flujo de trabajo típico en Python. Ya sea que estés empezando un nuevo proyecto o manteniendo uno existente, uv promete hacer ese proceso más rápido, sencillo y confiable.
¿Cómo instalar uv?
La instalación es realmente sencilla, mi recomendación es ejecutar esto en tu terminal:
curl -LsSf https://astral.sh/uv/install.sh | sh
De forma alternativa, si eres usuario de macOS puedes usar Homebrew:
brew install astral-sh/tap/uv
Crear un proyecto con uv
Crear un proyecto con uv es realmente sencillo, basta con escribir en nuestra terminal:
uv init weather_app
Cuando la creación haya finalizado, tendremos una carpeta weather_app con la siguiente estructura:
.
├── .gitginore
├── .python-version
├── main.py
├── pyproject.toml
└── README.md
Como podemos ver, uv nos ha creado los siguientes ficheros:
.gitignore
: Archivos que Git debe ignorar..python-version
: El archivo.python-version
contiene la versión de Python predeterminada del proyecto. Este archivo le indica a uv qué versión de Python usar al crear el entorno virtual del proyecto.main.py
: Punto de entrada de la aplicación.pyproject.toml
: El archivopyproject.toml
contiene metadatos sobre tu proyecto. Usarás este archivo para especificar dependencias, así como detalles del proyecto como su descripción o licencia. Puedes editar este archivo manualmente o usando comandos de uv.README.md
: Documento con la descripción de la aplicación.
Añadir uv a un proyecto existente
De igual forma, es posible añadir uv a un proyecto existente. Para ello, simplemente debemos ejecutar en nuestra terminal uv init
dentro de la carpeta del proyecto en cuestión.
En este caso simplemente nos creará los ficheros .python-version
y pyproject.toml
dentro de la carpeta de nuestro proyecto.
Migrar un proyecto a uv
Una característica que me gusta mucho de uv es la herramienta uvx, que entre otras muchas cosas nos permite migrar nuestros proyectos a uv.
En mi caso, es algo que he tenido que hacer en mi trabajo, ya que previamente estábamos usando Poetry y, si no llega a ser por esta herramienta, hubiera sido súper tedioso.
Para la migración, solo necesitamos ejecutar el siguiente comando dentro de la carpeta del proyecto a migrar y uvx se encargará del resto (básicamente, adaptar el fichero pyproject.toml
a uv, borrar el poetry.lock
y crear un nuevo uv.lock
):
uvx migrate-to-uv
Cómo usar uv
uv tiene multitud de comandos (se pueden listar todos con el comando uv help
), pero yo me quiero centrar en los que creo que son más importantes:
uv python install x.y.z
: nos permite instalar una versión de Python concreta para nuestro entorno virtual específico de esta aplicación.uv python pin x.y.z
: nos permite fijar una versión de Python concreta para nuestro entorno virtual específico de esta aplicación.uv sync
: nos permite instalar nuestras dependencias.uv lock --upgrade
: nos permite actualizar nuestras dependencias.uv add PACKAGE_NAME
: nos permite instalar nuevos paquetes.uv run COMMAND
: nos permite ejecutar comandos dentro de nuestro entorno virtual:uv run pytest tests/ -ra
uv run mypy .
uv run ruff format src tests
Usar Make para operar con uv
Aunque es muy interesante conocer cómo operar con uv, creo que tiene más valor centrarse en otras cosas y no tener que estar recordando cómo eran los comandos del apartado anterior. Por ello, yo soy muy fan de usar Makefile para darle más semántica a estos comandos y, por tanto, que sean más fáciles de recordar.
Esta podría ser la forma de nuestro Makefile para operar uv:
.DEFAULT_GOAL := help
.PHONY: help
help: ## Show this help.
@grep -E '^\S+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "%-30s %s\n", $$1, $$2}'
pre-requirements:
@scripts/pre-requirements.sh
.PHONY: install
install: pre-requirements ## Install the app packages
uv python install 3.12.8
uv python pin 3.12.8
uv sync
.PHONY: update
update: pre-requirements ## Updates the app packages
uv lock --upgrade
.PHONY: add-package
add-package: pre-requirements ## Installs a new package in the app. ex: make install package=XXX
uv add $(package)
.PHONY: run
run: pre-requirements ## Runs the app in production mode
uv run python main.py
.PHONY: check-typing
check-typing: pre-requirements ## Run a static analyzer over the code to find issues
uv run mypy .
.PHONY: check-lint
check-lint: pre-requirements ## Checks the code style
uv run ruff check
.PHONY: lint
lint: pre-requirements ## Lints the code format
uv run ruff check --fix
.PHONY: check-format
check-format: pre-requirements ## Check format python code
uv run ruff format --check
.PHONY: format
format: pre-requirements ## Format python code
uv run ruff format
.PHONY: checks
checks: check-lint check-format check-typing ## Run all checks
.PHONY: test
test: pre-requirements ## Run tests
uv run pytest -n auto tests -ra
Con este Makefile creado, tendremos a nuestra disposición una serie de comandos tales como:
make install
make update
make add-package package=pytest
make run
make test
make format
make check-typing
Un detalle menor, pero no por ello menos importante, es que en todos los comandos de Make podemos ver que se hace referencia a un comando llamado pre-requirements
. Este comando no es ni más ni menos que un script, el cual valida que tenemos uv instalado en nuestra máquina, ya que es lo único que necesitamos para poder trabajar en nuestro proyecto.
El script en cuestión tiene este aspecto:
#!/bin/bash
function check_uv() {
if ! command -v uv &> /dev/null; then
echo "uv is not installed. Please go to https://docs.astral.sh/uv/getting-started/installation/."
return 1
fi
return 0
}
check_uv
Configurar uv
Toda la descripción y configuración de uv reside en el fichero pyproject.toml. En él vamos a poder definir todo lo relativo a nuestro propio proyecto, así como a las librerías externas que usemos, como ruff o mypy.
[project]
name = "weather-app"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
...
[tool.pyright]
venvPath = "."
venv = ".venv"
[tool.ruff]
line-length = 120
[tool.mypy]
check_untyped_defs = true
...
En este ejemplo que os muestro hemos modificado:
- El tamaño de la línea para ruff (nuestro formateador).
- La localización de nuestro entorno virtual para pyright (nuestro LSP para Python).
- La configuración de mypy respecto al tipado.
Activado y desactivado automático de los entornos virtuales
Una cosa muy importante, aunque no estrictamente necesaria, es el manejo de los entornos virtuales creados por uv. Será necesaria la activación de los mismos cuando estemos dentro de la carpeta del proyecto y su desactivación cuando salgamos.
Aunque esto se puede hacer de forma manual con un par de comandos:
source .venv/bin/activate
: Para activar el entorno virtual.deactivate
: Para desactivar el entorno virtual.
Existen alternativas para que esta activación y desactivación sea automática. Si usas oh-my-zsh, como es mi caso, puedes instalar el plugin pyautoenv, el cual se encargará de todo y tú te podrás olvidar de los entornos virtuales.
=> cd weather_app
(weather_app) => git:(main)
(weather_app) => git:(main) cd ..
=>
A partir del momento en el que esté instalado el plugin, podremos ver, sin necesidad de activarlo, nuestro entorno virtual entre ()
en el prompt de nuestra terminal cuando entremos en una carpeta con un entorno virtual. De igual forma, cuando nos movemos a una carpeta sin entorno virtual, este se desactivará automáticamente.
Dockerizar una app con uv
Podemos ir un paso más allá y no tener que instalar nada en nuestra máquina (ni siquiera uv) haciendo uso de Docker.
De esta forma, podemos dockerizar nuestra app creando un fichero Dockerfile, usando la imagen de Python con la versión concreta, instalar uv y dejar todas nuestras dependencias encapsuladas dentro de nuestra imagen:
FROM python:3.12.8-alpine
RUN apk update --no-cache && apk upgrade --no-cache --available
RUN pip install --no-cache-dir uv
RUN uv python pin 3.12.8
WORKDIR /code
COPY pyproject.toml /code/pyproject.toml
RUN uv sync --no-group test
COPY main.py /code/main.py
COPY src /code/src
CMD ["uv", "run", "python", "main.py"]
Usar uv en un CI/CD (ej: GitHub Actions)
De forma muy similar a como dockerizamos nuestra app, podemos generar una pipeline con nuestro CI/CD de confianza. Por simplicidad, voy a usar GitHub Actions.
Os dejo por aquí un ejemplo muy sencillo, sacado de este repositorio:
name: Pass checks and tests
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.12.8
uses: actions/setup-python@v3
with:
python-version: "3.12.8"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: make install
- name: Check types, format and styles
run: make checks
- name: Tests
run: make test
Conclusiones
Como hemos podido ver, uv va un paso más allá que Poetry, en tanto que no solo nos permite gestionar nuestros entornos virtuales, sino que además nos permite gestionar nuestras versiones de Python de forma súper sencilla y limpia.
Llegando a poder desarrollar en Python sin ningún tipo de limitación, ¡sin tenerlo instalado en nuestra máquina!
Por último, destacar que en mi experiencia personal migrando unos cuantos proyectos de Poetry a uv, puedo decir que la mejora en el rendimiento es abismal y al principio impresiona. Con estas migraciones hemos conseguido reducir notablemente el tiempo de nuestras pipelines, lo que siempre es algo positivo para los equipos.
Os dejo por aquí varios repositorios en los que he aplicado todo lo comentado en el artículo por si os sirven de ayuda:
Un saludo, ¡espero que os haya gustado!