uv, Poetry y PDM: gestores modernos de dependencias

El ecosistema de herramientas para gestionar dependencias en Python ha madurado muchísimo en los últimos años. Ya no estamos limitados a pip + venv a pelo. Hoy tenemos tres alternativas con filosofías distintas, y elegir la correcta para tu proyecto marca una diferencia real en productividad y reproducibilidad.

Qué resuelve cada una (y por qué existe)

El problema central es siempre el mismo: necesitas que tu proyecto funcione igual en tu máquina, en CI y en producción. pip sin más no te da eso, porque pip install requests instala “la última versión compatible”, lo cual cambia con el tiempo. El lockfile es la solución: un archivo que fija cada paquete instalado —incluyendo dependencias transitivas— a una versión y hash exactos. Reproducibilidad bit a bit.

Las tres herramientas que vamos a ver atacan este problema, pero con prioridades distintas.

uv es un instalador y gestor de entornos escrito en Rust por Astral (los mismos de Ruff). Su única obsesión es la velocidad: resuelve y descarga dependencias usando paralelismo agresivo y una caché global compartida entre proyectos. En benchmarks reales es 10-100x más rápido que pip. Funciona como reemplazo directo de pip + venv, lo que significa que puedes adoptarlo sin cambiar nada más en tu flujo.

Poetry lleva más tiempo en el ecosistema y tiene una propuesta más amplia: gestión de dependencias con poetry.lock, manejo del entorno virtual integrado, y publicación a PyPI desde el mismo CLI. Si mantienes una librería open source, Poetry es el estándar de facto para muchos equipos porque el ciclo desarrollar → versionar → publicar está resuelto de fábrica.

PDM es la apuesta más purista estándares: soporta PEP 621 (pyproject.toml como fuente única de verdad) y experimentó con PEP 582 (dependencias locales sin activar entorno virtual). Es menos popular que Poetry pero su alineación con los estándares de packaging es impecable.

¿Cuándo te duele no usar alguna de estas? En el momento en que alguien clona tu repo, ejecuta pip install -r requirements.txt y las pruebas fallan porque requests 2.31 se comporta diferente a requests 2.28. Con un lockfile, eso no pasa.

Ejemplo completo: flujo real con uv y con Poetry

Vamos a ver los dos flujos más relevantes para proyectos distintos.

# project/src/myapp/main.py
# Aplicación de ejemplo que usaremos en ambos flujos

import httpx
import structlog

log = structlog.get_logger()


def fetch_status(url: str) -> int:
    response = httpx.get(url, timeout=10)
    log.info("fetched", url=url, status=response.status_code)
    return response.status_code


if __name__ == "__main__":
    code = fetch_status("https://httpbin.org/get")
    print(f"HTTP {code}")
# pyproject.toml — formato estándar PEP 621 compatible con uv y pdm
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "httpx>=0.27",
    "structlog>=24.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "ruff>=0.4",
]

# uv usa esta sección para configurar su comportamiento
[tool.uv]
dev-dependencies = [
    "pytest>=8.0",
    "ruff>=0.4",
]
# ── Flujo con uv ──────────────────────────────────────────────────────

# Crear entorno virtual en .venv (equivalente a python -m venv .venv)
uv venv

# Instalar dependencias del proyecto — usa caché global en ~/.cache/uv
# La primera vez descarga; las siguientes son prácticamente instantáneas
uv pip install -e ".[dev]"

# Generar lockfile (uv.lock) — fija TODAS las dependencias transitivas
uv lock

# Instalar desde lockfile: reproducible en cualquier máquina
uv pip sync uv.lock

# Ejecutar un script sin activar el entorno manualmente
uv run python src/myapp/main.py

# Ejecutar tests igual
uv run pytest

# Añadir una dependencia nueva y actualizar el lockfile en un paso
uv add rich
# ── Flujo con Poetry ──────────────────────────────────────────────────

# Inicializar proyecto (genera pyproject.toml con formato poetry)
poetry new myapp --src
cd myapp

# Añadir dependencias — actualiza pyproject.toml y poetry.lock atómicamente
poetry add httpx structlog
poetry add --group dev pytest ruff

# Instalar todo desde poetry.lock (lo que va en CI)
poetry install --no-root   # sin instalar el paquete en sí, solo deps

