runpy en Python: ejecuta módulos

Publicado el: 13/08/2026
Tempo de leitura: 6 minutos
Código en ejecución que representa módulos y rutas ejecutados con runpy en Python

El módulo runpy en Python localiza y ejecuta código mediante el sistema de módulos. Implementa parte del comportamiento de python -m paquete.modulo y también puede ejecutar scripts, directorios y archivos ZIP que contengan un __main__.py en la raíz.

Es útil en launchers, pruebas, automatizaciones y sistemas que necesitan ejecutar un entry point y recibir el diccionario global resultante. No es un sandbox. El código se ejecuta en el proceso actual y puede modificar archivos, red, variables de entorno, caches de import, logging, señales, threads y otros estados globales.

Ejecutar un módulo por nombre

run_module() recibe un nombre absoluto y localiza el código con el mecanismo normal de imports.

import runpy

resultado = runpy.run_module('mi_app.diagnostico')
print(resultado.keys())

El código se ejecuta en un namespace nuevo y la función devuelve ese diccionario. Esto difiere de un import normal, que crea o reutiliza una entrada persistente en sys.modules.

Ejecutar un paquete

Cuando el nombre corresponde a un paquete, run_module() busca y ejecuta paquete.__main__.

resultado = runpy.run_module('mi_app')
# ejecuta mi_app.__main__

Esto reproduce el núcleo de python -m mi_app. El paquete padre puede importarse durante la búsqueda, por lo que su inicializador puede producir efectos.

Variables globales especiales

Antes de ejecutar, runpy define __name__, __spec__, __file__, __cached__, __loader__ y __package__.

resultado = runpy.run_module(
    'mi_app.diagnostico',
    run_name='__main__',
)
print(resultado['__name__'])

__spec__.name continúa identificando el módulo real aunque cambie run_name. Desde Python 3.12, la asignación directa de algunos valores heredados está deprecada; conviene consultar la especificación del módulo.

Preconfigurar el namespace

init_globals proporciona valores iniciales sin modificar el mapping original.

contexto = {
    'CONFIGURACION': {'modo': 'test'},
    'SERVICIO': servicio_falso,
}

resultado = runpy.run_module(
    'mi_app.tarea',
    init_globals=contexto,
)

Los nombres especiales controlados por runpy sustituyen valores coincidentes. Entregar objetos poderosos al código también amplía sus capacidades.

Activar comportamiento de script

Muchos módulos contienen:

if __name__ == '__main__':
    main()

Configura run_name='__main__' para ejecutar ese bloque.

runpy.run_module(
    'mi_app.cli',
    run_name='__main__',
)

El módulo se comporta como script, pero permanece dentro del proceso. Las funciones y clases del diccionario devuelto no tienen garantía de funcionar correctamente después del retorno; usa import normal cuando quieras reutilizar APIs.

alter_sys y compatibilidad con -m

Con alter_sys=True, run_module() cambia temporalmente sys.argv[0] e instala un módulo temporal en sys.modules.

resultado = runpy.run_module(
    'mi_app.cli',
    run_name='__main__',
    alter_sys=True,
)

Esto se aproxima a la CLI, pero no es thread-safe. Otros threads pueden observar argumentos modificados o un módulo parcialmente inicializado. En servidores multithread, deja alter_sys=False o usa otro proceso.

Ejecutar una ruta con run_path()

run_path() ejecuta código en una ubicación del sistema de archivos.

import runpy

resultado = runpy.run_path('scripts/informe.py')
print(resultado.get('RESULTADO'))

La ruta puede apuntar a código fuente, bytecode o una entrada válida de sys.path, como un directorio o ZIP con __main__.py.

Ejecutar directorios y ZIPs

Para una entrada de ruta, runpy la añade temporalmente al principio de sys.path y busca __main__.

runpy.run_path('dist/herramienta.pyz', run_name='__main__')

Esto funciona con archivos creados por zipapp en Python. Valida el archivo: si no contiene __main__, la búsqueda puede encontrar otro módulo de ese nombre en sys.path.

run_path altera sys obligatoriamente

Ejecutar un directorio o ZIP requiere cambios temporales en sys.path, sys.argv[0] y sys.modules. Los valores se restauran, pero otros threads pueden ver el estado intermedio.

Serializa llamadas o, preferiblemente, usa un subprocesso. Un proceso separado también proporciona timeout y aislamiento de fallos.

runpy no es sandbox

El código ejecutado tiene acceso normal a Python y al sistema operativo.

from pathlib import Path
Path('/tmp/marcador').write_text('ejecutado')

Eliminar algunos nombres de init_globals no aísla el código. Para código de terceros, usa proceso o container desechable, usuario sin privilegios, sistema de archivos limitado, red bloqueada y cuotas.

Efectos persistentes

El namespace principal es nuevo, pero los imports realizados quedan en sys.modules. Cambios de entorno, handlers, threads de fondo, señales y estado de bibliotecas también pueden permanecer.

Cuando necesitas ejecución limpia y repetible, una frontera de proceso es más fiable que intentar revertir cada efecto.

runpy o importlib

Usa runpy para ejecutar un entry point con semántica de script. Utiliza importlib.import_module() cuando quieras un módulo y acceso soportado a sus funciones y clases.

La documentación advierte que las definiciones creadas por runpy pueden no funcionar después. Para descubrir imports estáticamente, consulta modulefinder en Python.

runpy o subprocess

