The Boardflare Python API

Complete API reference for the boardflare (bf) package.

The boardflare (bf) package provides reactive workbook integration, table selection, editable workbook tables, and worksheet formula publishing for Marimo notebooks.

All names are exported directly under boardflare (usually imported as import boardflare as bf).

bf.connector

bf.class connector()

Boardflare-managed external service connectors, exposed as bf.connector.

Declare delegated access with bf.connector.microsoft_graph(scopes=[...]), then read through it with graph.get(path, params=...) and pass the read to bf.inputs(...).

bf.inputs

bf.inputs(**named_specs: str | Reference | SelectionSpec | ConnectorRead)

Create a reactive workbook-input widget and return its Marimo wrapper.

Declare workbook dependencies as keyword arguments. Keep the returned widget displayed in a cell, and read synchronized values by name in downstream cells (e.g. inputs["sales"]). A notebook may declare at most one bf.inputs() widget.

Args: **named_specs: Named inputs where each value is: - A string range reference (e.g. "A1:C10" or "Sheet!TaxRate"). - A configured reference via bf.ref(reference, headers=True). - A connector read such as graph.get("/v1.0/me") from bf.connector.microsoft_graph(...). Each declared connector needs one sign-in, started with Connect in the Workbook Inputs section; the read resolves after sign-in. - An active table selection via bf.selection(...), yielding a pandas DataFrame of the visible row at the active cell.

Returns: A Marimo UI Anywidget. Access materialized inputs with dictionary subscript syntax: inputs[name]. Do not use attribute access (e.g. inputs.name).

bf.publish

bf.publish(*, outputs: Mapping[str, Any] | None=None, functions: Mapping[str, Callable[..., Any]] | None=None)

Publish live output values and callable Python functions to worksheet formulas.

Must be displayed in a cell so the Anywidget retains its connection to the host. Values are consumed in Excel via =BF.OUTPUT("name") and functions are invoked via =BF.FUNCTION("name", arg1, ...).

Args: outputs: Mapping of output names to values (DataFrames, Series, lists, scalars). Scalars return in a single cell; DataFrames, Series, and 1D/2D arrays spill. functions: Mapping of function names to Python callables. Supported signatures include positional parameters with trailing defaults, optional *args, keyword-only parameters that have defaults, and sync or async callables. Required keyword-only parameters and **kwargs are rejected.

Returns: A marimo UI element. Read the live consumer formulas from publication.consumers; publication.value raises RuntimeError.

Published dates and date-times are written to Excel as serial numbers, so they show with the destination cell’s own number format. Format the cells that hold =BF.OUTPUT or =BF.FUNCTION results as dates to display them as dates. A timezone-aware datetime is rejected; convert it to wall-clock time before publishing.

bf.ref

bf.ref(reference: str, *, headers: bool=False) -> Reference

Configure a reactive workbook reference for bf.inputs.

Use a plain reference string when no options are needed. bf.ref(...) is useful when the binding needs options such as headers=True and can be passed to bf.inputs().

Args: reference: Range address (e.g. "Sales!A1:D20") or defined name. headers: When True, treats the first row as column headers and materializes a rectangular range as a pandas DataFrame. When False (default), returns a scalar for one cell or a DataFrame with numbered columns for several.

Cells whose number format is a date or date-time arrive as datetime.datetime (a date-only cell is midnight), in a DataFrame as datetime64[ns] columns, never as Excel serial numbers. Time-only cells arrive as datetime.time. Cells with any other format arrive as numbers, so an unformatted date is a serial float.

Returns: A Reference specification to pass to bf.inputs(...).

bf.selection

bf.selection(table: str) -> SelectionSpec

Configure an active workbook table selection input for bf.inputs.

Args: table: Excel workbook table name.

Returns: A SelectionSpec to pass to bf.inputs(...). When read through the inputs widget (e.g. inputs["selected"]), returns a pandas DataFrame with the table’s headers as columns and at most one row: the row of the user’s active cell (the cell the cursor is on), even when a larger range is highlighted. The result is empty (an empty DataFrame with those columns) when the active cell is outside the table’s body rows or columns, on another sheet, on the header or total row, or on a hidden or filtered row. Filtering that hides the active row does not refresh the result until the next selection change. If the table does not exist in the workbook, it produces an input error (not an empty DataFrame).

bf.table

bf.table(name: str, *, columns: Mapping[str, type], esm: str | Path | None=None, css: str | Path | None=None)

Declare a workbook table and return a self-contained CRUD element for it.

Display the returned element in a cell to show its interface: by default, an add form, a list of rows with Edit and Delete on each row, and any error messages. The notebook is the application, so users add, edit, and delete rows here without opening the worksheet. Displaying the element creates the table in the worksheet if the table is missing.

Read the rows in a downstream cell with element.dataframe. The cell reruns when the table changes::