# Ejecutar dentro del entorno gestionado por poetry
poetry run python src/myapp/main.py
poetry run pytest

# Cuando el proyecto es una librería: build + publish en dos comandos
poetry build          # genera dist/myapp-0.1.0-py3-none-any.whl
poetry publish        # sube a PyPI (pide credenciales o token)

# Actualizar una dependencia específica y regenerar el lockfile
poetry update httpx
# ── Flujo con PDM ─────────────────────────────────────────────────────

# PDM es el más cercano al estándar PEP 621 puro
pdm init                    # genera pyproject.toml estándar
pdm add httpx structlog
pdm add -dG dev pytest ruff

# pdm.lock es el equivalente al poetry.lock
pdm install                 # instala desde lockfile si existe

pdm run python src/myapp/main.py
pdm publish                 # publicación también integrada

Qué significa cada decisión

El lockfile es el artefacto clave, no el pyproject.toml. El pyproject.toml declara restricciones (“quiero httpx >= 0.27″); el lockfile registra la realidad (“instalé httpx 0.27.4 con SHA256 abc123... y sus dependencias fueron httpcore 1.0.5, certifi 2024.2.2…”). Cuando haces uv pip sync uv.lock o poetry install, Python construye exactamente ese grafo, sin sorpresas.

uv comparte caché entre proyectos — esto es clave. Si tienes diez proyectos que usan pytest 8.2, uv lo descarga una vez y lo vincula. Poetry y PDM también tienen caché, pero uv está optimizado con resolución paralela escrita en Rust, lo que hace que incluso la primera instalación de un proyecto sea absurdamente rápida comparada con pip.

uv run es especialmente útil en CI. En lugar de activar el entorno con source .venv/bin/activate y luego ejecutar comandos, uv run maneja el entorno de forma transparente. Reduce una línea de shell propensa a errores de path.

Poetry gestiona el entorno virtual de forma opaca por defecto — lo crea en ~/.cache/pypoetry/virtualenvs/. Esto confunde a herramientas como VS Code que esperan un .venv local. Puedes cambiar esto con poetry config virtualenvs.in-project true, y es recomendable hacerlo globalmente en tu máquina.

PDM y PEP 582 merecen una nota: la propuesta de instalar paquetes en __pypackages__/ local (sin entorno virtual) nunca fue aceptada como estándar oficial, y PDM ahora usa entornos virtuales por defecto igual que los demás. El soporte PEP 582 sigue disponible pero ya no es su diferenciador principal.

Errores que debes conocer

Error: Commitear poetry.lock o uv.lock en .gitignore por considerarlo un archivo generado, rompiendo la reproducibilidad del proyecto.

# ❌ Wrong — en .gitignore
poetry.lock
uv.lock

# ✅ Right — el lockfile SIEMPRE va en control de versiones
# .gitignore debe ignorar el entorno, no el lockfile
.venv/
__pycache__/
dist/

El lockfile es precisamente lo que hace que git clone + poetry install produzca el mismo entorno que el tuyo. Sin él, cada instalación resuelve de nuevo.


Error: Mezclar pip install con un proyecto gestionado por Poetry o uv, instalando paquetes que no quedan registrados en el lockfile.

# ❌ Wrong — instala en el entorno pero no actualiza poetry.lock
source .venv/bin/activate
pip install pandas

# ✅ Right — Poetry actualiza pyproject.toml y poetry.lock atómicamente
poetry add pandas
# o con uv:
uv add pandas

Hacer pip install manual en un entorno gestionado es exactamente el “funciona en mi máquina” que estas herramientas existen para eliminar.


Error: Usar poetry install en CI sin --no-root cuando el paquete no tiene un punto de entrada instalable, causando errores de build innecesarios.

# ❌ Wrong — intenta hacer build del paquete, falla si falta build backend
poetry install

# ✅ Right — instala solo las dependencias, sin instalar el proyecto en sí
poetry install --no-root

--no-root es el flag correcto para aplicaciones (no librerías) en CI, donde solo te interesa tener las deps disponibles para ejecutar tests.


La regla práctica para elegir: si necesitas velocidad en CI o un reemplazo inmediato de pip/venv sin cambiar tu estructura de proyecto, uv gana sin discusión. Si mantienes una librería que publicas a PyPI y quieres que todo el ciclo de vida esté en un solo CLI, Poetry te da eso sin fricciones.

73

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio