⏱️ Lectura: 14 min

Un solo comando, uv sync, reemplaza hoy el ritual de crear un entorno virtual, activarlo a mano, instalar con pip y esperar a que el resolutor de turno no rompa nada. Eso es lo que resuelve el gestor de paquetes uv, escrito en Rust por Astral, la misma empresa detrás del linter Ruff.

📑 En este artículo
  1. TL;DR
  2. Qué es el gestor de paquetes uv y por qué importa
  3. Cómo funciona uv por dentro
  4. Ejemplos prácticos: de “hola mundo” a un proyecto real
  5. Cómo empezar: instalación y primeros pasos
  6. Casos de uso reales
  7. Errores comunes y buenas prácticas
  8. Comparativa con alternativas
  9. Profundizando: el resolutor, el caché y PEP 723
  10. Preguntas frecuentes
    1. Qué es el gestor de paquetes uv y quién lo desarrolla?
    2. uv necesita Python instalado para funcionar?
    3. uv reemplaza completamente a poetry?
    4. Todavía necesito pyenv si uso uv?
    5. Cómo migro un proyecto con requirements.txt a uv?
    6. uv sirve para paquetes con dependencias de sistema operativo?
  11. Referencias

Esta guía explica qué problema resuelve uv, cómo funciona por dentro, y cómo usarlo día a día para manejar dependencias, versiones de Python y scripts sueltos sin combinar pip, poetry, pipenv y pyenv por separado.

TL;DR

  • Vas a instalar uv sin necesitar Python previo, con un script o con pip install uv.
  • Vas a crear y sincronizar un entorno virtual completo con uv venv y uv sync, sin activarlo a mano.
  • Vas a manejar varias versiones de Python con uv python install, sin depender de pyenv.
  • Vas a fijar dependencias reproducibles en uv.lock con uv add y uv sync.
  • Vas a ejecutar scripts sueltos con dependencias declaradas inline usando uv run y PEP 723.
  • Vas a migrar un proyecto con requirements.txt a uv siguiendo pasos concretos.
  • Vas a identificar errores comunes (cache corrupto, mezclar pip y uv) y cómo evitarlos.
  • Vas a comparar uv contra pip, poetry, pipenv y conda para decidir cuándo conviene cada uno.

Qué es el gestor de paquetes uv y por qué importa

uv es un gestor de paquetes y entornos de Python escrito en Rust que unifica en un solo binario lo que hasta ahora exigía combinar varias herramientas: pip para instalar paquetes, pip-tools o poetry para fijar versiones exactas, virtualenv para crear entornos aislados, pipx para instalar herramientas de línea de comandos sin contaminar el entorno global, y pyenv para manejar múltiples versiones de Python en la misma máquina.

La gestión de paquetes con uv se apoya en dos archivos centrales: pyproject.toml, donde se declaran el nombre del proyecto, sus dependencias y la versión mínima de Python requerida, y uv.lock, un archivo de bloqueo generado automáticamente que fija la versión exacta y el hash criptográfico de cada paquete resuelto, incluyendo dependencias transitivas.

Astral, la empresa que mantiene uv, también desarrolla Ruff, el linter y formateador de Python que reemplazó a flake8, isort y black en muchos proyectos por su velocidad. uv sigue la misma filosofía: tomar un problema que la comunidad Python resolvía con herramientas escritas en Python puro y reescribirlo en Rust, sin cambiar el flujo de trabajo que los equipos ya conocen, ya que los comandos de uv se leen casi igual que los de pip o poetry.

Además de proyectos individuales, uv soporta workspaces: un repositorio con varios paquetes Python relacionados (por ejemplo, una librería y su CLI) que comparten un mismo uv.lock y un mismo entorno virtual, algo que antes requería configuración manual con pip o extensiones específicas de poetry.

Terminal mostrando la ejecucion del comando uv sync en un proyecto Python
uv sync crea el entorno y resuelve dependencias sin activar el virtualenv a mano.

Cómo funciona uv por dentro

