Skip to the content.

Tools reference

Complete API reference for mgtx-xlwings-utils — reusable xlwings (Excel COM on Windows) + pandas helpers used at MeiraGTx. Every public name is documented below with its verbatim signature, parameters, return type, exceptions, and a short runnable example.

The public API is exactly the names exported in src/xlwings_package/__init__.py __all__: 46 names — 36 functions + 10 exception classes. Everything imports from the top-level package:

import xlwings_package as xwp
# or
from xlwings_package import paste_df_to_named_range, import_excel_table

Underscore-prefixed names (e.g. _assert_stacked_only, _check_danger_zone, _sanitize_df_for_excel) are internal and are not part of the supported surface; they appear here only where they explain observable behaviour.

Key concepts (one paragraph)


Table of contents

Workbook / sheet initialize_workbook · clear_workbook · get_worksheet · create_sheet · delete_sheet

Named ranges & tables add_named_range · resolve_named_range · initialise_table_named_ranges · import_excel_table · paste_df_to_named_range · apply_table_features · scan_named_range_table · get_named_range_results · get_value · get_list_results · check_duplicate_values

Benchling initialise_table_benchling · apply_benchling_table_features · cleanup_stale_table_names

Plates create_plate_table · create_plate_layout · paste_plate_data · get_plate_format_presets · plate_layout_to_dataframe

Formatting & plotting colour_rows · paste_plot_to_cell · add_dropdown_list

File IO choose_save_path · read_file_to_df · save_dataframe · offer_file_actions · resolve_workbook_path

DataFrame utils strip_strings · limit_columns_to_decimals · rename_dtype_columns · check_missing_columns · check_case_sensitive_entries

UI display_message · display_message_and_exit

Exceptions XlwingsPackageError · UserCancelledError · NamedRangeNotFoundError · WorksheetNotFoundError · EmptyDataError · TableSpaceOccupiedError · ManifestNotFoundError · ExcelColumnOverflowError · ColumnOrderMismatchError · SideBySideNotSupportedError


Workbook / sheet

Source: excel_io.py. Many helpers in this package accept wb=None and fall back to a module-level default workbook set by initialize_workbook.

initialize_workbook

def initialize_workbook(wb):

Store wb as the module-level default workbook used by subsequent helper calls (those that accept wb=None). Returns the workbook it stored.

Parameter Type Meaning
wb xlwings Book Workbook to remember as the default.

Returns: the stored workbook object. Raises:

import xlwings as xw
import xlwings_package as xwp

wb = xw.Book()
xwp.initialize_workbook(wb)        # later calls may pass wb=None

clear_workbook

def clear_workbook() -> None:

Clear the module-level default workbook reference (set it back to None).

Parameters: none. Returns: None. Raises:

import xlwings_package as xwp
xwp.clear_workbook()

get_worksheet

def get_worksheet(wb=None, sheet_name: str = ""):

Return an xlwings Sheet from a workbook.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to the initialized default when None.
sheet_name str Name of the worksheet to retrieve (required).

Returns: xlwings Sheet. Raises: ValueError if sheet_name is empty; WorksheetNotFoundError if the sheet does not exist; ValueError if no workbook is available.

import xlwings_package as xwp
ws = xwp.get_worksheet(wb, "Results")

create_sheet

def create_sheet(wb, sheet_name: str):

Create and return a new sheet in the workbook.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to the default when None.
sheet_name str Name for the new sheet.

Returns: the new xlwings Sheet. Raises: ValueError if no workbook is available, or if sheet_name (case-insensitive) duplicates an existing sheet.

import xlwings_package as xwp
ws = xwp.create_sheet(wb, "Scratch")

delete_sheet

def delete_sheet(wb, sheet_name: str) -> None:

Delete a worksheet from the workbook.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to the default when None.
sheet_name str Name of the sheet to delete.

Returns: None. Raises: ValueError if sheet_name is empty or no workbook is available; WorksheetNotFoundError if the sheet does not exist.

import xlwings_package as xwp
xwp.delete_sheet(wb, "Scratch")

See also: normal tables guide.


Named ranges & tables

Source: excel_io.py. The core read/write workflow for managed tables. Typical lifecycle: build a header → initialise_table_named_rangespaste_df_to_named_range / import_excel_table.

add_named_range

def add_named_range(wb, sheet_name: str, name: str, range_object, scope: str = "sheet", replace_if_exists: bool = False):

Add a named range pointing at real cells, either sheet-scoped or workbook-scoped.

Parameter Type Meaning
wb xlwings Book Workbook.
sheet_name str Sheet used as the scope anchor.
name str Name token to assign.
range_object xlwings Range Range whose .address is used.
scope str "sheet" (default) or "workbook".
replace_if_exists bool When True, delete any existing same-named range first. Default False.

Returns: None. Raises: ValueError if range_object has no valid address, or scope is neither "sheet" nor "workbook"; WorksheetNotFoundError if sheet_name does not exist.

import xlwings_package as xwp
ws = wb.sheets["Results"]
xwp.add_named_range(wb, "Results", "sample_id", ws.range("B4"), scope="sheet")

resolve_named_range

def resolve_named_range(wb, named_range: str, scope_sheet: Optional[str] = None) -> Tuple[Any, Any]:

Resolve a named range (sheet- or workbook-scoped) and return (sheet, range_object).

Parameter Type Meaning
wb xlwings Book Workbook containing the name.
named_range str Name to resolve.
scope_sheet str or None If given, look up the name scoped to that sheet only; otherwise workbook-scoped.

Returns: (xlwings Sheet, xlwings Range). Raises: WorksheetNotFoundError if scope_sheet is given but missing; NamedRangeNotFoundError if the name cannot be found.

import xlwings_package as xwp
sheet, rng = xwp.resolve_named_range(wb, "sample_table", scope_sheet="Results")

initialise_table_named_ranges

def initialise_table_named_ranges(
    wb,
    table_name_range: str,
    scope_sheet: Optional[str] = None,
    include_list: Optional[Iterable[str]] = None,
    ignore_list: Optional[Iterable[str]] = None,
) -> dict[str, str]:

