runpy en Python: ejecuta módulos y scripts

Publicado el: 27/08/2026
Tempo de leitura: 6 minutos
A person typing on a laptop with a Python programming book visible, capturing technology and learning.

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__.py

Esta 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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Close-up of a python snake coiled in darkness, showcasing its scales and eyes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pkgutil en Python: descubre paquetes

    Aprende pkgutil en Python para listar módulos, recorrer paquetes, descubrir plugins, consultar importers y leer recursos con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Colorful stacked shipping containers at Hamburg port, showcasing global trade and logistics.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    modulefinder en Python: descubre imports

    Aprende modulefinder en Python para descubrir imports, dependencias transitivas, módulos ausentes, paths, plugins y límites del análisis estático.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Person sorting documents in folders outdoors, hands visible, neutral tone.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    py_compile en Python: compila un archivo

    Aprende py_compile en Python para compilar un archivo, controlar .pyc, filenames lógicos, optimización, invalidación por hash y errores.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compileall en Python: genera bytecode .pyc

    Aprende compileall en Python para generar .pyc, validar sintaxis, compilar en paralelo y controlar optimización, paths y builds reproducibles.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Vivid close-up of code on a computer screen showcasing programming details.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    codeop en Python: compila entrada interactiva

    Aprende codeop en Python para detectar comandos completos, incompletos o inválidos, crear REPLs y conservar flags de __future__ de forma

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    linecache en Python: lee líneas del código

    Aprende linecache en Python para recuperar líneas de código, actualizar cache, soportar tracebacks y loaders, conservar indentación y proteger rutas.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026