Cuando ejecutás uv add o uv sync, uv primero resuelve el árbol de dependencias completo, incluyendo dependencias transitivas y las variantes según sistema operativo o versión de Python, y lo escribe en uv.lock. Ese archivo fija versión y hash de cada paquete para cada plataforma soportada, así que instalar en otra máquina o en integración continua produce el mismo entorno.

Antes de descargar nada de PyPI, uv consulta un caché global compartido entre todos los proyectos del sistema, ubicado fuera de cualquier .venv. Si un paquete y versión ya se descargaron para otro proyecto, uv lo enlaza en el nuevo entorno virtual mediante enlaces duros o copia por referencia en vez de descargarlo y copiarlo de nuevo, lo que evita repetir trabajo entre proyectos que comparten dependencias.

Podés confirmar dónde vive ese caché con uv cache dir, revisar cuánto espacio ocupa con las herramientas del sistema operativo, y limpiarlo por completo con uv cache clean si sospechás de una entrada corrupta tras una descarga interrumpida.

El resolutor también respeta marcadores de entorno (sistema operativo, arquitectura, versión de Python) dentro del mismo uv.lock, así que un mismo lockfile puede describir el conjunto correcto de paquetes tanto para un desarrollador en macOS como para el contenedor Linux donde corre en producción.

flowchart TD
 A["pyproject.toml"] --> B["Resolutor de uv"]
 B --> C["uv.lock"]
 C --> D["uv sync"]
 D --> E[(".venv local")]
 B --> F["Cache global de paquetes"]
 F --> D

Ejemplos prácticos: de “hola mundo” a un proyecto real

El primer paso es instalar uv. No hace falta tener Python instalado de antemano, porque uv se distribuye como binario independiente:

curl -LsSf https://astral.sh/uv/install.sh | sh
uv --version

El script descarga el binario y lo agrega al PATH. El segundo comando confirma la instalación imprimiendo la versión instalada.

Con uv instalado, creamos un proyecto nuevo y agregamos una dependencia de producción:

uv init mi-proyecto
cd mi-proyecto
uv add requests
uv run python -c "import requests; print(requests.__version__)"

uv init genera un pyproject.toml mínimo. uv add requests resuelve la versión, la escribe en pyproject.toml y en uv.lock, y crea el entorno virtual .venv si todavía no existe. uv run ejecuta el comando dentro de ese entorno sin necesidad de activarlo.

Los proyectos reales necesitan separar dependencias de desarrollo (tests, linters) de las de producción. uv resuelve esto con grupos de dependencias:

uv add --dev pytest ruff
uv run pytest
uv run ruff check .

uv add --dev agrega las dependencias al grupo de desarrollo dentro de pyproject.toml. Ese grupo no se instala cuando otro proyecto usa este paquete como dependencia, solo cuando alguien trabaja directamente sobre el repositorio.

Para scripts sueltos que no forman parte de un proyecto, uv soporta metadata de dependencias inline según PEP 723:

# script.py
# /// script
# dependencies = ["httpx"]
# ///
import httpx

respuesta = httpx.get("https://example.com")
print(respuesta.status_code)

Al ejecutar uv run script.py, uv lee el bloque de metadata, resuelve httpx en un entorno temporal aislado y corre el script, sin que haga falta un pyproject.toml ni un entorno creado a mano.

Cómo empezar: instalación y primeros pasos

Para adoptar el gestor de paquetes uv en un proyecto ya existente que usa requirements.txt, los pasos son:

  1. Instalar uv con el script anterior o con pip install uv si preferís mantenerlo dentro de un entorno gestionado.
  2. Crear el pyproject.toml con uv init --no-readme dentro del repositorio existente.
  3. Importar las dependencias actuales: uv add -r requirements.txt.
  4. Fijar la versión de Python del proyecto: uv python pin 3.12, que escribe un archivo .python-version.
  5. Generar el entorno y el lockfile: uv sync.
  6. Confirmar el intérprete activo: uv run python --version y uv python list para ver qué versiones tiene uv instaladas y cuál está en uso.

La configuración mínima de pyproject.toml que necesita uv es:

[project]
name = "mi-proyecto"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
 "requests>=2.31",
]