orders = bf.table("orders", columns={"item": str, "qty": int})   # cell A
orders                                                           # cell B: shows the interface
totals = orders.dataframe.groupby("item")["qty"].sum()           # cell C

Args: name: Table name, beginning with a letter and containing only letters, digits, and underscores, at most 31 characters. The rows are stored in an Excel table named bf_<name> on a worksheet named <name>, so a formula reads them as bf_<name>[...]. Two tables in one notebook cannot differ only by case, because Excel treats their worksheet names as the same; that raises ValueError. Names beginning with bf_ in any case also raise ValueError, because Boardflare adds that prefix. The name registry lasts for the kernel session, so renaming a table’s case in its cell raises ValueError until the kernel restarts. columns: Mapping of column names to Python types: str, int, float, or bool. Other types raise TypeError. _id is reserved in any letter case, because Boardflare adds it to every table as the row identifier. Column names must be unique ignoring case, because Excel headers are case-insensitive. esm: Optional JavaScript ES module source string or pathlib.Path exporting a custom render({ el, table, model, signal }) function to replace the built-in CRUD view. table provides name, columns (declared columns without _id), rows (each with _id), status, error, loaded, add(row), update(rowId, updates), delete(rowId), and subscribe(listener). The widget root, el, carries data-bf-table set to the unprefixed table name. css: Optional CSS stylesheet string or pathlib.Path injected alongside the widget.

Returns: A marimo UI element. Display it in a cell and read rows through .dataframe.

Existing tables: the declared columns must be present in the table’s header row. Columns the table has but the declaration does not list are extra columns. Boardflare leaves them alone: it never adds, removes, renames, or clears them. They appear in .dataframe with their stored values, a new row leaves their cells blank, and an update to a declared column does not change them. A populated pre-seeded table must also have an _id column in its header row, which Boardflare uses as the managed row key. Without it, the table is refused with Cannot adopt populated table.

A populated table whose declared header was renamed, or that lacks a declared column, shows an error banner and is not changed. An empty table (header row only) is extended instead: a declared column missing from its header is appended at the right edge, and the row added through the form keeps its value in that column. A renamed header on an empty table therefore stays as an extra column beside the restored declared column.

Returned Object Interfaces and Methods

In addition to top-level module functions, Boardflare Anywidgets and return objects provide the following interfaces:

Inputs Widget (returned by bf.inputs(...))

  • Display the returned widget as the cell’s last expression so its communications channel with the workbook host remains connected.
  • Access synchronized input values with dictionary subscript syntax: inputs["name"]. Attribute access (e.g. inputs.name) is not supported.
  • At most one bf.inputs(...) widget is allowed per notebook. Calling bf.inputs(...) a second time raises RuntimeError("Only one bf.inputs(...) is allowed per notebook; use one bf.inputs(...) with all inputs").
  • A bare table name includes the header row, so use bf.ref("TableName", headers=True) for named columns.
  • Reading a selection input (configured via bf.selection("TableName")) returns a pandas DataFrame with the table’s headers as column names and at most one row: the row of the user’s active cell (the cell the cursor is on), even when a larger range is highlighted. It is empty (an empty DataFrame with those columns, never None) when the active cell is outside the table’s body rows or columns, on another sheet, on the header or total row, or on a hidden or filtered row. The cursor must be on a table cell; cells beside the table select no row. If the table does not exist in the workbook, it produces an input error (not an empty DataFrame).
  • Selection rows carry values, not positions (use column values like picked["id"], not index alignment). Filtering that hides the active row does not refresh the result until the next selection change.
  • Connector reads (graph.get(...) from bf.connector.microsoft_graph(...)) are passed to bf.inputs(...), which performs them. Each declared connector needs one sign-in, started with Connect in the Workbook Inputs section; cells that read them stop until sign-in.

Table Widget (returned by bf.table(...))

  • Display the returned element as a cell’s last expression so its connection to the workbook host remains connected. Displaying it creates the table on its worksheet if the table is missing.
  • Read the current rows with the dataframe property: a pandas DataFrame with _id first and the declared columns after it. A cell that reads it reruns when the rows change, whether the change came from the editor or from the sheet. The read stops its cell until the element is connected.
  • The element has no Python add, update, or delete methods. Users change rows in the element’s own editor.
  • esm= replaces the built-in editor with a custom ES module whose render({ el, table, model, signal }) draws into el. Its table controller exposes rows, status, add, update, delete, and subscribe. See Workbook Design.

Publication Object (returned by bf.publish(...))

  • Display the publication element as the cell’s last expression so its connection to the workbook host remains connected.
  • publication.consumers: Inspect active worksheet formula consumers for published outputs (=BF.OUTPUT("name")) and functions (=BF.FUNCTION("name", ...)).
  • publication.value: Raises RuntimeError.

For complete usage patterns and code examples, see Workbook Design.