Files
fn_registry/python/functions/notebook/jupyter_discover.md
T
egutierrez 5f4f1f7508 docs: params/output semántico en 506 funciones para composabilidad
Añade campos params y output al frontmatter YAML de las 506 funciones del registry.
Cada parámetro tiene descripción semántica (qué representa, unidades, rango típico)
y cada función describe qué produce su output. Permite a agentes razonar sobre
cadenas de composición (ej: prices → log_return → sharpe_ratio) sin leer código.
2026-04-05 18:45:16 +02:00

134 lines
5.1 KiB
Markdown

---
name: jupyter_discover
kind: function
lang: py
domain: notebook
version: "1.1.0"
purity: impure
signature: "def jupyter_discover(registry_root: str = \"\", ports: list[int] | None = None) -> list[dict]"
description: "Descubre instancias de Jupyter Lab activas escaneando archivos .jupyter-port en analysis/ y puertos comunes (8888-8892). Detecta el root_dir real de cada instancia via /proc/pid/cmdline (Linux) para identificar correctamente el analisis en escenarios multi-instancia. Para cada instancia consulta /api/status, /api/config, /api/kernels y /api/sessions via HTTP REST."
tags: [jupyter, notebook, discovery, api, http, kernels, sessions, analysis, multi-instance, proc]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [json, os, urllib.error, urllib.request, pathlib]
params:
- name: registry_root
desc: "Raíz del registry para detectar análisis (opcional, usa FN_REGISTRY_ROOT si no se proporciona)"
- name: ports
desc: "Puertos a escanear (default: 8888-8892, detecta automáticamente)"
output: "Lista de dicts con información de instancias Jupyter: url, port, analysis, root_dir, collaborative, kernels, sessions"
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/notebook/jupyter_discover.py"
---
## Ejemplo
```python
from notebook.jupyter_discover import jupyter_discover
# Descubrir con deteccion automatica de puertos
instances = jupyter_discover(registry_root="/home/lucas/fn_registry")
# Escanear puertos especificos
instances = jupyter_discover(ports=[8888, 8900])
for inst in instances:
print(inst["url"], inst["analysis"], inst["root_dir"], inst["collaborative"])
# http://localhost:8888 estudio_mercados /home/lucas/fn_registry/analysis/estudio_mercados True
```
## Estructura del dict retornado
Cada elemento de la lista tiene la siguiente forma:
```python
{
"url": "http://localhost:8888",
"port": 8888,
"analysis": "estudio_mercados", # nombre del subdirectorio en analysis/, detectado via /proc
"root_dir": "/home/lucas/fn_registry/analysis/estudio_mercados", # path absoluto real del proceso
"collaborative": True, # True si YDocExtension esta activo
"kernels": [
{
"id": "abc123...",
"name": "python3",
"execution_state": "idle",
"last_activity": "2026-04-01T10:00:00.000Z"
}
],
"sessions": [
{
"notebook": "notebooks/01_exploracion.ipynb",
"kernel_id": "abc123...",
"kernel_state": "idle"
}
]
}
```
## CLI
```bash
# Descubrir con deteccion automatica
python python/functions/notebook/jupyter_discover.py --registry-root /home/lucas/fn_registry
# Puertos especificos, salida JSON
python python/functions/notebook/jupyter_discover.py --port 8888 --port 8889 --json
# Usando variable de entorno
FN_REGISTRY_ROOT=/home/lucas/fn_registry python python/functions/notebook/jupyter_discover.py
```
Ejemplo de salida en modo texto con multi-instancia:
```
Puerto 8888 [colaborativo]
url: http://localhost:8888
analysis: estudio_mercados
root_dir: /home/lucas/fn_registry/analysis/estudio_mercados
kernels (1):
- python3 estado=idle id=abc12345...
sesiones (1):
- notebooks/01.ipynb kernel=abc12345... estado=idle
Puerto 8889 [estandar]
url: http://localhost:8889
analysis: estudio_embeddings
root_dir: /home/lucas/fn_registry/analysis/estudio_embeddings
kernels: ninguno
sesiones: ninguna
```
## Notas
Solo usa stdlib: `urllib`, `json`, `pathlib`, `os`. No requiere `requests` ni clientes Jupyter especializados.
El escaneo de puertos tiene un timeout de 2 segundos por instancia para no bloquear en puertos cerrados.
### Deteccion de root_dir via /proc
En Linux, `_find_jupyter_pid_for_port()` escanea `/proc/*/cmdline` buscando el proceso Jupyter que tenga `--port=N` o `--ServerApp.port=N` en sus argumentos. Para el puerto default 8888, acepta cualquier proceso jupyter sin `--port` explicito.
Una vez encontrado el PID, `_get_root_dir_from_proc()` lee los argumentos buscando `--ServerApp.root_dir=` o `--notebook-dir=`. Si ninguno esta presente, usa el cwd del proceso (`/proc/{pid}/cwd`) como fallback.
La funcion `_extract_analysis_name()` extrae el nombre del analisis del root_dir: si el path contiene `analysis/{nombre}`, retorna `{nombre}`; en caso contrario retorna el ultimo componente del path.
Esta cadena es mas fiable que confiar solo en `.jupyter-port` porque detecta el directorio real del proceso, no el registrado al arranque.
### Prioridad de fuentes para analysis name
1. root_dir detectado via /proc (mas fiable)
2. Hint del archivo .jupyter-port (fallback si /proc no esta disponible)
3. Cadena vacia si ninguno funciona
### Modo colaborativo
La deteccion de modo colaborativo busca `YDocExtension` o `collaborative` en el JSON de `/api/config`. Esto cubre tanto jupyter-collaboration >= 2.x (que expone la extension bajo `LabApp`) como configuraciones antiguas.
Archivos `.jupyter-port`: el pipeline `init_jupyter_analysis` escribe este archivo en cada analisis al lanzar Jupyter, permitiendo que `jupyter_discover` los encuentre sin escanear todos los puertos.