uv 0.12: el gestor de Python que ya dejó atrás a pip y venv
uv 0.12: el gestor de Python que ya dejó atrás a pip y venv
Si todavía usás pip install -r requirements.txt y armás un virtualenv a mano con python -m venv .venv, este post es para vos. La herramienta uv, del equipo de Astral (los mismos detrás de Ruff), acaba de llegar a la versión 0.12 con cambios que la convierten en la opción por default para cualquier proyecto nuevo en Python. Y no es hype: es velocidad, reproducibilidad y menos boilerplate.
Por qué uv existe
Durante años el flujo de trabajo de un dev Python fue una secuencia conocida: instalar Python, crear un virtualenv, activar el venv, escribir un requirements.txt con versiones pinneadas, y rezar para que en la máquina del compañero se instale todo igual. En 2024 Astral presentó uv como un reemplazo moderno escrito en Rust que colapsa todos esos pasos en uno solo.
Lo que empezó como un "pip pero rápido" hoy es un gestor completo de proyectos: maneja versiones de Python, dependencias, lockfiles, scripts, builds y publicación en PyPI. La versión 0.12 consolida esa visión con un cambio fuerte en cómo se inicializa un proyecto.
Qué trae uv 0.12
El cambio central:
uv initahora genera un proyecto con layoutsrc/, configurauv_buildcomo backend por default y crea un entry point ejecutable. Es decir, dejás de tener unmain.pysuelto en la raíz.
Antes de la 0.12, uv init te dejaba esto:
mi-proyecto/
├── main.py
├── pyproject.toml
└── README.md
Ahora te deja esto:
mi-proyecto/
├── pyproject.toml
├── README.md
└── src/
└── mi_proyecto/
├── __init__.py
└── __init__.py # contiene una función main() tipada
El cambio no es cosmético. El layout src/ es el que recomienda la guía oficial de packaging de Python porque evita el clásico problema de importar la versión "del código fuente" cuando en realidad querés importar la versión instalada. Si alguna vez te pasó que tu test pasaba en local pero el CI rompía, probablemente estabas en flat layout y este layout lo soluciona.
Además, el pyproject.toml ahora define un bloque project.scripts con un alias listo para correr:
[project.scripts]
mi-proyecto = "mi_proyecto:main"
Eso significa que después de uv sync podés ejecutar uv run mi-proyecto y se llama a la función main() del paquete. Nada de if __name__ == "__main__": ni de armar CLIs a mano para un script chico.
Instalación en 30 segundos
Si tenés macOS o Linux, una línea y listo:
curl -LsSf https://astral.sh/uv/install.sh | sh
En Windows, el instalador está en docs.astral.sh/uv. Después de instalar, verificá:
uv --version
# uv 0.12.0
Del requirements.txt a un proyecto real
Vamos a lo concreto. Supongamos que querés armar un script para descargar videos de YouTube, similar al que ya vimos en el post [Descargar Videos de YouTube con Python](Descargar Videos de YouTube con Python.md). El flujo con uv es así:
1. Inicializar el proyecto
uv init yt-downloader
cd yt-downloader
Eso te crea la estructura nueva con src/. Si querés el layout viejo, podés pasar --package=false, pero la recomendación del propio Astral es el nuevo.
2. Sumar dependencias
uv add pytube tkinter
Este comando edita el pyproject.toml, crea un uv.lock y deja todo listo. El lockfile es la pieza clave: garantiza que vos, tu compañero y tu CI instalen exactamente las mismas versiones. Reemplaza al requirements.txt y al pip freeze en un solo archivo.
3. Correr el script
uv run yt-downloader
uv run se encarga de crear el virtualenv, instalar las dependencias declaradas y ejecutar el entry point. No hace falta activar nada.
4. Manejar la versión de Python
¿Necesitás asegurarte de que todos usen Python 3.12? Lo declarás en pyproject.toml:
[project]
requires-python = ">=3.12"
Y uv se encarga de descargar la versión correcta automáticamente con uv python install 3.12. Si tu sistema tiene otra versión, no importa: uv la baja on demand y la usa para tu proyecto sin tocar tu instalación global.
Cómo se compara con el flujo clásico
Una comparación directa para que se vea el ahorro real:
| Tarea | Flujo clásico | Con uv |
|---|---|---|
| Crear venv | python -m venv .venv && source .venv/bin/activate | Implícito en uv run |
| Instalar dependencias | pip install -r requirements.txt | uv sync |
| Congelar versiones | pip freeze > requirements.txt | uv.lock automático |
| Cambiar versión Python | Instalar a mano o usar pyenv | uv python install 3.12 |
| Publicar en PyPI | python -m build && twine upload ... | uv publish |
La diferencia de velocidad es notable: uv resuelve e instala dependencias entre 10 y 100 veces más rápido que pip porque hace la resolución en paralelo y cachea los wheels en ~/.cache/uv/. En un proyecto mediano con cientos de dependencias, lo que antes tardaba un minuto, ahora termina en pocos segundos.
Migrar un proyecto existente no es traumático
Si ya tenés un proyecto con requirements.txt, el camino es:
- Inicializá uv en la raíz:
uv init --bare . - Importá las dependencias actuales:
uv add -r requirements.txt - Borrá el
requirements.txty empezá a commitear eluv.lock. - Reemplazá los pasos de tu README para usar
uv syncyuv run.
El uv sync lee el lockfile y deja el entorno idéntico en cualquier máquina. En CI, podés cachear ~/.cache/uv/ y bajar el tiempo de instalación a casi cero a partir de la segunda corrida.
Tip: Si tu proyecto es una librería que se publica en PyPI, el nuevo
uv_buildbackend es lo que necesitás. Reemplaza asetuptoolsyhatchlingcomo opción por default, y como está escrito en Rust es sensiblemente más rápido para builds grandes.
Algo que veo venir
El propio Simon Willison se preguntó en su blog cuándo Astral va a marcar a uv como 1.0. La respuesta obvia es que todavía hay esquinas por pulir (particularmente la compatibilidad con proyectos que usan setup.py legacy), pero la velocidad a la que el proyecto incorporó features nuevas en los últimos 12 meses hace pensar que el 1.0 está cerca. Mientras tanto, ya hay empresas corriendo uv en producción, así que no es una tecnología riesgosa para adoptar.
Lo que sí me parece claro es que el flujo "venv + pip + requirements.txt" se va a sentir tan arcaico en dos años como se siente hoy el python setup.py install. Si tenés la chance de elegir para un proyecto nuevo, no hay razón seria para no empezar con uv.
Links útiles para arrancar
Y si querés ver otros flows de tooling moderno, te dejo el post de Docker para desarrolladores y el de GitHub Actions para sincronizar Obsidian con tu blog. Mismo espíritu: reemplazá el flujo manual de toda la vida por una herramienta que lo automatiza.
seguir leyendo
Claude Opus 5: qué cambia para los que lo usamos desde la API
Anthropic lanzó Claude Opus 5 con un knob de effort configurable y resultados SOTA en coding. Mirá los benchmarks, precios y un ejemplo real con el SDK.
Cómo usar Ollama para correr LLMs localmente: guía completa para developers
Aprende a instalar Ollama, descargar modelos open-source y correr LLMs como Llama 3 o Mistral directamente en tu máquina. Privacidad, cero costos de API y control total sobre tus datos.
Generador de Contraseñas con Python
Generador de contraseñas seguras en Python usando random y array, con garantía de caracteres mixtos.
comentarios
$ subscribe --newsletter
Recibe los nuevos posts en tu email
Un email cuando publico algo nuevo. Sin ruido, sin relleno.