El campo requires-python es clave: sin él, el resolutor de uv puede elegir versiones de paquetes incompatibles con el intérprete que termines usando en producción.

uv también incluye uvx (alias de uv tool run), equivalente a npx en el ecosistema de Node: ejecuta una herramienta de línea de comandos publicada en PyPI en un entorno temporal, sin instalarla de forma permanente.

uvx ruff check .
uvx cowsay -t "hola"

💡 Tip: usá uv run en lugar de activar el entorno virtual a mano con source .venv/bin/activate. uv detecta el .venv del proyecto automáticamente y ejecuta el comando dentro de él.

Casos de uso reales

En integración continua, reemplazar pip install -r requirements.txt por uv sync --frozen evita que el pipeline reescriba el lockfile por accidente: el comando falla explícitamente si uv.lock quedó desactualizado respecto a pyproject.toml, en vez de resolver versiones nuevas de forma silenciosa.

Dentro de un Dockerfile, copiar primero pyproject.toml y uv.lock, ejecutar uv sync --frozen --no-install-project y recién después copiar el código fuente aprovecha el cacheo de capas de Docker: si el código cambia pero las dependencias no, la capa de instalación no se vuelve a ejecutar.

FROM python:3.12-slim
COPY pyproject.toml uv.lock ./
RUN pip install uv && uv sync --frozen --no-install-project
COPY . .
CMD ["uv", "run", "python", "app.py"]

Para herramientas de línea de comandos como black, ruff o httpie, uv tool install ruff cumple el rol que hoy cubre pipx: instala la herramienta en un entorno aislado propio y la expone en el PATH, sin contaminar el entorno del proyecto actual.

Para equipos que administran varias versiones de Python en la misma máquina, por ejemplo mantener un servicio en una versión mientras el resto del equipo migra a otra más nueva, uv python install 3.10 3.11 3.12 descarga las tres versiones usando binarios precompilados de python-build-standalone, sin pasar por pyenv ni por el gestor de paquetes del sistema operativo.

Diagrama de multiples entornos virtuales de Python compartiendo un cache central
El caché global evita reinstalar el mismo paquete para cada proyecto nuevo.

Errores comunes y buenas prácticas

  • No versionar uv.lock: si el archivo no se sube al repositorio, cada máquina resuelve versiones distintas y el problema de “funciona en mi máquina” vuelve a aparecer.
  • Mezclar pip y uv en el mismo entorno: instalar con pip install dentro de un .venv creado por uv rompe la sincronía con uv.lock. Usá uv pip install o, mejor, uv add.
  • Confundir uv add con uv pip install: uv add modifica pyproject.toml y uv.lock de forma persistente; uv pip install solo instala en el entorno actual, sin dejar registro en el proyecto.
  • Omitir requires-python: sin ese campo el resolutor puede aceptar paquetes que no corren en la versión real del intérprete de producción.
  • Caché corrupto tras una interrupción: si una descarga se corta a la mitad, uv cache clean fuerza a uv a volver a descargar en vez de usar un paquete parcial.
  • Copiar uv.lock entre proyectos distintos: cada lockfile corresponde a un árbol de dependencias específico; reutilizarlo en otro proyecto sin regenerarlo produce versiones incoherentes con lo declarado en su pyproject.toml.
  • Esperar que uv resuelva dependencias de sistema operativo: uv instala paquetes Python, no librerías nativas del sistema; para eso siguen haciendo falta apt, brew o conda.

⚠️ Ojo: uv.lock no es intercambiable con poetry.lock ni con requirements.txt. Si necesitás exportar un requirements.txt para una herramienta externa, generalo con uv export --format requirements-txt.

Comparativa con alternativas