Scan a table’s header row (anchored by table_name_range) and assign a sheet-scoped named range to every header cell that lacks one. Also writes the <name>_table_start / <name>_table_end bookmarks when data rows exist below the header. Run this once before reading with import_excel_table or scan_named_range_table.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to the default when None.
table_name_range str Anchor named range covering the header row.
scope_sheet str or None Restrict name resolution to this sheet.
include_list iterable of str or None Column names / range tokens to keep. Mutually exclusive with ignore_list.
ignore_list iterable of str or None Column names / range tokens to drop. Mutually exclusive with include_list.

Returns: dict[str, str] mapping each range-name token → its display header. Raises: NamedRangeNotFoundError if the anchor can’t resolve; SideBySideNotSupportedError on a row-overlap with another managed table; ValueError if both include_list and ignore_list are passed.

import xlwings_package as xwp
mapping = xwp.initialise_table_named_ranges(wb, "sample_table", scope_sheet="Results")
# {'sample_id': 'Sample ID', 'concentration': 'Concentration', ...}

import_excel_table

def import_excel_table(
    wb,
    table_name_range: str,
    scope_sheet: Optional[str] = None,
    use_name_range_names: bool = True,
    check_empty_columns: bool = True,
    include_list: Optional[Iterable[str]] = None,
    ignore_list: Optional[Iterable[str]] = None,
):

Read a managed table under a header anchor into a DataFrame, discovering data rows by scanning downward from the header. Auto-heals the _table_start / _table_end bookmarks (writes missing, grows stale; never auto-shrinks).

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to default when None.
table_name_range str Anchor named range over the header row.
scope_sheet str or None Resolve names within this sheet only.
use_name_range_names bool True (default): DataFrame columns keyed by range token; False: keyed by raw header text.
check_empty_columns bool True (default): raise if any column is entirely null.
include_list iterable or None Columns/tokens to keep. Mutually exclusive with ignore_list.
ignore_list iterable or None Columns/tokens to drop. Mutually exclusive with include_list.

Returns: (DataFrame, dict) — the data and the {range_token: display_header} mapping. Raises: EmptyDataError if the table is empty or an all-null column is found while check_empty_columns=True; NamedRangeNotFoundError if the anchor can’t resolve; ValueError if both selector lists are passed.

import xlwings_package as xwp
df, mapping = xwp.import_excel_table(wb, "sample_table", scope_sheet="Results")

paste_df_to_named_range

def paste_df_to_named_range(
    wb,
    df: pd.DataFrame | list[pd.DataFrame],
    named_range: str | list[str],
    scope_sheet: Optional[str] = None,
    sanitize_formulas: bool = True,
    borders: Literal["mesh"] | None = None,
) -> None:

Write one or more DataFrames into managed table areas. The data is written positionally (df.values only — the index is not written) starting under the header named range. Across grows and shrinks the gap to the next stacked table below is preserved; bookmarks are updated every call and auto-healed if corrupt.

Calling modes:

Behaviour notes:

Parameter Type Meaning
wb xlwings Book Workbook.
df DataFrame or list[DataFrame] Data to write.
named_range str or list[str] Target table header name(s).
scope_sheet str or None Resolve names within this sheet only.
sanitize_formulas bool Apostrophe-escape leading =/+/-/@ text cells. Default True.
borders "mesh" or None "mesh" draws the cell-grid border mesh over the pasted extent. Default None.