Runpy es rápido y comparte memoria, pero también comparte fallos y estado global. Un subprocesso ofrece vector de argumentos, código de salida, timeout, captura de salida y aislamiento.

import subprocess
import sys

subprocess.run(
    [sys.executable, '-m', 'mi_app.cli'],
    check=True,
    timeout=30,
)

Prefiere subprocess para plugins no confiables, tareas largas, ejecución concurrente o código que pueda terminar el intérprete.

Leer resultados del namespace

El diccionario devuelto puede incluir valores producidos por el script.

resultado = runpy.run_path(
    'calculo.py',
    init_globals={'ENTRADA': 21},
)
print(resultado['SALIDA'])

Define un contrato pequeño, con nombres y tipos esperados. No serialices todo el namespace: puede contener módulos, funciones, archivos y objetos sensibles.

Tratar SystemExit

El código puede llamar sys.exit(), que genera SystemExit.

try:
    runpy.run_module('mi_app.cli', run_name='__main__')
except SystemExit as salida:
    codigo = salida.code

Decide si propagar el código, convertirlo en resultado o registrarlo como fallo. Trata también KeyboardInterrupt en el límite exterior.

Excepciones y diagnóstico

Los errores de import, sintaxis y runtime se propagan. Registra módulo o ruta, versión de Python y argumentos sanitizados.

Usa las técnicas de traceback en Python para diagnósticos internos, sin mostrar detalles sensibles al usuario.

Launchers controlados

Una herramienta interna puede mapear acciones amigables a módulos aprobados.

TAREAS = {
    'migrar': 'mi_app.migraciones.aplicar',
    'verificar': 'mi_app.diagnostico',
}

modulo = TAREAS[accion]
runpy.run_module(modulo, run_name='__main__')

Usa una allowlist. No aceptes cualquier nombre importable o ruta del usuario.

Concurrencia

Incluso con alter_sys=False, el código puede modificar estado global. Con alter_sys=True o run_path(), el riesgo aumenta.

En aplicaciones asíncronas, no ejecutes tareas pesadas en el event loop. Usa workers de proceso y limita trabajos simultáneos.

Probar una capa basada en runpy

Crea scripts temporales pequeños.

def test_run_path(tmp_path):
    script = tmp_path / 'job.py'
    script.write_text('SALIDA = ENTRADA * 2\n', encoding='utf-8')

    resultado = runpy.run_path(
        str(script),
        init_globals={'ENTRADA': 5},
    )
    assert resultado['SALIDA'] == 10

Prueba SystemExit, módulos ausentes, excepciones, ZIPs sin __main__, cambios globales y llamadas concurrentes.

Relación con code y codeop

code en Python crea REPLs persistentes. Codeop detecta entrada interactiva incompleta. Runpy ejecuta unidades completas por nombre o ruta.

Elige la abstracción correcta. No uses runpy como sandbox interactivo improvisado.

Errores frecuentes

  • Tratar runpy como sandbox.
  • Usar alter_sys=True en código multithread.
  • Ejecutar una ruta no validada.
  • Suponer que todos los efectos se revierten.
  • Reutilizar definiciones sin garantía.
  • Aceptar cualquier nombre de módulo.
  • Ignorar SystemExit y timeouts.

Buenas prácticas

  • Usa allowlists de módulos y rutas.
  • Prefiere importlib para APIs reutilizables.
  • Prefiere subprocess para aislamiento.
  • Evita cambios de sys en threads.
  • Valida ZIPs y __main__.py.
  • Define un contrato mínimo de resultados.
  • Registra fallos sin secretos.

Conclusión

runpy en Python ejecuta módulos y rutas con semántica cercana a python -m y a los scripts. Soporta paquetes, directorios, ZIPs y devuelve los globals resultantes.

Úsalo solo para código confiable en el proceso actual. Para concurrencia, aislamiento y seguridad, prefiere subprocessos. Consulta la documentación oficial de runpy y la documentación de la opción -m.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Paquete de software que representa descubrimiento de módulos con pkgutil en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pkgutil en Python: descubre paquetes

    Aprende pkgutil en Python para descubrir módulos, recorrer paquetes, resolver objetos, extender rutas y acceder a recursos con seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    13/08/2026
    Red de código binario que representa el grafo de imports analizado con modulefinder en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    modulefinder en Python: analiza imports

    Aprende modulefinder en Python para mapear imports, detectar módulos ausentes, personalizar rutas y auditar dependencias con límites claros.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Archivadores organizados que representan aplicaciones empaquetadas en archivos .pyz con zipapp en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zipapp en Python: crea ejecutables .pyz

    Aprende zipapp en Python para empaquetar aplicaciones en archivos .pyz, definir entry points, incluir dependencias y distribuir con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Editor de código que representa autocompletado de REPL con rlcompleter en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    rlcompleter en Python: autocompletar REPL

    Aprende rlcompleter en Python para añadir autocompletado a REPLs, consolas y editores, controlar namespaces y evitar efectos secundarios.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Ventana de terminal que representa una consola interactiva creada con cmd en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    cmd en Python: crea consolas interactivas

    Aprende cmd en Python para crear consolas interactivas con comandos, ayuda, historial, autocompletado, pruebas y control seguro de acciones.

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Terminal interactivo que representa un REPL personalizado creado con el módulo code en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    code en Python: crea un REPL personalizado

    Aprende el módulo code en Python para crear REPLs personalizados, controlar namespaces, prompts, salida, bloques incompletos y cierre local.

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026