El módulo runpy ejecuta código Python localizado mediante el sistema de importación o un path y devuelve el namespace global resultante. Implementa parte del comportamiento de comandos como python -m paquete.modulo y resulta útil en launchers, test harnesses, herramientas educativas, wrappers de CLI y sistemas que necesitan ejecutar un módulo como programa.
El módulo no proporciona aislamiento. El código se ejecuta en el proceso actual y puede modificar estado global, importar dependencias, abrir archivos, iniciar threads o terminar el programa. Úsalo solo con código confiable y comprende cómo gestiona variables como __name__, __spec__, __package__ y ciertos valores de sys.
run_module
runpy.run_module(mod_name, ...) localiza un módulo mediante el sistema de importación y ejecuta su código.
import runpy
namespace = runpy.run_module("mi_paquete.modulo")
print(namespace.keys())
El nombre debe ser importable en el entorno actual.
El retorno es un diccionario
Después de ejecutar, el resultado contiene globals definidos por el módulo.
resultado = runpy.run_module("configuracion")
valor = resultado.get("CONFIG")
Los objetos devueltos continúan vivos y pueden referenciar módulos, archivos, locks y otros recursos.
run_name
run_name controla __name__ durante la ejecución.
runpy.run_module(
"mi_paquete.cli",
run_name="__main__",
)
Esto activa bloques if __name__ == "__main__":.
Ejecuta como __main__
Usar run_name="__main__" aproxima python -m, pero el comportamiento exacto de sys.argv y sys.modules depende de otras opciones.
Para una CLI pública, un subprocess con -m suele ser más fiel.
Paquetes y __main__.py
Cuando el nombre corresponde a un paquete, Python puede localizar y ejecutar paquete.__main__.
mi_paquete/
__init__.py
__main__.pyEsta es la estructura convencional de python -m mi_paquete.
alter_sys
Con alter_sys=True, runpy modifica temporalmente estado de sys, como sys.argv[0] y una entrada en sys.modules.
runpy.run_module(
"mi_paquete.cli",
run_name="__main__",
alter_sys=True,
)
Los valores se restauran al finalizar, incluso ante excepción.
alter_sys no es thread-safe
Otras threads pueden observar valores temporales y un módulo parcialmente inicializado.
Evita alter_sys=True cuando threads concurrentes dependen del estado global. Prefiere proceso separado.
init_globals
init_globals proporciona valores iniciales al namespace.
namespace = runpy.run_module(
"informe",
init_globals={"AMBIENTE": "test"},
)
El módulo puede sobrescribir las claves.
init_globals no es seguridad
Un diccionario reducido no impide imports, filesystem, builtins o introspección.
Úsalo para configuración y tests controlados, no como sandbox.
Variables globales especiales
Runpy inicializa __name__, __file__, __cached__, __loader__, __package__ y __spec__.
Estos valores permiten imports relativos y diagnósticos semejantes a una ejecución normal.
Identidad de __spec__
__spec__ describe cómo se encontró y cargó el módulo. En paquetes ejecutables, su nombre real sigue asociado al módulo descubierto aunque cambie __name__.
Usa __spec__.name para identidad importable y __name__ para contexto de ejecución.
run_path
runpy.run_path(path_name, ...) ejecuta código desde un path.
namespace = runpy.run_path("scripts/tarea.py")
El path puede ser un archivo Python o una entrada válida de sys.path con __main__.py.
Directorios ejecutables
Si el path es un directorio, runpy lo hace visible temporalmente y busca __main__.py.
Verifica que contenga el entry point esperado para evitar comportamientos sorprendentes.
Archivos ZIP
Un ZIP compatible puede contener __main__.py y ejecutarse con run_path().
Esto sustenta aplicaciones .pyz. Consulta zipapp en Python.
run_path y run_name
El run name predeterminado de run_path() es especial. Define __main__ cuando necesites comportamiento de programa principal.
runpy.run_path(
"scripts/tarea.py",
run_name="__main__",
)
Diferencia frente a importlib.import_module
importlib.import_module() hace un import normal y registra el módulo en sys.modules. Imports posteriores suelen reutilizar la instancia.
run_module() ejecuta en un namespace nuevo con comportamiento de script.
Ejecución repetida
Llamar runpy dos veces puede repetir efectos de nivel de módulo.
Registros, threads, handlers, escrituras y conexiones pueden duplicarse. Usa código preparado para ese lifecycle o proceso descartable.
Efectos en sys.modules
Según alter_sys, el módulo ejecutado puede no quedar registrado como import normal.
Los imports realizados por el código sí permanecen y modifican el proceso host.
El estado global permanece
El diccionario devuelto no captura todos los efectos. El código puede cambiar otros módulos, logging, locale, señales, cwd y caches.
Descartar el namespace no deshace la ejecución.
Las excepciones se propagan
Los errores del código vuelven al caller.
try:
runpy.run_module("mi_paquete.cli", run_name="__main__")
except SystemExit as error:
print("código de salida", error.code)
Las CLIs suelen llamar sys.exit(), que genera SystemExit.
KeyboardInterrupt
Una interrupción puede cruzar el boundary. Decide si el launcher termina, cancela solo la tarea o traduce el estado.
No captures BaseException sin política explícita.
Argumentos de línea de comandos
Runpy no es una API completa de subprocess. Simular argumentos cambiando sys.argv afecta todo el proceso.
Prefiere llamar una función main(argv) o usar subprocess.
Arquitectura recomendada de CLI
def main(argv=None):
args = parser.parse_args(argv)
return ejecutar(args)
if __name__ == "__main__":
raise SystemExit(main())
Esto simplifica tests y mantiene fino el entry point.
Prueba ejecución como módulo
Runpy puede verificar que un paquete confiable funciona como -m.
resultado = runpy.run_module(
"mi_paquete",
run_name="__main__",
)
Si cambia estado global, lanza procesos o sale, subprocess es más realista.
Usa subprocess para fidelidad
subprocess.run(
[sys.executable, "-m", "mi_paquete"],
check=True,
)
Separa argumentos, módulos, señales, entorno y exit status.
Rendimiento
Las llamadas repetidas pueden reutilizar caches de importación, pero ejecutan de nuevo el cuerpo.
No uses runpy como mecanismo frecuente entre componentes. Importa una función y llámala.
Plugins
Runpy rara vez es la interfaz ideal de plugins. Los plugins deberían exponer APIs, entry points o callables.
Ejecutarlos como scripts completos complica contratos y cleanup.
Herramientas educativas
Una plataforma puede usar runpy en un worker separado para ejemplos preparados.
El código de alumnos o uploads necesita límites de filesystem, red, CPU, memoria y tiempo.
Launchers internos
Un launcher puede mapear comandos aprobados a módulos confiables.
No aceptes directamente cualquier nombre de módulo del usuario.
Valida nombres mediante mapping
COMANDOS = {
"informe": "miapp.comandos.informe",
"limpieza": "miapp.comandos.limpieza",
}
Aplica autorización antes de seleccionar.
Código no confiable
Runpy ejecuta con los privilegios del proceso. No sirve para archivos, snippets o paquetes desconocidos sin aislamiento fuerte.
Usa proceso o container con políticas de acceso y recursos.
Paths no confiables
run_path() puede ejecutar cualquier script accesible. Resuelve targets dentro de una raíz aprobada y rechaza escapes.
Un directorio temporal compartido no es fuente segura sin controles de ownership.
Directorio actual
El código puede depender del cwd. Runpy no crea un contexto aislado.
Usa paths absolutos y APIs de recursos de paquete.
Logging
El módulo ejecutado puede instalar handlers globales y duplicarlos en ejecuciones repetidas.
La aplicación debería ser dueña de la configuración de logging.
Threads y tareas
Threads iniciadas pueden continuar después del retorno.
Un subprocess descartable ofrece cleanup más predecible.
Integración con faulthandler
Para módulos confiables que pueden bloquearse, activa faulthandler en Python en el worker y aplica deadline.
Un dump antes de terminar revela la ubicación bloqueada.
Aplicaciones empaquetadas
Los ejecutables frozen pueden implementar ejecución de módulos de forma específica y requerir inclusión explícita.
Prueba el artefacto final.
__cached__ y bytecode
El resultado puede contener el path de cache asociado.
No asumas que existe o puede distribuirse; el bytecode depende de versión.
Observabilidad
Registra módulo lógico, versión, duración, resultado y código traducido. Evita registrar el namespace completo.
Puede contener secretos y objetos con representaciones costosas.
Pruebas de concurrencia
Si usas runpy con threads activas, prueba cambios temporales de sys, imports, logging y señales.
Para ejecución independiente, subprocess sigue siendo la arquitectura más segura.
Errores comunes
Los fallos frecuentes son confundir runpy con import normal, usar alter_sys=True en programas multithread, asumir rollback, simular argv globalmente, aceptar nombres o paths arbitrarios, esperar cleanup completo y usar runpy como sandbox.
Conclusión
runpy ejecuta módulos y scripts mediante la infraestructura de Python y devuelve el namespace. Usa run_module() para nombres importables, run_path() para paths y run_name="__main__" para comportamiento de entry point.
Prefiere subprocess cuando importen aislamiento, argumentos reales y códigos de salida. Consulta la documentación oficial de runpy y pkgutil en Python para descubrir módulos antes de seleccionarlos.







