El módulo pyclbr en Python lee código fuente y extrae información limitada sobre clases, funciones, métodos y definiciones anidadas sin importar el módulo analizado. Fue diseñado para ofrecer datos suficientes a navegadores de código, índices de símbolos, generadores de documentación sencillos y exploradores de proyectos.
La ventaja principal es evitar la ejecución del archivo. Importar un módulo puede abrir conexiones, leer variables de entorno, registrar plugins o ejecutar código en el nivel superior. pyclbr trabaja directamente con la fuente, por lo que resulta más apropiado para examinar código desconocido. Su información es parcial y no sustituye un análisis completo con AST.
Qué encuentra pyclbr
La API moderna, readmodule_ex(), devuelve un diccionario con descriptores de funciones y clases declaradas mediante def, async def y class. Cada descriptor incluye nombre, archivo, módulo, línea inicial, padre e hijos.
import pyclbr
simbolos = pyclbr.readmodule_ex("mi_paquete.servicio")
for nombre, descriptor in simbolos.items():
if nombre == "__path__":
continue
print(nombre, descriptor.file, descriptor.lineno)El nombre utiliza notación de importación, no una ruta arbitraria de archivo.
Proporciona rutas de búsqueda
El argumento path añade directorios antes de sys.path mientras se localiza la fuente.
simbolos = pyclbr.readmodule_ex(
"aplicacion.modulo",
path=["/workspace/src"],
)Resuelve y limita estas rutas a raíces aprobadas. Una interfaz web no debe permitir que una persona externa inspeccione cualquier directorio del sistema.
readmodule y readmodule_ex
readmodule() es la interfaz histórica y devuelve solo clases del nivel del módulo. readmodule_ex() añade funciones, clases y definiciones anidadas, por lo que debe preferirse en código nuevo.
solo_clases = pyclbr.readmodule("mi_modulo")
completo = pyclbr.readmodule_ex("mi_modulo")Herramientas antiguas pueden depender del formato original, pero la versión extendida ofrece un árbol de símbolos mucho más útil.
Descriptores de función
Los objetos Function exponen file, module, name, lineno, parent, children e is_async.
for nombre, item in simbolos.items():
if isinstance(item, pyclbr.Function):
print({
"nombre": item.name,
"linea": item.lineno,
"asincrona": item.is_async,
})is_async diferencia funciones normales de coroutines declaradas con async def.
Descriptores de clase
Los objetos Class incluyen los mismos atributos y añaden super y methods. La lista super puede contener descriptores resueltos o strings cuando una clase base no se encuentra.
for item in simbolos.values():
if isinstance(item, pyclbr.Class):
bases = [
base.name if hasattr(base, "name") else base
for base in item.super
]
print(item.name, bases, item.methods)No presupongas que toda herencia será resuelta. Imports condicionales, aliases, bases generadas y metaprogramación limitan el resultado.
Definiciones anidadas
Los descriptores poseen children y parent, lo que permite representar clases internas, métodos y funciones locales.
def visitar(item, profundidad=0):
print(" " * profundidad, item.name, item.lineno)
for hijo in item.children.values():
visitar(hijo, profundidad + 1)
for item in simbolos.values():
if hasattr(item, "children"):
visitar(item)Si combinas resultados de varios módulos, considera un conjunto de objetos visitados.
Construye un índice de símbolos
Los descriptores pueden convertirse en JSON para una búsqueda de código.
def serializar(item):
return {
"module": item.module,
"name": item.name,
"file": item.file,
"line": item.lineno,
"kind": type(item).__name__,
"children": [
serializar(hijo)
for hijo in item.children.values()
],
}Normaliza rutas antes de almacenarlas y evita publicar rutas absolutas del servidor.
Crea enlaces para el editor
Como los descriptores incluyen archivo y línea, un navegador puede abrir la definición exacta. Comprueba que el archivo resuelto siga dentro del workspace antes de generar enlaces.
Para enumerar módulos disponibles, combina esta técnica con pkgutil en Python. Para mapear imports usados por un script, consulta modulefinder en Python.
Paquetes y __path__
Cuando el nombre analizado es un paquete, el diccionario contiene la clave __path__ con rutas de búsqueda.
resultado = pyclbr.readmodule_ex("mi_paquete")
print(resultado.get("__path__"))Trata esta clave por separado porque no es un descriptor de función o clase.
Por qué no importar el módulo
importlib.import_module() junto con inspect ofrece datos de runtime, pero importar ejecuta código superior. Un plugin malicioso, roto o dependiente del entorno puede producir efectos antes de la inspección.
pyclbr evita esa ejecución al leer la fuente. Esto no convierte rutas arbitrarias en inocuas: la herramienta abre archivos y puede consumir recursos en árboles grandes.
Limitaciones con extensiones
El módulo analiza implementaciones escritas en Python. Extensiones C, módulos integrados y algunos módulos generados no tienen fuente compatible. El análisis puede fallar o no devolver símbolos útiles.
Usa stubs de tipos, metadatos o inspección controlada en otro proceso cuando necesites representar extensiones.
Limitaciones de análisis estático
Clases creadas dinámicamente, funciones asignadas mediante expresiones, decoradores que sustituyen objetos, métodos inyectados por metaclases e imports dinámicos pueden no aparecer. pyclbr informa declaraciones sintácticas, no el estado final.
Usa ast para mayor profundidad. Para funciones léxicas de editor, consulta tokenize en Python.
Comparación con codeop
La guía de codeop en Python explica cómo compilar texto interactivo y detectar entradas completas. pyclbr resuelve otro problema: extraer un árbol limitado de definiciones desde un módulo localizado por el sistema de imports.
Cache y actualización
Las herramientas de índice deben detectar cambios de fuente y leer nuevamente. Guarda timestamp, tamaño o hash con cada resultado. No mantengas descriptores antiguos indefinidamente.
Manejo de errores
Archivos ausentes, sintaxis inválida, problemas de encoding y bases importadas no resueltas afectan los resultados. Captura errores por módulo y continúa con los demás.
def leer_seguro(nombre, rutas):
try:
return pyclbr.readmodule_ex(nombre, path=rutas)
except (ImportError, OSError, SyntaxError) as error:
registrar_fallo(nombre, error)
return {}No ocultes todas las excepciones sin registro porque el índice puede parecer completo y no serlo.
Seguridad y límites
- Restringe las raíces de búsqueda.
- No expongas rutas absolutas.
- Limita cantidad y tamaño de archivos.
- Define timeout para la indexación.
- No sigas enlaces fuera del workspace.
- Ejecuta con permisos mínimos.
- Recuerda que no importar no es un sandbox completo.
Pruebas recomendadas
Prueba funciones síncronas y asíncronas, clases con herencia múltiple, métodos, clases internas, funciones locales, paquetes, aliases y archivos con sintaxis inválida. Verifica líneas y rutas en Windows y Unix.
Incluye decoradores y metaclases para documentar qué puede representar tu navegador.
Buenas prácticas
- Prefiere
readmodule_ex(). - Trata
__path__por separado. - Espera clases base no resueltas.
- Normaliza y restringe rutas.
- Actualiza el índice cuando cambia la fuente.
- Registra fallos por módulo.
- Usa AST para detalles completos.
- No importes código desconocido solo para listar símbolos.
Conclusión
pyclbr en Python ofrece una forma ligera de descubrir funciones y clases sin ejecutar el módulo. Es apropiado para navegadores de código, índices sencillos y herramientas que desean reducir efectos secundarios.
Úsalo con expectativas claras: solo fuente Python, declaraciones sintácticas e información parcial. Consulta la documentación oficial de pyclbr y la documentación del módulo ast.







