Changelog
All notable changes to mgtx-xlwings-utils are documented here. The format is based on Keep a Changelog, and this project aims to follow Semantic Versioning.
[0.2.1] — 2026-07-10
Added
offer_file_actions— the post-save “Open file / Open folder” dialog is now a public function (the internal_saved_dialogname still works as an alias), with a new Copy file path button and anactions=argument selecting which buttons to show (any subset of"open_file","open_folder","copy_path"; default all three). Copy file path copies the full path to the clipboard and leaves the dialog open so you can still open the file or its folder; those buttons (and OK) close it. Call it directly after saving a file any other way.save_dataframe(..., actions=...)— forwards the same button selection to the post-save dialog, so a batch step can, e.g., only offer"copy_path".
Fixed
- Copying the path now persists after the dialog closes. The button copies
via the OS clipboard (PowerShell
Set-Clipboard, UTF-8) instead of Tkinter’s delayed-render clipboard, which silently dropped the value when the window was destroyed.
[0.2.0] — 2026-07-06
A formatting + dropdowns release, plus plate and paste plumbing changes. Several breaking renames/removals are grouped under Changed.
Added
apply_table_features— one call to apply column colour fills, prefill rows, and in-cell dropdowns to any managed table (manual or Benchling). Call it after the table’s init function. On an otherwise-empty table it auto-seeds a data row so a colour/dropdown has a cell to attach to.apply_benchling_table_features— the Benchling twin: hand back theresultsfrominitialise_table_benchlingand every schema enum column becomes an in-cell dropdown automatically, seeded from the option list already resolved onto the result (no second Benchling call).paste_plate_data— refresh an already-initialised plate’s values without redrawing the grid, borders, merges, or named ranges (split out fromcreate_plate_layout). Formatting and the user’s active tab survive untouched.paste_df_to_named_range(..., borders="mesh")— draw the cell-grid border mesh over the pasted extent.- Format-preserving paste.
paste_df_to_named_rangenow reads a formatted table’s template row back and propagates its column colours and dropdown validations across the pasted rows, rejecting off-list values in a dropdown column.
Changed
- Breaking —
apply_benchling_dropdownsrenamed toapply_benchling_table_features(it now does colours as well as dropdowns). - Breaking — default table formatting is now the two-tone purple theme,
applied through the shared
apply_table_featurespath. - Breaking —
paste_df_to_named_rangerejects a named (orMultiIndex) DataFrame index instead of silently dropping it; reset or rename it into a column first (#19). - Breaking — plate per-column named ranges collapsed from O(rows × vars) to O(vars) and the dead per-column plate named ranges were dropped (#21). Plate reinit now purges orphaned variable names.
- Breaking — the dead
paste(dropdowns=)parameter was removed; declare dropdowns viaapply_table_features/apply_benchling_table_features.
Fixed
- Dropdown matching no longer trips over the formula-safety escape apostrophe, and the view stays on the table while dropdowns are built.
- Single-letter Benchling schema anchors no longer alias a column (#11).
- Source docstrings/comments are ASCII-only, with a regression guard (#12).
- Integration-test artefacts are written off the OneDrive sync path so the live-Excel suite no longer hangs mid-run (#13).
- Whole-column/row named ranges no longer hang a paste. The danger-zone
scan tests each name’s extent rectangle instead of materialising every
covered cell, so a sheet carrying a
=$A:$A-style name pastes instantly. - Paste-mode re-runs stack correctly.
initialise_table_benchling(mode="paste")advances past a reinitialised table’s real extent, so a newly-added schema stacks below it instead of colliding at the anchor (TableSpaceOccupiedError). - A first paste larger than the 1000-row scan window no longer inserts
~
n - 1000spurious blank rows below the table. - Per-table dropdown option lists no longer clobber each other — two tables
that share a column header (e.g. both “Status”) get separate namespaced
option columns, and dropdowns on a sheet whose name contains an apostrophe
(e.g.
O'Brien) round-trip correctly. check_case_sensitive_entriescompares by case-insensitive equality, not a regex substring match, so"Plasmid-derived"no longer false-matches"Plasmid"and a value like"C++"no longer raisesre.error.get_named_range_results(strip=True)is shape-preserving — 1-D length and 2-D shape are kept (blank/Nonecells survive) so positional reads stay aligned.import xlwings_packageno longer hijacks the host matplotlib backend;matplotlib.use("Agg")is deferred intopaste_plot_to_cell, which also sorts a numeric x-axis numerically and rejects non-positive values on a log axis.- A schema id listed twice for one sheet raises
ValueErrorinstead of silently writing the table twice and clobbering the first’s named ranges. - Clearer errors on edge inputs: a zero-column DataFrame raises
EmptyDataError;delete_sheeton a missing sheet raisesWorksheetNotFoundError;scan_named_range_tableanchored on row 1 raisesNamedRangeNotFoundError.
Performance
- Plate draw and paste use bulk range writes instead of per-cell writes (#9).
Internal
- Deduplicated shared helpers (name-token extraction, column limits, title styling, grid normalisation) and made the paste header pre-flight O(names) instead of O(columns x names). No public API or behaviour change.
[0.1.35] — 2026-06-22
Changed
- Autopaste now separates a new Benchling table from any content above it.
initialise_table_benchling(mode="autopaste")previously applied thetable_gapseparation only when stacking below another managed Benchling table (one carrying a*_table_startbookmark). A table placed below a manually built table or stray notes got zero blank rows, so its merged title row stuck directly to the content above.table_gapblank rows are now inserted below manual tables, notes, and managed tables alike.- An empty sheet still starts at row 1 (no leading gap).
- Pass
table_gap=0to restore the previous flush placement.
[0.1.3] — 2026-06-08
Changed
initialise_table_benchlingnow takes aschema_mapdict ({sheet_name: [schema_id, ...]}) and returns a typedlist[SchemaInitResult](each with a tuple ofColumnInfo) instead of alist[dict]. Result fields are accessed by attribute (e.g.result.action,column.range_name).
Added
- Per-column named ranges are namespaced as
{anchor}__{token}so multiple schemas can share a sheet without name collisions; readers still reference columns by their bare token.
[0.1.0] — [0.1.2]
- Initial PyPI releases: the package-first xlwings + pandas helper API (named-range tables, plate functions, Benchling table initialisation, formatting, plotting, and file I/O).