Herramienta Cuándo usarla Ventaja Limitación
uv Proyecto nuevo o migración completa de la gestión de dependencias Un solo binario para entorno virtual, resolución, lockfile y versiones de Python Ecosistema de plugins más chico que poetry
pip + venv Scripts simples sin necesidad de lockfile Viene incluido con Python, cero instalación extra Sin resolución determinista ni lockfile nativo
poetry Proyectos que ya dependen de su sistema de plugins o de configuración específica en pyproject.toml Gestión de publicación a PyPI integrada desde hace años Resolución de dependencias históricamente más lenta que uv
pipenv Equipos que ya estandarizaron Pipfile Separa dependencias de desarrollo y producción con claridad Desarrollo menos activo que uv o poetry
conda Paquetes científicos con dependencias binarias no disponibles en PyPI Maneja librerías del sistema operativo, no solo paquetes Python No reemplaza la gestión fina de dependencias Python por proyecto

Profundizando: el resolutor, el caché y PEP 723

El resolutor de uv usa un algoritmo de tipo PubGrub, el mismo enfoque que adoptaron Cargo (el gestor de paquetes de Rust) y Dart, que encuentra un conjunto de versiones compatibles o explica con precisión qué paquetes entran en conflicto, en vez de fallar con un error genérico de dependencias incompatibles.

Por defecto, uv resuelve hacia las versiones más nuevas compatibles. Cuando un proyecto necesita fijarse deliberadamente a versiones mínimas, por ejemplo para verificar que declara correctamente sus límites de compatibilidad, uv lock --resolution lowest fuerza al resolutor a elegir la versión más antigua que satisface cada restricción.

Para equipos que todavía dependen de scripts o pipelines escritos contra la interfaz de pip, uv expone una capa de compatibilidad: uv pip install, uv pip compile y uv pip sync aceptan argumentos equivalentes a los de pip y pip-tools, lo que permite migrar de forma gradual sin reescribir scripts existentes de una sola vez.

La metadata inline de PEP 723 que soporta uv run no es una invención de Astral: es un estándar del lenguaje pensado para scripts de un solo archivo, y cualquier otra herramienta que implemente el mismo PEP puede leer el mismo bloque de comentarios sin depender de uv.

flowchart LR
 A["uv init"] --> B["uv add paquete"]
 B --> C["uv lock"]
 C --> D["uv sync"]
 D --> E["uv run script.py"]
 E --> F["uv build"]
 F --> G["uv publish"]

💭 Clave: uv y Ruff comparten filosofía: reemplazar una herramienta lenta escrita en Python puro por un binario en Rust que resuelve el mismo problema sin cambiar el flujo de trabajo que el equipo ya conoce.

Tu próximo paso: cloná un repositorio con un requirements.txt real, corré uv init && uv add -r requirements.txt && uv sync y compará el resultado contra tu entorno actual con uv pip list.

📖 Resumen en Telegram: Ver resumen

Preguntas frecuentes

Qué es el gestor de paquetes uv y quién lo desarrolla?

uv es un gestor de paquetes y entornos de Python escrito en Rust, desarrollado por Astral, la misma empresa detrás del linter Ruff.

uv necesita Python instalado para funcionar?

No. uv se distribuye como binario independiente y puede instalar versiones de Python por su cuenta con uv python install, incluso en una máquina sin ningún intérprete previo.

uv reemplaza completamente a poetry?

Para la mayoría de los flujos de trabajo (agregar dependencias, generar lockfile, publicar a PyPI con uv publish) sí. Equipos con plugins específicos de poetry pueden necesitar migrar esa configuración manualmente.

Todavía necesito pyenv si uso uv?

No debería hacer falta: uv python install y uv python pin cubren instalar y fijar versiones de Python por proyecto, el mismo rol que cumple pyenv.

Cómo migro un proyecto con requirements.txt a uv?

Con uv init para crear el pyproject.toml y uv add -r requirements.txt para importar las dependencias existentes, seguido de uv sync para generar el entorno y el lockfile.

uv sirve para paquetes con dependencias de sistema operativo?

Para paquetes puramente Python o con wheels precompiladas, sí. Para dependencias binarias complejas, como ciertas librerías científicas con dependencias de sistema, conda sigue siendo más adecuado.

Referencias

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.


Andrés Morales

Desarrollador e investigador en inteligencia artificial. Escribe sobre modelos de lenguaje, frameworks, herramientas para devs y lanzamientos open source. Cubre papers de ML, ecosistema de startups tech y tendencias de programación.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.