> For the complete documentation index, see [llms.txt](https://docs.augelab.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.augelab.com/spanish/caracteristicas-clave/headless/scripting-reference.md).

# Referencia de scripting

Esta página documenta la API pública para ejecución headless (sin interfaz) expuesta por el paquete `studio` de Python.

Se centra en estos símbolos públicos:

* `StudioScenario`
* `install_logging_event`
* `enable_logging_stdout`
* `disable_logging_stdout`

Si nunca has ejecutado un escenario en modo headless, primero lee la sección Headless y luego vuelve aquí para ver los detalles de la API.

***

## Imports

Todas las APIs documentadas aquí se exportan desde el paquete de nivel superior `studio`:

```python
from studio import (
	StudioScenario,
	install_logging_event,
	enable_logging_stdout,
	disable_logging_stdout,
)
```

***

## Type Stubs

La interfaz pública para headless puede encontrarse aquí:

```python
from __future__ import annotations

from typing import Any, Callable, Optional


def enable_logging_stdout() -> None: ...
def disable_logging_stdout() -> None: ...
def install_logging_event(event: Callable[[str], None]) -> None: ...


class StudioScenario:
	def __init__(self, *, verification_code: str = "") -> None: ...

	def load_scenario(self, path: str) -> Optional[StudioScenario]: ...
	def load_scenario_raw(self, content: str) -> bool: ...
	def save_scenario(self, path: str) -> bool: ...
	def clear_scenario(self) -> bool: ...
	def disable_load_errors(self) -> StudioScenario: ...
	def load_custom_nodes(self, path: str) -> bool: ...

	def get_ports(
		self,
		input_op_title: str = "Subsystem In",
		output_op_title: str = "Subsystem Out",
	) -> dict[str, list[str]]: ...
	def get_name(self) -> str: ...

	def run(self, args: tuple[Any, ...] = ()) -> tuple[list[Any], ...]: ...
	def run_server(
		self,
		inputs: tuple[Any, ...] = (),
		host: str = "127.0.0.1",
		port: int = 8080,
		display_controls: bool = False,
		header: str = "Scenario Server",
	) -> None: ...
	def cleanup(self) -> bool: ...

	def enable_logging_stdout(self) -> StudioScenario: ...
	def disable_logging_stdout(self) -> StudioScenario: ...
	def install_logging_hook(self, hook: Callable[[str, int], None], level: int = 0) -> StudioScenario: ...

	def installStartEvent(self, event: Callable[..., None]) -> StudioScenario: ...
	def installEndEvent(self, event: Callable[..., None]) -> StudioScenario: ...
	def installStepStartEvent(self, event: Callable[..., None]) -> StudioScenario: ...
	def installStepEndEvent(self, event: Callable[..., None]) -> StudioScenario: ...
```

***

## `StudioScenario`

`StudioScenario` es el punto de entrada principal para cargar y ejecutar un archivo `.pmod` sin la interfaz de escritorio (modo headless).

### Constructor

```python
scenario = StudioScenario(verification_code="...")
```

#### `verification_code`

Este valor se usa para configurar la licencia en la ejecución headless.

Patrones comunes:

* Máquina local / servidor: pasa la cadena del verification code.
* Docker / CI: monta un archivo de licencia dentro del contenedor y pasa su **ruta** como `verification_code`.

> Advertencia\
> Para entornos no interactivos (Docker/CI/servicios), siempre pasa `verification_code` explícitamente.

***

### Ciclo típico de uso

En la mayoría de los scripts harás:

1. Crear el escenario (`StudioScenario(...)`)
2. Cargar un `.pmod` (`load_scenario(...)`)
3. Ejecutarlo (`run(...)`)
4. Limpiar recursos (`cleanup()`)

Ejemplo:

```python
import os

from studio import StudioScenario, enable_logging_stdout


def main() -> None:
	# Opcional: imprime logs de runtime en stdout (útil al depurar).
	enable_logging_stdout()

	verification_code = os.environ.get("AUGELAB_VERIFICATION_CODE", "")
	scenario = StudioScenario(verification_code=verification_code)

	try:
		loaded = scenario.load_scenario("your_scenario.pmod")
		if loaded is None:
			raise FileNotFoundError("Could not load .pmod file")

		# Si tu escenario tiene N puertos de entrada, debes pasar exactamente N args.
		result = scenario.run(args=())
		print("Scenario result:", result)
	finally:
		scenario.cleanup()


if __name__ == "__main__":
	main()
```

> Nota\
> `cleanup()` es importante en procesos de larga duración (servicios, jobs por lotes) para liberar memoria y resetear el estado de runtime.

***

### Cargar un `.pmod`

```python
scenario.load_scenario("path/to/scenario.pmod")
```

* Devuelve `StudioScenario` en caso de éxito.
* Devuelve `None` si la ruta no existe.

Si tu escenario referencia recursos externos, conserva la misma estructura de carpetas usada en la versión de escritorio (ver la sección “Transferring .pmod files”).

***

### Ejecutar un escenario

```python
outputs = scenario.run(args=(... ,))
```

El runtime valida que el número de argumentos coincida con el número de entradas del escenario.

Si tu `.pmod` tiene:

* 0 entradas: llama a `scenario.run()` o `scenario.run(())`
* 1 entrada: llama a `scenario.run((value,))`
* 2 entradas: llama a `scenario.run((value1, value2))`

> Advertencia\
> `args` debe ser una tupla. Para una sola entrada recuerda la coma final: `(value,)`.

***

## Más métodos de `StudioScenario`

Esta sección cubre métodos públicos adicionales que puedes usar para automatización y depuración.

### Cargar sin fallar por recursos faltantes

Si mueves escenarios entre máquinas/contenedores y algunos recursos pueden no estar disponibles, puedes optar por cargar de forma más permisiva:

```python
from studio import StudioScenario

scenario = StudioScenario(verification_code="YOUR_CODE")
scenario.disable_load_errors()
scenario.load_scenario("scenario.pmod")
```

### Cargar / guardar

```python
scenario.load_scenario_raw(content="...")
scenario.save_scenario("out.pmod")
scenario.clear_scenario()
```

### Nodos personalizados

Carga módulos Python de nodos personalizados desde una carpeta:

```python
scenario.load_custom_nodes("path/to/custom_nodes")
```

> Advertencia\
> Esto importa dinámicamente archivos Python. Solo carga código de confianza.

### Nombre del escenario

```python
name = scenario.get_name()
```

### Puertos (entradas/salidas del escenario)

Puedes obtener los nombres de los puertos de entrada y salida:

```python
ports = scenario.get_ports()
print("inputs:", ports["inputs"])
print("outputs:", ports["outputs"])
```

### Ejecutar un servidor web (opcional)

Si quieres exponer un runner de escenario por HTTP:

```python
scenario.run_server(host="0.0.0.0", port=8080)
```

### Eventos

Instala callbacks alrededor del ciclo de ejecución del escenario:

```python
def on_start() -> None:
	print("Scenario started")


scenario.installStartEvent(on_start)
```

***

## Helpers de logging

AugeLab Studio tiene un logger de runtime usado por blocks y escenarios. En modo headless normalmente querrás una de estas opciones:

* imprimir logs en stdout mientras desarrollas
* reenviar logs a tu propio sistema de logging

Estos helpers son globales (afectan al logger de runtime usado por tu escenario headless).

***

### `enable_logging_stdout()`

Habilita logging de runtime “amigable” hacia stdout.

Úsalo cuando quieras ver logs de blocks/escenario en la consola:

```python
from studio import enable_logging_stdout

enable_logging_stdout()
```

***

### `disable_logging_stdout()`

Deshabilita el logging de runtime hacia stdout.

Útil cuando:

* estás usando `install_logging_event(...)` y quieres evitar logs duplicados
* quieres una salida limpia (solo tus propios `print(...)` o el output de tu logger)

```python
from studio import disable_logging_stdout

disable_logging_stdout()
```

***

### `install_logging_event(event: Callable[[str], None])`

Instala un callback que recibe los mensajes de log del runtime.

Esta es la forma más sencilla de integrar los logs de AugeLab en tu propio framework de logging.

```python
import logging

from studio import install_logging_event, disable_logging_stdout

logger = logging.getLogger("augelab")


def forward_to_python_logging(msg: str) -> None:
	logger.info(msg)


disable_logging_stdout()
install_logging_event(forward_to_python_logging)
```

### `StudioScenario.install_logging_hook(hook, level=0)`

Si prefieres instalar un hook por escenario (con filtrado por nivel), usa:

```python
def hook(msg: str, level: int) -> None:
	print(level, msg)


scenario.install_logging_hook(hook, level=20)
```

> Advertencia\
> Mantén tu callback rápido. Si necesitas hacer trabajo pesado (I/O, red), considera encolar los mensajes y procesarlos en otro hilo/proceso.

***

## Patrones recomendados

### 1) Variable de entorno para la licencia

Para scripts que se ejecutan en distintos entornos (local vs CI vs Docker), pasar `verification_code` mediante variable de entorno mantiene el código portable:

```python
import os
from studio import StudioScenario

scenario = StudioScenario(verification_code=os.environ["AUGELAB_VERIFICATION_CODE"])
```

### 2) Siempre limpiar con `try/finally`

```python
from studio import StudioScenario

scenario = StudioScenario(verification_code="YOUR_CODE")
try:
	scenario.load_scenario("scenario.pmod")
	scenario.run(())
finally:
	scenario.cleanup()
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.augelab.com/spanish/caracteristicas-clave/headless/scripting-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
