---
name: jupyter_run_cells
kind: function
lang: py
domain: notebook
version: "1.0.0"
purity: impure
signature: "jupyter_run_cells(notebook_path: str, cell_indices: list[int], server_url: str, token: str, stop_on_error: bool, timeout_per_cell_s: int) -> dict"
description: "Ejecuta un lote de celdas existentes (por indice) en una sola conexion WebSocket. Un GET inicial + un PUT final. Latencia fija ~3s en vez de ~3s * N de jupyter_execute_cell individual."
tags: [jupyter, notebook, kernel, websocket, execution, cells, batch, notebook]
uses_functions: [jupyter_exec_py_notebook]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [jupyter_kernel_client, notebook.jupyter_exec]
params:
  - name: notebook_path
    desc: "Ruta relativa al notebook (relativa a la raiz del servidor Jupyter)"
  - name: cell_indices
    desc: "Lista de indices de celdas a ejecutar (0-based, en orden). Solo celdas de tipo code."
  - name: server_url
    desc: "URL del servidor Jupyter (default http://localhost:8888)"
  - name: token
    desc: "Token de autenticacion (default vacio = sin auth)"
  - name: stop_on_error
    desc: "Si True, para al primer output de tipo error. El PUT se hace con lo ejecutado hasta ese punto."
  - name: timeout_per_cell_s
    desc: "Timeout en segundos por cada ejecucion individual de celda (default 600)"
output: "Dict con notebook, executed (lista de resultados por celda), stopped_at, kernel_id y total_duration_s"
tested: true
tests:
  - "test_run_cells_single_cell_returns_output"
  - "test_run_cells_stops_on_error_by_default"
  - "test_run_cells_no_stop_on_error_continues"
  - "test_run_cells_invalid_index_raises"
  - "test_run_cells_non_code_cell_raises"
  - "e2e: test_e2e_run_cells_batch"
  - "e2e: test_e2e_run_cells_stop_on_error"
test_file_path: "python/functions/notebook/tests/test_jupyter_run_cells.py"
file_path: "python/functions/notebook/jupyter_run_cells.py"
---

## Ejemplo

```python
from notebook.jupyter_run_cells import jupyter_run_cells

result = jupyter_run_cells(
    notebook_path="notebooks/analisis.ipynb",
    cell_indices=[0, 1, 2, 5],
    server_url="http://localhost:8888",
    token="",
    stop_on_error=True,
)
# {
#   "notebook": "notebooks/analisis.ipynb",
#   "executed": [
#     {"cell_index": 0, "execution_count": 1, "outputs": ["import ok"], "error": None, "duration_s": 1.2},
#     {"cell_index": 1, "execution_count": 2, "outputs": ["42"], "error": None, "duration_s": 0.1},
#     ...
#   ],
#   "stopped_at": None,
#   "kernel_id": "abc-123",
#   "total_duration_s": 4.8,
# }
```

CLI:

```bash
# Indices como argumentos posicionales
python python/functions/notebook/jupyter_run_cells.py notebooks/analisis.ipynb 0 1 2 5

# Indices via stdin JSON (util desde scripts)
echo '[0, 1, 2, 5]' | python python/functions/notebook/jupyter_run_cells.py notebooks/analisis.ipynb

# No parar en error + timeout custom
python python/functions/notebook/jupyter_run_cells.py notebooks/analisis.ipynb 0 1 2 \
  --no-stop-on-error --timeout 120
```

## Cuando usarla

Cuando necesites re-ejecutar varias celdas de un notebook existente y el overhead de abrir/cerrar N conexiones WS sea inaceptable (>3 celdas, celdas pesadas, o en pipelines automatizados). Sustituye a llamar `jupyter_execute_cell` N veces en bucle desde el agente.

## Gotchas

- Las celdas deben existir previamente en el notebook. Para anadir y ejecutar celdas nuevas, usar `jupyter_append_execute` de `jupyter_exec`.
- Solo ejecuta celdas de tipo `code`. Pasar el indice de una celda markdown lanza `ValueError` antes de abrir el WS.
- El `PUT` final se hace siempre, incluso si `stop_on_error` detiene el lote. El notebook queda con los outputs de las celdas ejecutadas hasta el punto de parada.
- `KernelClient` ignora `timeout_per_cell_s` si la implementacion subyacente no lo soporta (depende de la version de `jupyter-kernel-client`). En ese caso el timeout global del proceso es el unico limite.
- Si el servidor Jupyter no esta corriendo, `_ensure_session` lanza `URLError` inmediatamente (no hay retry incorporado).
- El WS se abre con el `kernel_id` de la sesion activa del notebook. Si el kernel muere entre el `_ensure_session` y la ejecucion, `KernelClient` lanzara una excepcion.
