El Entorno y Modelo de Resolución de Python
Cada herramienta cubierta en esta sección — venv, uv, Poetry, PDM — está resolviendo un problema subyacente: decidir qué archivos en el disco carga realmente una declaración de import dada, y hacerlo de manera consistente entre máquinas.
Conceptos Básicos de Entornos y Empaquetado repasa los comandos prácticos para crear un entorno e instalar un paquete.
Esta página se mantiene un nivel por encima: las mecánicas de la ruta de búsqueda que hacen posible el aislamiento en primer lugar, y el modelo de resolución que convierte un pyproject.toml en un conjunto concreto y reproducible de paquetes instalados.
Comprender este modelo es lo que convierte "por qué instalar un paquete rompió una importación no relacionada" de un misterio a un resultado esperado y diagnosticable. También es la base sobre la que se construyen Resolución de Dependencias y Lockfiles, pyproject.toml Explicado y uv - La Herramienta Rápida.
Resumen
- Una declaración de
importse resuelve recorriendo una lista ordenada de directorios (sys.path); un entorno virtual funciona cambiando lo que contiene esa lista, no encapsulando el intérprete en sí mismo. - Por Qué Importa: La mayoría de los errores de "se importó la versión incorrecta del paquete" y "funciona en mi máquina" se remontan a una brecha entre la ruta de búsqueda del intérprete y lo que un desarrollador asume que está instalado; comprender el orden de búsqueda cierra esa brecha.
- Conceptos Clave:
sys.path, site-packages, el sistema de importación, resolución de dependencias, el lockfile, metadatos PEP 621. - Cuándo Usar Este Modelo: Depurar por qué se importó la versión incorrecta de un paquete, elegir entre
uv, Poetry y PDM para un equipo, razonar sobre instalaciones editables y comprender por qué CI necesita un lockfile incluso cuandopyproject.tomlno ha cambiado. - Limitaciones / Compensaciones: Los rangos de versiones expresan una intención, no una garantía; una dependencia aún puede publicar una versión técnicamente compatible que rompa tu código, y ningún lockfile protege contra un paquete que fue comprometido desde el momento en que se publicó.
- Temas Relacionados: el sistema de importación de módulos, empaquetado y publicación, índices de paquetes privados, reproducibilidad en CI.
Fundamentos
Un intérprete de Python no sabe, al inicio, dónde viven las dependencias de tu proyecto.
Cada vez que el código ejecuta import httpx, el intérprete recorre una lista ordenada de directorios llamada sys.path, comprobando cada uno en orden en busca de un módulo o paquete coincidente, y se detiene en la primera coincidencia que encuentra.
Esa lista se construye a partir de varias fuentes: el directorio que contiene el script que se está ejecutando, la variable de entorno PYTHONPATH si está configurada, y un conjunto de ubicaciones de bibliotecas estándar y de terceros; la más importante, para este tema, es site-packages, el directorio donde pip y sus pares copian realmente los paquetes instalados.
Un entorno virtual se entiende mejor como un pequeño truco aplicado a ese orden de búsqueda en lugar de como una caja de arena alrededor del propio binario del intérprete.
Ejecutar python -m venv .venv crea un nuevo directorio con su propia carpeta site-packages y una copia (o enlace simbólico) del intérprete; activarlo ajusta sys.path y PATH de la shell para que python y pip dentro de esa shell apunten primero al site-packages local del proyecto.
Por lo tanto, dos proyectos pueden depender cada uno de una versión diferente del mismo paquete sin ningún conflicto, porque cada uno tiene su propio directorio site-packages y el intérprete nunca necesita reconciliar los dos.
Una analogía útil: sys.path es el orden de búsqueda de un bibliotecario en varias sucursales del mismo sistema de bibliotecas.
Pides un libro y el bibliotecario revisa primero la sucursal más cercana (el site-packages del proyecto), luego sucursales más amplias (la biblioteca estándar, cualquier instalación global), devolviendo la copia que se encuentre primero, no necesariamente la que esperabas si dos sucursales tienen ediciones diferentes.
import sys
print(sys.path[:3])
# ['', '/home/dev/myapp/.venv/lib/python3.14/site-packages', ...]Mecánicas e Interacciones
Instalar un paquete con pip, uv o cualquier otro instalador es, debajo de la ceremonia de la línea de comandos, un acto de dos pasos: obtener metadatos y un artefacto de distribución (una wheel, generalmente) de un índice de paquetes como PyPI, luego desempaquetar los archivos de esa wheel en el site-packages del entorno activo.
Nada más misterioso sucede; no hay un registro separado de "qué está instalado" más allá de los archivos que existen físicamente en ese directorio, que es exactamente por qué eliminar un .venv y recrearlo es una forma completamente segura de empezar de nuevo.
La resolución de dependencias es el problema más difícil que se asienta sobre ese mecanismo: un proyecto rara vez declara solo paquetes sin subdependencias, por lo que la herramienta debe leer cada rango de versiones declarado —tanto las dependencias directas del proyecto como las dependencias transitivas de cada dependencia— y encontrar un conjunto consistente de versiones que satisfaga a todas ellas simultáneamente.
pyproject.toml expresa esa intención como rangos (httpx>=0.28,<1), mientras que un lockfile (uv.lock, poetry.lock) registra las versiones exactas que el resolvedor eligió la última vez que se ejecutó, incluyendo paquetes transitivos que el desarrollador nunca nombró explícitamente.
Esa separación —los rangos expresan lo que es aceptable, el lockfile registra lo que se eligió realmente— es el modelo mental más importante en toda esta sección, porque explica por qué uv sync --frozen en CI se comporta de manera completamente diferente a un uv sync básico: el primero se niega a tocar el lockfile e instala exactamente lo que dice, el segundo está dispuesto a volver a resolver si cambió pyproject.toml.
Una instalación editable (pip install -e . o uv pip install -e .) modifica esta imagen de una manera específica: en lugar de copiar los archivos del paquete a site-packages, deja un pequeño puntero (históricamente un archivo .pth, ahora más a menudo un hook finder ligero) que le dice al sistema de importación que busque directamente en el directorio fuente.
Es por eso que editar un archivo en src/myapp/ tiene efecto en la próxima import sin reinstalar; la ubicación "instalada" resuelta y la copia de trabajo del desarrollador son, deliberadamente, los mismos archivos.
Consideraciones Avanzadas y Aplicaciones
Tres herramientas dominan este espacio hoy en día — uv, Poetry y PDM — y vale la pena ser preciso sobre lo que realmente difiere entre ellas, porque no es el problema de resolución en sí.
Las tres leen los metadatos de pyproject.toml (en gran medida estandarizados por PEP 621), construyen un grafo de dependencias y escriben un lockfile; donde divergen es en la velocidad de implementación del resolvedor, el formato del lockfile y el flujo de trabajo circundante (grupos de dependencias, soporte para monorepos/workspaces, comandos de publicación).
uv, escrito en Rust, resuelve e instala un orden de magnitud más rápido que las herramientas basadas en pip en la mayoría de los proyectos, que es la razón principal por la que se ha convertido en la recomendación predeterminada para nuevos proyectos de Python 3.14.
Los índices de paquetes privados complican el modelo de resolución de una manera que vale la pena nombrar explícitamente: cuando un resolvedor apunta a un índice interno (a través de UV_INDEX_URL o [[tool.uv.index]]), todavía realiza el mismo algoritmo de resolución de grafos, solo que contra metadatos diferentes; el riesgo es que un índice mal configurado pueda servir silenciosamente un paquete diferente (o malicioso) bajo un nombre que sombrea uno público, razón por la cual las credenciales y la configuración del índice merecen el mismo escrutinio que las dependencias mismas.
Reproducir un entorno "desde cero" —la prueba que toda configuración de empaquetado debería pasar— es realmente una afirmación sobre la completitud del lockfile: si uv sync --frozen (o el equivalente de Poetry/PDM) en una instalación limpia produce una instalación funcional sin acceder a la red para nada más que para obtener artefactos fijados, el lockfile está haciendo su trabajo.
| Herramienta | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
venv + pip | Instalación adicional cero, viene con Python | Sin lockfile incorporado o resolvedor rápido | Scripts pequeños, enseñanza, dependencias mínimas |
uv | Resolución/instalación muy rápida, binario único, reemplazo directo de pip | Herramienta más nueva, ecosistema de plugins más pequeño que pip | Opción predeterminada para nuevos proyectos de Python 3.14 |
| Poetry | Flujo de trabajo maduro, grupos de dependencias, herramientas de publicación | Históricamente resolvedor más lento que uv | Equipos ya invertidos en las convenciones de proyecto de Poetry |
| PDM | PEP 582/621-forward, backends flexibles | Comunidad más pequeña que Poetry | Equipos que desean herramientas centradas en estándares sin dependencia del proveedor |
Conceptos Erróneos Comunes
- "Un entorno virtual aísla el propio intérprete." Aísla
site-packagesy ajustasys.path; el mismo binario del intérprete (o una copia del mismo) todavía se ejecuta; nada sobre el tiempo de ejecución del lenguaje cambia entre entornos. - "
pyproject.tomlme dice exactamente qué está instalado." Solo declara rangos aceptables; el lockfile registra lo que se resolvió realmente, y eso es en lo que confía una instalación reproducible. - "Un rango de careto o versión compatible nunca puede romper mi código." Puede hacerlo, siempre que un editor de versiones no siga correctamente el versionado semántico; el rango expresa una expectativa, no un contrato forzado.
- "Las instalaciones editables son solo una conveniencia sin una diferencia real con una instalación normal." Cambian completamente dónde busca el sistema de importación el paquete, apuntando al árbol fuente de trabajo en lugar de a un artefacto copiado, que es exactamente por qué importan para el desarrollo local y los monorepos.
- "Herramientas más rápidas como
uvsignifican un algoritmo de resolución fundamentalmente diferente." El problema de resolución —encontrar un conjunto de versiones consistente en un grafo de dependencias— es el mismo; la ventaja deuves un resolvedor e instalador mucho más rápido basado en Rust, no un modelo diferente.
Preguntas Frecuentes
¿Qué sucede realmente cuando ejecuto `import nombre_paquete`?
El intérprete recorre sys.path en orden, comprobando cada directorio en busca de un módulo o paquete coincidente, y se detiene en la primera coincidencia, generalmente el site-packages del entorno activo.
¿Cuál es la diferencia práctica entre un entorno virtual e instalar paquetes globalmente?
Un entorno virtual le da a un proyecto su propio directorio site-packages insertado en sys.path, por lo que sus dependencias nunca entran en conflicto con las de otro proyecto; una instalación global comparte un único site-packages en todos los scripts de la máquina.
¿Por qué `pip install algún-paquete` a veces instala una versión diferente a la que espero?
El resolvedor está satisfaciendo un rango, no una versión específica; si el rango permite una versión más reciente y se publicó una desde tu última instalación, la resolución puede elegirla legítimamente a menos que un lockfile fije la versión exacta.
¿Cuál es el propósito de un lockfile si `pyproject.toml` ya enumera mis dependencias?
pyproject.toml declara lo que es aceptable; el lockfile declara lo que se eligió realmente, incluyendo todas las dependencias transitivas que nunca nombraste directamente; esa es la única forma de garantizar que dos máquinas instalen código idéntico.
¿Por qué `uv sync --frozen` falla a veces cuando `uv sync` solo tendría éxito?
--frozen se niega a volver a resolver o tocar el lockfile en absoluto, por lo que si pyproject.toml ha cambiado desde la última generación del lockfile, falla ruidosamente en lugar de derivar silenciosamente; esa rigidez es exactamente lo que lo hace seguro para CI.
¿Una instalación editable copia mi código a `site-packages`?
No, instala un pequeño puntero que redirige el sistema de importación a tu directorio fuente de trabajo, por lo que las ediciones surten efecto inmediatamente sin reinstalar.
¿Por qué `uv` es más rápido que pip para el mismo problema de resolución?
Es una implementación desde cero en Rust con un resolvedor mucho más rápido y una caché de contenido global, no un algoritmo de resolución diferente; el problema subyacente de satisfacción del grafo que resuelve es el mismo que resuelven pip y Poetry.
¿Pueden dos proyectos de Python diferentes en la misma máquina depender de versiones incompatibles del mismo paquete?
Sí, sin ningún conflicto; debido a que el entorno virtual de cada proyecto tiene su propio site-packages, el intérprete nunca tiene que reconciliar las dos instalaciones entre sí.
¿Cambia un índice de paquetes privado la forma en que funciona la resolución?
No, el resolvedor todavía construye y satisface el mismo grafo de dependencias; simplemente lee los metadatos del paquete de un índice diferente (o adicional), razón por la cual los índices privados mal configurados son una preocupación de seguridad real, no solo una inconveniencia de red.
¿Sigue siendo relevante `PYTHONPATH` con herramientas modernas como `uv`?
Rara vez se configura manualmente en proyectos que usan uv, Poetry o PDM, ya que el mecanismo de entorno virtual ya ajusta sys.path correctamente; PYTHONPATH es relevante ahora principalmente para casos especializados como scripts ad-hoc fuera de la estructura del proyecto.
¿Por qué los equipos se estandarizan en un formato de lockfile en lugar de mezclar herramientas?
El formato del lockfile y el comportamiento del resolvedor de cada herramienta difieren ligeramente, y confirmar más de uno (por ejemplo, tanto uv.lock como poetry.lock) invita a la deriva entre ellos; solo el resultado de resolución de una herramienta debe considerarse la fuente de verdad por proyecto.
¿El fijado de versiones en el lockfile protege contra una actualización maliciosa de un paquete?
Protege contra que un paquete sea intercambiado o alterado silenciosamente después del hecho, ya que el lockfile puede registrar hashes de integridad; no hace nada contra un paquete que ya era malicioso o comprometido en la versión que fijaste.
Relacionados
- Conceptos Básicos de Entornos y Empaquetado - los comandos prácticos que este modelo explica
- uv - La Herramienta Rápida - el resolvedor/instalador rápido construido sobre este modelo
- Poetry & PDM - flujos de trabajo alternativos que resuelven el mismo problema de resolución
- pyproject.toml Explicado - dónde se declaran los rangos de versiones en este modelo
- Resolución de Dependencias y Lockfiles - el algoritmo de resolución y las estrategias de fijación en profundidad
- Instalaciones Editables y Paquetes Locales - cómo funciona el mecanismo de puntero de importación en la práctica
Versiones de la Pila: Esta página fue escrita para Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ y uv 0.6+.