Especificaciones de configuración
Este documento define el esquema y el comportamiento de vnma.toml (nivel de proyecto) y vnma_config.toml (nivel de paquete).
1. Configuración del proyecto (vnma.toml)
El vnma.toml raíz declara las dependencias del proyecto, los ajustes de vendor y las sobrescrituras específicas por paquete.
Ejemplo completo
[project]
name = "my-application"
version = "0.1.0"
[dependencies]
# Restricciones estándar de PyPI:
colorama = "==0.4.6"
pygame-ce = ">=2.5.0, <2.6.0"
# Dependencia de directorio de código local:
my-internal-tool = { path = "./packages/my-internal-tool" }
# Dependencia de repositorio Git:
custom-fork = { git = "https://github.com/user/fork.git", branch = "main" }
[dev-dependencies]
pytest = ">=8.0.0"
[tool.vnma]
vendor_dir = "vendor"
ide_integration = ["vscode", "pycharm"]
# Sobrescrituras explícitas para paquetes de terceros:
[package.unusual-package]
dll_directories = ["unusual_package/libs"]
entry_points = { run-tool = "unusual_package.cli:run" }
Campos
[project]: metadatos básicos del proyecto (name,version).[dependencies]: mapa de nombres de paquete a restricciones de versión, rutas del sistema de archivos o repositorios Git.[tool.vnma]:vendor_dir: nombre del directorio de vendoring (vendorpor defecto; se ajusta automáticamente avendor_vnmasivendor/ya está ocupado).ide_integration: lista de IDE a sincronizar (["vscode", "pycharm"]).
[package.<name>]: sobrescrituras aplicadas a paquetes específicos durante la instalación.
2. Configuración a nivel de paquete (vnma_config.toml)
vnma_config.toml es un manifiesto explícito y opcional que se coloca en el directorio raíz de tu biblioteca (o repositorio). Su propósito es eliminar las suposiciones heurísticas ("cero magia") cuando vnma construye e instala paquetes desde repositorios de GitHub, directorios de código locales o subproyectos de un monorepo.
Cuando existe vnma_config.toml, vnma da prioridad a sus declaraciones sobre las inspecciones automáticas del sistema de archivos.
Esquema completo
[package]
name = "my-library" # Identificador de distribución; puede contener guiones
package = "my_library" # Nombre de import de Python: import my_library
version = "0.1.0"
type = "pure-python" # "pure-python" | "native" | "mixed"
[runtime]
# Rutas relativas dentro del paquete que contienen bibliotecas dinámicas (.dll, .so, .dylib)
# vnma las registra automáticamente con os.add_dll_directory() en Windows al ejecutar:
dll_directories = [
"my_library/libs",
"my_library/vendor_bin",
]
# Fuerza el aislamiento hermético al cargar este paquete (filtra site-packages externos):
hermetic = true
[exports]
# Directorios de módulos de nivel superior a exponer en sys.path:
modules = ["my_library"]
# Puntos de entrada de línea de comandos expuestos como wrappers ejecutables en vendor/.bin/:
entry_points = {
my-tool = "my_library.cli:main",
runner = "my_library.__main__:main"
}
Referencia de campos
| Sección | Campo | Tipo | Descripción |
|---|---|---|---|
[package] |
name |
string | Identificador de distribución (normalizado según PEP 503). No tiene que coincidir con el nombre del módulo importado. |
[package] |
package |
string | Nombre de import oficial de Python, p. ej. my_lib cuando la distribución se llama my-lib-framework. Debe usar identificadores de Python válidos separados por puntos. |
[package] |
packname |
string | Alias heredado de package (soportado para manifiestos existentes). package tiene prioridad si ambos están definidos. |
[package] |
version |
string | Versión de lanzamiento del paquete (p. ej. "1.0.0"). Reemplaza el versionado de respaldo. |
[package] |
type |
string | Clasificación del paquete: "pure-python" (por defecto), "native" o "mixed". |
[runtime] |
dll_directories |
lista de strings | Subdirectorios con objetos compartidos nativos (.dll, .so, .dylib). Se inyectan en _DLL_SUBDIRS de __bootstrap__.py. |
[runtime] |
hermetic |
boolean | Por defecto true. Si está activo, impide el respaldo en tiempo de ejecución al site-packages global de Python. |
[exports] |
modules |
lista de strings | Paquetes o módulos raíz de Python exportados por esta distribución. |
[exports] |
entry_points |
tabla / mapa | Mapa de nombre de binario a callable de Python module.submodule:function. Los wrappers se escriben en .bin/. |
[dependencies] |
nombre | string / tabla | Dependencias de este paquete: una restricción de PyPI, { git = ..., branch = ... } o { path = ... }. Se instalan junto con el paquete (consulta Dependencias anidadas). |
Nombre de distribución vs. nombre de import
Un nombre de distribución puede diferir del nombre que se importa. Por ejemplo, un manifiesto puede usar un nombre de distribución como my-lib-framework2.0.0Beta, el campo heredado packname = "my_lib", [exports] modules = ["my_lib"] y un punto de entrada para my_lib.__main__. VNMA lo sigue aceptando sin cambios. Los manifiestos nuevos deberían expresar el mismo contrato de forma explícita:
[package]
name = "my-lib-framework2.0.0Beta"
package = "my_lib"
version = "2.0.0b1"
type = "pure-python"
[runtime]
dll_directories = []
hermetic = true
[exports]
modules = ["my_lib"]
[exports.entry_points]
my-lib = "my_lib.__main__:main"
name sigue siendo la identidad de dependencia/distribución que usan el manifiesto y la resolución de VNMA; package es la identidad de import de Python, validada contra los módulos del wheel instalado. No es un renombrado de my_lib al nombre de distribución.
Las dependencias locales editables ({ path = "...", editable = true }) y las dependencias de rama de Git ({ git = "...", branch = "main" }) están soportadas en el manifiesto raíz.
Instalación atómica en 0.3.0
vnma install, update, link y reload preparan sus operaciones sobre vendor en una carpeta hermana temporal y publican el árbol vendor completo solo si todo sale bien. La eliminación de paquetes y prune desde la CLI también preparan las modificaciones. Los comandos fallidos restauran el vendor, la configuración del proyecto y el lockfile anteriores. Un journal permite la recuperación tras la caída de un proceso, con un bloqueo exclusivo contra mutaciones concurrentes. Los wrappers de CLI generados usan rutas seguras ante reubicación.
Limitaciones: la preparación por copia del árbol completo puede requerir espacio extra en disco (aproximadamente otro árbol vendor), y en Windows los dos renombrados son recuperables pero no una primitiva atómica de intercambio de directorios. La purga genérica de todo el espacio de trabajo uninstall --all sigue siendo una operación destructiva explícita, no una transacción reversible.
Casos de uso reales
Caso A: biblioteca de Python puro con herramientas CLI
Si tu biblioteca de GitHub ofrece una herramienta de línea de comandos:
[package]
name = "devkit"
version = "1.2.0"
type = "pure-python"
[exports]
entry_points = { devkit = "devkit.cli:main" }
Al instalar con vnma install git+https://github.com/org/devkit.git@main, vnma:
- Compila el paquete en
vendor/devkit/. - Genera automáticamente
vendor/.vnma_meta/.bin/devkit.cmd(y.bat). - Permite ejecutar
vnma exec devkit --helpde inmediato.
Caso B: biblioteca con binarios nativos compilados (C / C++ / Rust / SDL2)
Si tu biblioteca incluye extensiones C compiladas o DLL de terceros:
[package]
name = "fast-engine"
version = "2.0.0"
type = "native"
[runtime]
dll_directories = ["fast_engine/native_libs"]
Al instalarla, vnma:
- Omite el escaneo heurístico de extensiones binarias.
- Inyecta
"fast_engine/native_libs"en el registro estático de DLL de complejidad $O(1)$ devendor/__bootstrap__.py. - Garantiza que las llamadas a la API Win32 de Windows (
os.add_dll_directory) mantengan los handles de las DLL de forma persistente en memoria.
Jerarquía de precedencia
Para determinar cómo se comporta un paquete, vnma resuelve las configuraciones en este orden estricto:
1. Sobrescrituras del proyecto ([package.<name>] en el vnma.toml raíz)
↓
2. Manifiesto del paquete (vnma_config.toml en el repositorio de la biblioteca)
↓
3. Estándares PEP 517 / Wheel (.dist-info/entry_points.txt, METADATA, RECORD)
↓
4. Heurísticas automáticas (escáneres de extensión de archivo: .pyd, .so, .dll)