Returns: None. Raises: ValueError (mismatched list lengths; named/MultiIndex df index, #19); TypeError (wrong df / named_range type); EmptyDataError (zero rows or zero columns); NamedRangeNotFoundError; WorksheetNotFoundError (scope_sheet given but missing); TableSpaceOccupiedError (loose content in the danger zone); SideBySideNotSupportedError (post-paste row overlap); ColumnOrderMismatchError (df columns don’t match existing header positions); ExcelColumnOverflowError (auto-widen past column XFD).

import pandas as pd
import xlwings_package as xwp

df = pd.DataFrame({"sample_id": ["S1", "S2"], "concentration": [1.2, 3.4]})
xwp.paste_df_to_named_range(wb, df, "sample_table", scope_sheet="Results")

# Multi-table in one call
xwp.paste_df_to_named_range(wb, [df1, df2], ["table_a", "table_b"])

apply_table_features

def apply_table_features(
    wb,
    table_name_range: str,
    *,
    scope_sheet: Optional[str] = None,
    column_colour_map: Optional[dict[str, str]] = None,
    prefill_rows: int = 0,
    dropdowns: Optional[dict[str, Sequence]] = None,
    dropdown_list_sheet: str = "Lists",
) -> None:

Apply column colour fills, prefill rows, and/or in-cell dropdowns to a table after its init call. Works on both manual (initialise_table_named_ranges) and Benchling (initialise_table_benchling) tables — for the Benchling side, apply_benchling_table_features is the convenience wrapper that seeds dropdowns from the schema enums for you.

A colour or dropdown can only attach to real cells, so when a feature is requested on an otherwise-empty table one data row is auto-seeded (that seed is not meshed — pass prefill_rows > 0 to draw the border mesh). A later paste_df_to_named_range reads the template row back and propagates the colours and dropdowns across the pasted rows.

Parameter Type Meaning
wb xlwings Book Workbook.
table_name_range str Anchor named range of the table header row.
scope_sheet str or None Resolve names within this sheet only.
column_colour_map dict[str, str] or None {column_key: colour_token}. Key = header display value (case-insensitive) or per-column range name. Tokens: "purple", "dark purple", "light yellow", "dark yellow".
prefill_rows int Seed this many rows below the header and mesh them. Default 0.
dropdowns dict[str, Sequence] or None {column_key: [options]} — attach an in-cell list dropdown over the column’s data extent.
dropdown_list_sheet str Sheet hosting the dropdown option source lists. Default "Lists".

Returns: None. Raises: ValueError (unknown colour token, or a column key that resolves to no column); NamedRangeNotFoundError (table_name_range not found).

import xlwings_package as xwp

xwp.initialise_table_named_ranges(wb, {"sample_table": ["sample_id", "grade"]})
xwp.apply_table_features(
    wb, "sample_table",
    column_colour_map={"grade": "dark purple"},
    dropdowns={"grade": ["A", "B", "C"]},
    prefill_rows=8,
)

scan_named_range_table

def scan_named_range_table(
    wb,
    table_start_name: str,
    table_end_name: str,
    scope_sheet: Optional[str] = None,
    use_name_range_names: bool = False,
    include_list: Optional[Iterable[str]] = None,
    ignore_list: Optional[Iterable[str]] = None,
):

Read a table using explicit start/end bookmark names (no header anchor). The header row is inferred as the row directly above table_start_name; the column width is found by scanning header cells rightward until the first blank (so the header must be contiguous — no blank separator column on this path). Grows table_end_name in place if data extends beyond it.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to default when None.
table_start_name str Sheet-scoped name at the first data cell (top-left of data, not header).
table_end_name str Sheet-scoped name at the last data cell (bottom row, any column).
scope_sheet str or None Resolve within this sheet; otherwise names must be unambiguous workbook-wide.
use_name_range_names bool True: columns keyed by range token; False (default): raw header text.
include_list / ignore_list iterable or None Column/token selectors (mutually exclusive).

Returns: (DataFrame, dict) — data and {range_token: display_header}. Raises: NamedRangeNotFoundError if start/end can’t resolve or are on different sheets; ValueError if both selector lists are passed.

import xlwings_package as xwp
df, mapping = xwp.scan_named_range_table(
    wb, "sample_table_start", "sample_table_end", scope_sheet="Results"
)

get_named_range_results

def get_named_range_results(
    wb=None,
    worksheet_name: str = "",
    named_range: str = "",
    strip: bool = True,
):

Read a single named range’s value, stripping strings by default.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to default when None.
worksheet_name str Sheet to read from.
named_range str Range/name to read (required).
strip bool When True (default), strip whitespace via strip_strings.

Returns: the cell value (whitespace-stripped when strip=True). Raises: ValueError if named_range is empty; WorksheetNotFoundError if the sheet is missing.

import xlwings_package as xwp
val = xwp.get_named_range_results(wb, "Results", "operator_name")

get_value

def get_value(wb, range_name: str, sheet_name: str = ""):

Read a single Excel range value from a worksheet (no stripping).

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to default when None.
range_name str Cell address or named range (e.g. "A1").
sheet_name str Sheet to read from (required).

Returns: the raw cell value. Raises: ValueError if sheet_name is empty; WorksheetNotFoundError if the sheet is missing.

import xlwings_package as xwp
v = xwp.get_value(wb, "B2", sheet_name="Results")

get_list_results

def get_list_results(wb, sheet_name: str, column_name: str, start_row_name: str, end_row_name: str):

Read a contiguous vertical range into a Python list. The column comes from column_name’s column; the first/last rows come from the rows of start_row_name / end_row_name.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to default when None.
sheet_name str Sheet to read from.
column_name str Named range / address whose column is used.
start_row_name str Named range / address whose row marks the first data row.
end_row_name str Named range / address whose row marks the last data row.

Returns: list of cell values from start row to end row in that column. Raises: WorksheetNotFoundError if the sheet is missing.

import xlwings_package as xwp
ids = xwp.get_list_results(wb, "Results", "sample_id", "table_start", "table_end")

check_duplicate_values

def check_duplicate_values(mapping: dict, error_prefix: str = "Duplicate value found") -> None:

Raise if any values in mapping repeat (useful for validating {range_token: display_header} mappings).

Parameter Type Meaning
mapping dict Dict whose values must be unique.
error_prefix str Prefix for the error message. Default "Duplicate value found".

Returns: None. Raises: ValueError listing the duplicated values.

import xlwings_package as xwp
xwp.check_duplicate_values({"a": "X", "b": "X"})   # ValueError: Duplicate value found: X

See also: normal tables guide, stacked tables & re-init concept.


Benchling

Source: benchling_io.py. These functions resolve assay-result schemas via mgtx_benchling_wrapper’s SchemaHandler and write/refresh table headers. You supply the authenticated BenchlingContext via ctx=.

initialise_table_benchling

def initialise_table_benchling(
    wb,
    schema_map: "dict[str, list[str]]",
    *,
    ctx,
    mode: Literal["autopaste", "paste"] = "autopaste",
    column: "int | str | dict[str, int | str] | None" = None,
    at: "str | None" = None,
    table_gap: int = 2,
) -> list[SchemaInitResult]:

Create or refresh Benchling assay-result table headers across one or more sheets. On first run a schema’s title + header row + per-column named ranges + schema-column manifest are written; on later runs the existing table is re-synced to Benchling (display names, title, added/removed/reordered columns). New columns append at the right edge and are placed in schema order by the repack pass. Dropped columns are always purged: cells cleared, name deleted, remaining columns close up, table extent shrinks.

schema_map maps each sheet name to the ordered list of assaysch_… ids to place on it. Multiple ids under one sheet are stacked there in list order, and the same id may appear under more than one sheet.

Schema lookup accepts only Benchling ids (assaysch_ prefix) — name-based lookup was removed.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to default when None.
schema_map dict[str, list[str]] Maps each sheet name to the ordered list of assaysch_… ids placed on it (stacked in list order).
ctx BenchlingContext (keyword-only) Caller-supplied context exposing a callable .benchling().
mode Literal["autopaste", "paste"] "autopaste" (default) auto-stacks rows; "paste" pins to an exact cell.
column int, Excel letter str, dict[schema_id, col], or None Autopaste column anchor. Scalar = same column for all; dict = per-schema (unlisted auto-detect); None auto-detects. Rejected in paste mode.
at str or None Paste anchor — top-left cell of the first table (e.g. "C5"); later tables stack below. Defaults to "A1". Rejected in autopaste mode.
table_gap int Empty rows between stacked tables. Default 2.

Returns: list[SchemaInitResult], one per schema. Each SchemaInitResult is a frozen, slotted dataclass with attributes schema_name, display_name, warehouse_name, range_name, sheet_name, action ("created" | "reinitialised"), columns (tuple[ColumnInfo, ...] — each ColumnInfo has display_name, warehouse_name, range_name), and diff (dict | None, only populated when action == "reinitialised"). Both SchemaInitResult and ColumnInfo are exported from xlwings_package. Raises: ValueError (bad mode, empty schema_map, a sheet mapped to an empty id list, column in paste mode, or at in autopaste mode); TypeError (schema_map not a dict, a sheet’s value not a list/tuple, column not an int/letter/dict/None, ctx not Benchling-compatible, or a column anchor is a bool); ImportError (mgtx-benchling-wrapper not installed); TableSpaceOccupiedError (fresh header lands on occupied cells); ExcelColumnOverflowError (placement past column XFD); NamedRangeNotFoundError (reinit can’t find the schema’s range). On a mid-reinit failure the raised exception carries a partial_diff attribute.

from mgtx_benchling_wrapper.context.benchling_context import BenchlingContext
import xlwings_package as xwp

ctx = BenchlingContext(client_id=..., client_secret=..., base_url=..., token_url=...)

# Single sheet, two schemas stacked
xwp.initialise_table_benchling(
    wb, {"Results": ["assaysch_abc123", "assaysch_def456"]}, ctx=ctx
)

# Multiple sheets
xwp.initialise_table_benchling(
    wb,
    {"Sheet1": ["assaysch_abc123"], "Sheet2": ["assaysch_def456"]},
    ctx=ctx,
)

apply_benchling_table_features

def apply_benchling_table_features(
    wb,
    results: list[SchemaInitResult],
    *,
    dropdowns: bool = True,
    column_colour_map: Optional[dict[str, str]] = None,
    prefill_rows: int = 1,
    dropdown_list_sheet: str = "Lists",
) -> list[str]:

Format initialised Benchling tables — the Benchling twin of apply_table_features. Call it after initialise_table_benchling, handing back its results. You only “mention the table”; each schema’s enum columns become in-cell dropdowns automatically, built from the option list already resolved onto the result (no second Benchling call). A subsequent paste_df_to_named_range reads the validation back, propagates it onto the pasted rows, and rejects off-list values.

prefill_rows defaults to 1 (one dropdown-ready, mesh-formatted row per table). On an empty table apply_table_features still auto-seeds one un-meshed row, so nothing is silently skipped.

Renamed from apply_benchling_dropdowns in 0.2.0.

Parameter Type Meaning
wb xlwings Book Workbook.
results list[SchemaInitResult] The list returned by initialise_table_benchling.
dropdowns bool Declare a dropdown on every enum column. Default True; False = colours only.
column_colour_map dict[str, str] or None {column_key: colour_token} (see apply_table_features); applied to whichever table(s) a key matches.
prefill_rows int Rows to pre-seed below each header. Default 1.
dropdown_list_sheet str Sheet hosting the dropdown option source lists. Default "Lists".

Returns: list[str] — the range_name of each table that received at least one feature. Raises: ValueError (a column_colour_map key matches no column in any table).

import xlwings_package as xwp

results = xwp.initialise_table_benchling(wb, {"Results": ["assaysch_abc123"]}, ctx=ctx)
xwp.apply_benchling_table_features(wb, results, column_colour_map={"grade": "purple"})

cleanup_stale_table_names

def cleanup_stale_table_names(
    wb,
    table_name: str,
    *,
    sheet: Optional[str] = None,
) -> dict[str, list[str]]:

Prune stale sheet-scoped names tied to a managed Benchling table. Deletes all #REF!-broken names (named ranges left behind when a user manually deletes a column in Excel). Ownership is determined by name pattern and the schema manifest.

Parameter Type Meaning
wb xlwings Book or None Workbook; falls back to default when None.
table_name str Schema range name (matches the {table_name}__schema_columns manifest).
sheet str or None (keyword-only) Restrict to one sheet; None sweeps every sheet hosting the manifest.

Returns: {"removed_ref": [...]} of sheet-qualified "<sheet>!<name>" strings naming the broken names that were pruned. Raises: ManifestNotFoundError if no manifest for table_name exists on the targeted sheet(s); WorksheetNotFoundError if sheet= names a missing sheet.

import xlwings_package as xwp
result = xwp.cleanup_stale_table_names(wb, "hplc_results")
# {'removed_ref': ['Results!old_col']}

result = xwp.cleanup_stale_table_names(wb, "hplc_results", sheet="Results")
# {'removed_ref': ['Results!old_col']}

See also: Benchling (single schema) guide, Benchling (multiple schemas) guide, stale-name cleanup.


Plates

Source: plate_functions.py. Supported preset formats: 6, 12, 24, 48, 96, 384, 1536 (or custom dimensions). Typical flow: create_plate_table (fill in sample data) → create_plate_layout (visual grid) → plate_layout_to_dataframe (read back).

create_plate_table

def create_plate_table(
    wb: xw.Book,
    *,
    sheet_name: str = _DEFAULT_SHEET_NAME,          # "plate sheet"
    sample_rows: int | None = None,
    extra_headers: Sequence[str] | None = None,
    table_title: str = _DEFAULT_TABLE_TITLE,        # "Plate Table"
    header_named_range: str = "plate_table",
    dropdown_list_sheet_name: str = "lists",
    dropdown_list_title: str = "plate dropdown list",
    plate_format: str = "96",
    plate_rows_count: int | None = None,
    plate_cols_count: int | None = None,
    start_col: int | str = 2,
    sanitize_formulas: bool = True,
) -> tuple[pd.DataFrame, xw.Sheet]:

Create a fresh source plate table sheet: title + bold orange header row, default columns (Sample No., Sample ID, Plate Position) plus any extra_headers, a sheet-scoped named range per header, and a Plate Position dropdown driven by the chosen format. Stores plate_format_key / plate_rows_count_key / plate_cols_count_key so the layout step can detect the shape automatically.

Parameter Type Meaning
wb xlwings Book Workbook.
sheet_name str Target sheet (default "plate sheet").
sample_rows int or None Number of data rows; defaults to the format’s total well count.
extra_headers sequence of str or None Extra columns appended after the defaults.
table_title str Title cell text (default "Plate Table").
header_named_range str Name for the header-row range (default "plate_table").
dropdown_list_sheet_name str Sheet for the dropdown source list (default "lists").
dropdown_list_title str Title for the dropdown source block.
plate_format str One of 6/12/24/48/96/384/1536 (default "96").
plate_rows_count / plate_cols_count int or None Custom dims; provide both or neither.
start_col int or Excel letter str Left edge of the table (default 2; must be >= 1).
sanitize_formulas bool Escape leading =/+/-/@ in written cells. Default True.

Returns: (DataFrame, Sheet) — the table data and the sheet it was written to. Raises: ValueError (only one of the custom-dim pair given; non-positive sample_rows / dims / start_col; invalid plate_format).

import xlwings_package as xwp
df, ws = xwp.create_plate_table(wb, plate_format="96", extra_headers=["Volume (uL)"])

create_plate_layout

def create_plate_layout(
    wb: xw.Book,
    *,
    plate_name: str,
    plate_title: str,
    value_named_ranges: Sequence[str],
    well_value_order: Sequence[str] | None = None,
    source_table_named_range: str = "plate_table",
    source_sheet_name: str = "plate sheet",
    target_sheet_name: str = "plate layouts",
    start_cell: str = "B2",
    append_if_sheet_exists: bool = True,
    sanitize_formulas: bool = True,
) -> xw.Sheet:

Render a visual well-plate grid from the source table. Plate shape is read automatically from the source sheet’s stored metadata. Each well stacks the requested variables in well_value_order (or value_named_ranges order). Wells are filled by matching the source Plate Position column when present, else by row order. Replacing an existing plate handles format changes (clears the old footprint, inserts/deletes rows for taller/shorter plates).

Parameter Type Meaning
wb xlwings Book Workbook.
plate_name str (keyword-only) Plate identity; used as the named-range token.
plate_title str (keyword-only) Title shown above the grid.
value_named_ranges sequence of str Source columns (tokens or display names) to lay out.
well_value_order sequence of str or None Stacking order within each well; defaults to value_named_ranges.
source_table_named_range str Source header range (default "plate_table").
source_sheet_name str Source sheet (default "plate sheet").
target_sheet_name str Layout sheet (default "plate layouts"; created if missing).
start_cell str Top-left anchor for a fresh plate (default "B2").
append_if_sheet_exists bool Stack below existing plates rather than overwrite. Default True.
sanitize_formulas bool Escape leading =/+/-/@ in well values. Default True.

Returns: the target Sheet. Raises: ValueError (missing plate_name/plate_title; unknown variable; source row count mismatch; duplicate plate positions); NamedRangeNotFoundError (source table range unresolvable); EmptyDataError (source table has no data rows); SideBySideNotSupportedError on a row overlap with another managed table/plate.

import xlwings_package as xwp
xwp.create_plate_layout(
    wb,
    plate_name="plate1",
    plate_title="Plate 1 — Concentrations",
    value_named_ranges=["sample_id", "concentration"],
)

paste_plate_data

def paste_plate_data(
    wb,
    *,
    plate_name: str,
    value_named_ranges: Sequence[str],
    well_value_order: Sequence[str] | None = None,
    source_table_named_range: str = "plate_table",
    source_sheet_name: str = "plate sheet",
    target_sheet_name: str = "plate layouts",
    sanitize_formulas: bool = True,
) -> xw.Sheet:

Refresh the values of an already-initialised plate without redrawing it. Unlike create_plate_layout, this never redraws the grid, borders, merges, or named ranges and never inserts/deletes rows — it locates the plate by its {plate}_plate_start / {plate}_plate_end named ranges and writes only the well values, so formatting and the user’s active tab survive untouched. The plate must already exist with a shape and variable count matching the source table (otherwise a clear error names the fix). Use this for a pure data refresh; use create_plate_layout to first render or re-shape the plate.

Parameter Type Meaning
wb xlwings Book Workbook containing source table and initialised plate.
plate_name str Plate to paste into (same value passed to create_plate_layout).
value_named_ranges Sequence[str] Source headers/tokens to write, one per stacked sub-row. Must match the count/order the plate was initialised with.
well_value_order Sequence[str] or None Explicit top-to-bottom order within each well. Defaults to value_named_ranges.
source_table_named_range str Named range of the source table. Default "plate_table".
source_sheet_name str Sheet holding the source table. Default "plate sheet".
target_sheet_name str Sheet holding the rendered plate. Default "plate layouts".
sanitize_formulas bool Escape leading =/+/- in well values. Default True.

Returns: the plate Sheet. Raises: ValueError (blank plate_name; plate not initialised — target sheet or _plate_start/_plate_end names missing; source shape/variable-count mismatch; missing variable or wrong source row count); NamedRangeNotFoundError (source table range unresolvable); EmptyDataError (source table has no data rows).

import xlwings_package as xwp

# Re-render once …
xwp.create_plate_layout(wb, plate_name="plate1", plate_title="Plate 1",
                        value_named_ranges=["sample_id", "concentration"])
# … then refresh values many times without touching the formatting.
xwp.paste_plate_data(wb, plate_name="plate1",
                     value_named_ranges=["sample_id", "concentration"])

get_plate_format_presets

def get_plate_format_presets() -> dict[str, dict[str, str]]:

Return the built-in plate-format presets with dimension and volume metadata (keys 6/12/24/48/96/384/1536). The returned dict is a copy — safe to mutate.

Parameters: none. Returns: dict[str, dict[str, str]] — e.g. presets["96"]["rows_x_columns"] == "8 x 12". Raises:

import xlwings_package as xwp
presets = xwp.get_plate_format_presets()
print(presets["384"]["recommended_working_volume"])   # "10-90 uL"

plate_layout_to_dataframe

def plate_layout_to_dataframe(
    wb: xw.Book,
    *,
    plate_name: str,
    sheet_name: str = "plate layouts",
    variables: Sequence[str] | None = None,
    rows: Sequence[str] | None = None,
    columns: Sequence[int] | None = None,
    return_format: Literal["wide", "long"] = "wide",
) -> pd.DataFrame:

Read a rendered plate layout back into a DataFrame, inferring the plate dimensions from the stored named ranges. Optional filtering by variables, plate rows, and plate columns.

Parameter Type Meaning
wb xlwings Book Workbook.
plate_name str (keyword-only) Plate to read.
sheet_name str Layout sheet (default "plate layouts").
variables sequence of str or None Subset of variables (display names or tokens).
rows sequence of str or None Subset of plate row labels (e.g. ["A", "B"]).
columns sequence of int or None Subset of plate column numbers.
return_format "wide" or "long" "wide" (default): one row per well; "long": one row per (well, variable).

Returns: pandas.DataFrame. Wide form always includes plate_name, plate_row, plate_col, plate_position plus a column per variable; long form adds variable_token, variable_display, value. Raises: ValueError (missing plate_name; bad return_format; sheet/plate ranges not found; empty rows/columns filter result).

import xlwings_package as xwp
df = xwp.plate_layout_to_dataframe(wb, plate_name="plate1", return_format="long")

See also: plates guide.


Formatting & plotting

Sources: formatting.py, plotting.py, excel_utils.py.

colour_rows

def colour_rows(
    wb,
    name_range: str,
    df: pd.DataFrame,
    mode: Literal["group_alternate", "equals_no", "values_match"],
    column_name: str,
    scope_sheet: Optional[str] = None,
    values_to_colour: Optional[Iterable] = None,
    colour_name: Optional[str] = None,
):

Apply row background colours to a table by column value. df must be in the same row order as the sheet.

Modes:

Parameter Type Meaning
wb xlwings Book Workbook.
name_range str Header named range (locates sheet + columns to colour).
df DataFrame Table data, sheet-row-aligned.
mode literal One of the three modes above.
column_name str Column driving the colouring.
scope_sheet str or None Resolve name_range within this sheet only.
values_to_colour iterable or None Required for "values_match".
colour_name str or None Required for "values_match"; a known name, CSS name (via webcolors), or #RRGGBB.

Returns: None (mutates cell colours). Raises: EmptyDataError if column_name is missing/all-null; ValueError for an unknown mode or missing mode-required args.

import xlwings_package as xwp
xwp.colour_rows(wb, "sample_table", df, mode="equals_no", column_name="Pass?")
xwp.colour_rows(
    wb, "sample_table", df, mode="values_match", column_name="status",
    values_to_colour={"FAIL"}, colour_name="#FFB6C1",
)

paste_plot_to_cell

def paste_plot_to_cell(
    workbook,
    df: pd.DataFrame,
    x_column: str,
    y_column: str,
    target_cell: str,
    sheet_name: str,
    *,
    title: str = "",
    x_label: str = "",
    y_label: str = "",
    width_px: int = 480,
    height_px: int = 320,
    dpi: int = 150,
    marker: str = "o",
    line: bool = True,
    best_fit: bool = True,
    r_squared: bool = True,
    log: str = "",
):

Build a matplotlib scatter plot from two DataFrame columns and paste it as a PNG image anchored at target_cell. Optional linear best-fit line and R² annotation; log scaling computes the fit on log-transformed values.

Parameter Type Meaning
workbook xlwings Book Workbook.
df DataFrame Data to plot.
x_column / y_column str Columns for the X / Y axes.
target_cell str Anchor cell for the image top-left (e.g. "C5").
sheet_name str Target sheet.
title / x_label / y_label str (keyword-only) Labels; sensible defaults derived from columns.
width_px / height_px / dpi int (keyword-only) Image size (480×320) and resolution (150 DPI).
marker str (keyword-only) Point marker (default "o").
line / best_fit / r_squared bool (keyword-only) Connect points / overlay fit / annotate R². All True by default.
log str (keyword-only) "x", "y", "xy"/"both", or "" (linear).

Returns: None (inserts the picture). Raises: WorksheetNotFoundError if sheet_name is missing; EmptyDataError if a plot column is missing or no numeric values survive coercion.

import xlwings_package as xwp
xwp.paste_plot_to_cell(
    wb, df, x_column="conc", y_column="signal",
    target_cell="H2", sheet_name="Results", best_fit=True, log="x",
)

add_dropdown_list

def add_dropdown_list(
    wb: xw.Book,
    *,
    list_sheet_name: str,
    title: str,
    options: Sequence[str],
    start_column: str = "A",
    start_row: int = 1,
    column_step: int = 2,
) -> tuple[xw.Sheet, xw.Range]:

Write a titled option source list to the first free column of a list sheet (scanning columns from start_column in steps of column_step). This lays out the values only — it does not attach an in-cell Data Validation dropdown; callers (e.g. create_plate_table) add Validation themselves pointing at the returned options range.

Parameter Type Meaning
wb xlwings Book Workbook.
list_sheet_name str (keyword-only) Sheet to write the list to (created if missing).
title str (keyword-only) Title cell text (required).
options sequence of str (keyword-only) Option values (blanks dropped).
start_column str Column letter to start scanning from. Default "A".
start_row int Title row. Default 1.
column_step int Column stride when scanning for free space. Default 2.

Returns: (list_sheet, options_range) — the sheet and the value range (title row excluded). Raises: ValueError if title is empty, options has no non-empty value, or column_step <= 0.

import xlwings_package as xwp
sheet, opts = xwp.add_dropdown_list(
    wb, list_sheet_name="lists", title="Operators", options=["Alice", "Bob"]
)

See also: normal tables guide, plates guide.


File IO

Source: file_io.py. These open Tkinter dialogs / Excel alerts; user cancellation raises UserCancelledError.

choose_save_path

def choose_save_path(
    save_as: str = "csv",
    default_file_name: str = "output",
    title: str = "Choose where to save",
) -> str:

Open a save dialog and return the chosen absolute path.

Parameter Type Meaning
save_as str "csv", "excel"/"xlsx", or "auto" (user picks in dialog).
default_file_name str Suggested base filename.
title str Dialog title.

Returns: str absolute path. Raises: ValueError for an invalid save_as; UserCancelledError if the user cancels.

import xlwings_package as xwp
path = xwp.choose_save_path(save_as="xlsx", default_file_name="run42")

read_file_to_df

def read_file_to_df(
    sheet_name=None,
    file_type: str = "csv",
    show_pre_alert: bool = True,
    *,
    sep: str | None = None,
    header_marker: str | None = None,
) -> pd.DataFrame:

Prompt the user to select a CSV/TXT/XLSX file (optional pre-alert in the active Excel app) and return it as a DataFrame. .csv and .txt are read as delimited text; .xlsx is read with pd.read_excel.

Parameter Type Meaning
sheet_name str, list, or None Excel sheet selector; None reads the first sheet (keeps the -> DataFrame contract).
file_type str "csv" (lists .csv and .txt), "excel", or "all".
show_pre_alert bool Show an OK/Cancel alert before the picker. Default True.
sep str or None Delimiter for .csv/.txt. Defaults to a comma for .csv and a tab for .txt.
header_marker str or None Skip a metadata preamble: lines are skipped until the first whose first field equals this marker, which is then read as the header. .csv/.txt only.

Returns: pandas.DataFrame. Raises: ValueError for an invalid file_type, a wrong selected extension, an unreadable file, a header_marker that is not found, or header_marker combined with a multi-character sep; UserCancelledError if the user cancels the alert or picks no file.

import xlwings_package as xwp
df = xwp.read_file_to_df(file_type="all")
df = xwp.read_file_to_df(file_type="csv", header_marker="Sample")  # skip a .txt preamble

save_dataframe

def save_dataframe(
    df: pd.DataFrame,
    save_path: str | None = None,
    save_as: str = "auto",
    default_file_name: str = "output",
    show_dialog: bool = True,
    dialog_message: str = "Sequence saved to selected directory.",
    actions: Sequence[str] = FILE_ACTIONS,
) -> str:

Save a DataFrame to CSV/XLSX — prompting for the path when save_path is None — and optionally show a post-save dialog. The dialog’s buttons are chosen by actions (see offer_file_actions).

Parameter Type Meaning
df DataFrame Data to save.
save_path str or None Full output path; None opens a save dialog.
save_as str "csv", "excel"/"xlsx", or "auto" (infer from extension / dialog).
default_file_name str Suggested name when a dialog is shown.
show_dialog bool Show the post-save dialog (set False for batch). Default True.
dialog_message str Message shown in the dialog.
actions Sequence[str] Which post-save buttons to offer ("open_file", "open_folder", "copy_path"). Default: all three.

Returns: str — the absolute path written. Raises: ValueError for an invalid save_as, unresolved extension, or unknown actions name; UserCancelledError if a save dialog is cancelled.

import xlwings_package as xwp
out = xwp.save_dataframe(df, save_path=r"C:\data\run42.xlsx", show_dialog=False)
# Save, then only offer "Copy file path":
out = xwp.save_dataframe(df, actions=["copy_path"])

offer_file_actions

FILE_ACTIONS = ("open_file", "open_folder", "copy_path")

def offer_file_actions(
    save_path: str,
    message: str = "File saved.",
    actions: Sequence[str] = FILE_ACTIONS,
) -> None:

Show a small dialog offering to open the file, open its folder (File Explorer, file selected), or copy the full file path to the clipboard. An OK button is always shown. Copying leaves the dialog open (so you can then open the file/folder too); Open file / Open folder / OK close it. save_dataframe calls this for you; call it directly when you saved a file some other way and still want the prompt.

Parameter Type Meaning
save_path str Path the buttons act on.
message str Headline shown above the path. Default "File saved.".
actions Sequence[str] Buttons to offer, any subset of FILE_ACTIONS. Order/duplicates ignored — buttons render in FILE_ACTIONS order. Default: all three.

Returns: None. Raises: ValueError if actions is a bare string or contains a name outside FILE_ACTIONS.

import xlwings_package as xwp
xwp.offer_file_actions(r"C:\data\run42.xlsx", actions=["open_file", "copy_path"])

resolve_workbook_path

def resolve_workbook_path(path_or_folder: str) -> str:

Resolve a workbook path from either a direct .xlsm/.xlsx file path or a folder containing exactly one workbook.

Parameter Type Meaning
path_or_folder str A workbook file path, or a folder holding exactly one workbook.

Returns: str absolute workbook path. Raises: ValueError if the path doesn’t exist, is the wrong file type, the folder has zero workbooks, or the folder has more than one.

import xlwings_package as xwp
path = xwp.resolve_workbook_path(r"C:\runs\today")   # single workbook in folder

See also: common patterns.


DataFrame utils

Source: dataframe_utils.py. Pure pandas helpers (no Excel COM).

strip_strings

def strip_strings(value):

Strip whitespace across common container/string types. Strings are stripped; lists/dicts have their string members stripped and empty-string entries dropped; DataFrames have every string cell stripped; anything else is returned as-is.

Parameter Type Meaning
value str, list, dict, DataFrame, or any Value to clean.

Returns: the cleaned value (same type for str/list/dict/DataFrame). Raises:

import xlwings_package as xwp
xwp.strip_strings(["  a ", "", "b "])     # ['a', 'b']

limit_columns_to_decimals

def limit_columns_to_decimals(df: pd.DataFrame, columns: Iterable[str], decimals: int = 3) -> pd.DataFrame:

Round numeric values in the selected columns to a fixed precision (booleans and non-numeric values are left untouched). Mutates and returns df.

Parameter Type Meaning
df DataFrame Input frame.
columns iterable of str Columns to round.
decimals int Decimal places. Default 3.

Returns: the DataFrame (same object, columns rounded). Raises: EmptyDataError if a named column is absent.

import xlwings_package as xwp
df = xwp.limit_columns_to_decimals(df, ["concentration"], decimals=2)

rename_dtype_columns

def rename_dtype_columns(
    df: pd.DataFrame,
    org_name: list[str],
    new_name: list[str],
    desired_dtype: list,
) -> pd.DataFrame:

Rename selected columns and cast each to the corresponding dtype. The three lists are zipped positionally and must be equal length. Returns a copy.

Parameter Type Meaning
df DataFrame Input frame.
org_name list[str] Existing column names.
new_name list[str] New names (aligned to org_name).
desired_dtype list Target dtypes (aligned to new_name).

Returns: a new DataFrame with renamed, recast columns. Raises: ValueError if the lists differ in length; EmptyDataError if any org_name column is missing.

import xlwings_package as xwp
df = xwp.rename_dtype_columns(df, ["Conc"], ["concentration"], [float])

check_missing_columns

def check_missing_columns(df: pd.DataFrame, required_columns: Iterable[str]) -> pd.DataFrame:

Validate that df contains every required column. Returns df unchanged on success.

Parameter Type Meaning
df DataFrame Frame to validate.
required_columns iterable of str Columns that must be present.

Returns: the same DataFrame. Raises: EmptyDataError listing any missing columns.

import xlwings_package as xwp
xwp.check_missing_columns(df, ["sample_id", "concentration"])

check_case_sensitive_entries

def check_case_sensitive_entries(
    df: pd.DataFrame,
    column_name: str,
    expected_value: str,
    display_name: str | None = None,
) -> pd.DataFrame:

Require an exact, case-sensitive value in one column. Adds a "{name}?" flag column ("Yes"/"No") and raises if any value matches case-insensitively but not exactly. Returns a copy.

Parameter Type Meaning
df DataFrame Input frame.
column_name str Column to check.
expected_value str The exact value required.
display_name str or None Label for the flag column; defaults to expected_value.

Returns: a new DataFrame with the flag column added. Raises: EmptyDataError if column_name is absent; ValueError if a case-mismatched entry is found.

import xlwings_package as xwp
df = xwp.check_case_sensitive_entries(df, "gene", "GFP")   # rejects "gfp"/"Gfp"

See also: common patterns.


UI

Source: ui.py. Thin wrappers over the active Excel app’s alert.

display_message

def display_message(message: str) -> None:

Show a message via the active Excel app alert.

Parameter Type Meaning
message str Text to display.

Returns: None. Raises: RuntimeError if there is no active Excel app.

import xlwings_package as xwp
xwp.display_message("Import complete.")

display_message_and_exit

def display_message_and_exit(message: str, exit_code: int = 1) -> None:

Show a message, then terminate via raise SystemExit(exit_code).

Parameter Type Meaning
message str Text to display.
exit_code int Exit code. Default 1.

Returns: does not return normally. Raises: SystemExit (always); RuntimeError if there is no active Excel app.

import xlwings_package as xwp
xwp.display_message_and_exit("Validation failed — aborting.", exit_code=2)

Exceptions

Source: exceptions.py. All custom errors derive from XlwingsPackageError, so a single except XlwingsPackageError: catches everything this package raises. (Several functions additionally raise built-in ValueError / TypeError for caller-side input validation — see each function above.)

XlwingsPackageError

class XlwingsPackageError(Exception):

Base class for all package errors. Base: Exception. Catch this to handle any error raised by the package.

import xlwings_package as xwp
try:
    xwp.import_excel_table(wb, "missing_table")
except xwp.XlwingsPackageError as exc:
    print("package error:", exc)

UserCancelledError

class UserCancelledError(XlwingsPackageError):

Base: XlwingsPackageError. Raised when the user cancels an interactive operation — the pre-alert or file/save dialog in read_file_to_df, choose_save_path, save_dataframe.

NamedRangeNotFoundError

class NamedRangeNotFoundError(XlwingsPackageError):

Base: XlwingsPackageError. Raised when a named range cannot be resolved (by resolve_named_range and the table read/paste functions), or when a schema’s range is not found during a Benchling reinit.

WorksheetNotFoundError

class WorksheetNotFoundError(XlwingsPackageError):

Base: XlwingsPackageError. Raised when a worksheet doesn’t exist — e.g. get_worksheet, paste_plot_to_cell, or a sheet= argument to cleanup_stale_table_names.

EmptyDataError

class EmptyDataError(XlwingsPackageError):

Base: XlwingsPackageError. Raised when required data is missing or empty — an empty table or all-null column in import_excel_table, a zero-row DataFrame in paste_df_to_named_range, missing columns in the DataFrame utils, or missing/empty plot columns.

TableSpaceOccupiedError

class TableSpaceOccupiedError(XlwingsPackageError):

Base: XlwingsPackageError. Raised when a write would shift or destroy non-table content — loose cells or floating shapes in the danger zone below a managed table (lane band), or cells a fresh Benchling header would land on. The exception message names the colliding object and provides a move instruction. (A DataFrame wider than the declared header is a ValueError, not this — that’s caller-side input validation.)

ManifestNotFoundError

class ManifestNotFoundError(XlwingsPackageError):

Base: XlwingsPackageError. Raised by cleanup_stale_table_names when the table has no {table_name}__schema_columns manifest on the targeted sheet(s) — i.e. it was never initialised by initialise_table_benchling (or the manifest name was deleted manually).

ExcelColumnOverflowError

class ExcelColumnOverflowError(XlwingsPackageError):

Base: XlwingsPackageError. Raised when a placement would land a header column past Excel’s last column (XFD = 16,384) — auto-widening in paste_df_to_named_range, or a fresh schema placement in initialise_table_benchling.

ColumnOrderMismatchError

class ColumnOrderMismatchError(XlwingsPackageError):

Base: XlwingsPackageError. Raised by paste_df_to_named_range when df.columns don’t match the existing header positions (reordered / inserted / dropped middle column), which would silently misalign the positional write. Fires before any destructive op; matches against either the per-column range token or the header display value. Fix by reordering the DataFrame or re-initialising the table.

SideBySideNotSupportedError

class SideBySideNotSupportedError(XlwingsPackageError):

Base: XlwingsPackageError. Raised when a managed table/plate on the sheet has a row range overlapping the operating table’s rows within the same lane (column-overlapping tables). Tables in disjoint column bands are in separate lanes and are never checked against each other. Fires at init (initialise_table_named_ranges, create_plate_layout) and at paste (paste_df_to_named_range) before any destructive operation.

See also: stacked tables & re-init concept.