Repository navigation
Releases
v0.9.0
ExStruct v0.9.0
ExStruct v0.9.0 is a major performance-focused release that substantially reduces extraction latency, memory usage, repeated workbook parsing, and unnecessary heavy dependency loading.
The biggest change is a new direct OOXML extraction path for light mode. Supported .xlsx and .xlsm files can now be read directly from the workbook ZIP package without loading openpyxl, xlwings, pandas, or SciPy on the normal path.
This release also significantly improves verbose color extraction by avoiding thousands of unnecessary per-cell Excel COM calls while preserving Excel-rendered conditional formatting semantics.
Highlights
Direct OOXML extraction for light mode
light mode now uses a shared direct OOXML extraction session for supported .xlsx and .xlsm workbooks.
The new path extracts:
- cells and shared strings
- formulas
- hyperlinks
- merged cells
- print areas
- explicit and heuristic table candidates
- shapes and connectors
- charts
A normal light extraction now opens the workbook archive once and does not import:
- openpyxl
- xlwings
- pandas
- SciPy
If the direct OOXML reader encounters unsupported workbook content, ExStruct safely discards the partial result and restarts extraction through the existing openpyxl compatibility path.
Much faster light extraction
Representative local Windows benchmarks showed substantial improvements while producing identical serialized output.
| Fixture | Previous light | v0.9.0 |
|---|---|---|
| 24-sheet workbook | 4102 ms | 120 ms |
| Large workbook | 1596 ms | 596 ms |
| Style-heavy workbook | 535 ms | 178 ms |
| Table-heavy workbook | 242 ms | 72 ms |
After switching the default light path fully to direct OOXML, additional cold-start improvements were observed:
| Fixture | openpyxl path | OOXML path |
|---|---|---|
| Small | 983 ms | 428 ms |
| Sparse | 823 ms | 254 ms |
| 24 sheets | 840 ms | 333 ms |
| Large / 60,000 cells | 1626 ms | 1060 ms |
Peak Python memory usage was also lower across all measured light-mode fixtures.
These are local benchmark results and should not be interpreted as universal timing guarantees.
Faster verbose color extraction
Previously, rendered background-color extraction queried Excel's DisplayFormat separately for every cell in the used range.
v0.9.0 introduces a hybrid strategy:
- ordinary saved RGB fills are read directly from workbook styles
- Excel COM is used only for conditional-formatting candidates and ambiguous cases
- unsupported or unsafe cases fall back to the previous full COM scan
On the measured 300 × 20-cell fixtures:
| Fixture | DisplayFormat calls | Legacy | v0.9.0 |
|---|---|---|---|
| Static fills | 6,000 → 0 | 15.5–18.7 s | 0.084–0.100 s |
| Sparse conditional formatting | 6,000 → 30 | 12.8–17.0 s | 0.144–0.208 s |
All 24 paired color-map comparisons in the benchmark matched exactly.
Excel remains the source of truth for rendered conditional-formatting colors; ExStruct does not attempt to reimplement Excel's conditional-formatting engine.
Other performance improvements
Removed pandas from production extraction
Cell extraction no longer converts worksheets through pandas DataFrames.
The production path now reads worksheet data directly while preserving existing:
- value normalization
- dates
- cached formula values
- hyperlinks
- sparse-row behavior
- serialized output
pandas is now a development/benchmark dependency rather than a production extraction dependency.
Shared openpyxl extraction sessions
Compatibility extraction now reuses an extraction-scoped openpyxl workbook instead of repeatedly reopening the same file.
For multi-sheet workbooks, this removes a major source of redundant ZIP parsing and workbook object construction.
Formula extraction still opens a separate data_only=False workbook only when required.
SciPy is now optional
SciPy has moved out of the mandatory dependency set.
Install the optional accelerator with:
pip install "exstruct[fast]"Without SciPy, ExStruct continues to use its Python border-clustering implementation.
Lighter backend imports
COM and rendering implementations are now loaded lazily.
Light extraction no longer pays the import cost for Excel automation backends that it does not use.
Reliability improvements
- Fixed a lifecycle issue where a newly created Excel instance could remain running if workbook opening failed.
- Existing/borrowed Excel applications and workbooks remain under caller ownership.
- OOXML fallback restarts the complete compatibility extraction rather than mixing partial results from multiple backends.
- Sparse worksheets no longer materialize large numbers of blank cells during extraction.
- Improved handling of relocated OOXML workbook/drawing parts.
- Preserved rich/plain shared-string segment ordering.
- Reduced repeated merge and hyperlink scans for sparse worksheets.
COM-first evaluation
As part of this release, a bulk COM-based cell extraction path was prototyped and benchmarked for standard and verbose modes.
It was not adopted as the public default.
The experiment showed no consistent performance advantage and introduced semantic differences for:
- Excel dates represented through
Value2 - formula recalculation
- missing formula caches
- unsaved workbook edits
ExStruct therefore continues to use the current saved-file-first architecture for standard and verbose extraction.
Compatibility
v0.9.0 is intended to preserve the existing public extraction output contract.
Performance work was validated with output-parity tests across representative workbooks, including cells, formulas, links, tables, merged ranges, shapes, charts, and color maps.
Unsupported direct OOXML cases continue to use the existing compatibility backend automatically.
Installation
pip install -U exstructOptional SciPy acceleration:
pip install -U "exstruct[fast]"Summary
v0.9.0 changes ExStruct's extraction architecture from:
repeated workbook parsing
+ heavyweight intermediate readers
+ eager backend imports
+ per-cell COM formatting calls
to:
shared extraction sessions
+ direct OOXML where possible
+ lazy compatibility backends
+ targeted COM only where Excel rendering is required
The result is significantly faster and lighter extraction while retaining compatibility with the existing ExStruct output model.
v0.8.2
v0.8.1
v0.8.0
v0.8.0 Release Notes
This release publishes the April 2026 extraction work: stronger pure-Python rich
extraction in light mode, corrected print-area defaults across public
entrypoints, and LibreOffice / OOXML resilience hardening.
Highlights
lightnow acts as the pure-Python OOXML-rich baseline for.xlsx/
.xlsm, so non-COM environments can emit best-effort:- shapes
- connectors / arrows
- charts
lightnow keepsprint_areasby default across:extract(...)process_excel(...)ExStructEngine- CLI extraction and
--print-areas-dir
libreofficenow seeds the same OOXML baseline first and then applies UNO
enrichment when available, so fallback paths preserve already recovered rich
artifacts where safe.- LibreOffice workbook lifecycle handling is more robust for custom
session_factoryintegrations via typed workbook handles and session-owned
close semantics. - OOXML drawing parsing is more resilient and more efficient:
- malformed or corrupt drawing parts now fail per sheet instead of dropping
healthy workbook siblings - worksheet metrics are read with streaming XML parsing
- row/column offset lookups now use cached cumulative offsets
- malformed or corrupt drawing parts now fail per sheet instead of dropping
Compatibility Notes
- No new extraction CLI commands were added in
v0.8.0. lightmode behavior changed intentionally:- previous releases treated
lightas cells + table candidates only v0.8.0adds best-effort OOXML shapes / connectors / charts for OOXML
workbooks and keeps print areas by default
- previous releases treated
.xlsremains outside the new OOXML-rich baseline; the new non-COM rich path
applies to.xlsx/.xlsm.- Serialized backend metadata may now report
python_ooxmlprovenance when
backend metadata output is enabled. - MCP tool names and payload shapes remain compatible; the release changes the
extraction content available behind existing interfaces rather than adding a
new transport contract.
Notes
- The repository docs/build path still has a pre-existing
mkdocstrings
failure indocs/api.md; this issue was already reproducible before the
v0.8.0extraction work and is not introduced by this release. - Review-driven hardening after the initial implementation also restored
process_excel()auto-filter behavior, corrected stale README / architecture
wording, and prevented OOXML baseline seeding failures from crashing the
LibreOffice pipeline.
v0.7.1
v0.7.1 Release Notes
This patch release publishes the CLI and package import startup optimization work
completed under issues #107, #108, and #109.
Highlights
- The extraction CLI now always shows
--auto-page-breaks-dirin help output
and validates support only when the option is actually used at runtime. - Lightweight CLI paths now avoid unnecessary heavy imports:
exstruct --help- extraction-style argv that do not route to edit commands
exstruct ops list
- Public package imports are lighter:
import exstructimport exstruct.engine- exported names from
exstructandexstruct.editremain compatible while
loading implementation modules lazily
- Regression coverage now locks the startup boundary and validates that
lightweight import paths do not eagerly load extraction, edit, MCP, render,
orpydanticdependencies.
Startup impact
Compared with v0.7.0, local median startup timings on the same Python
environment improved by approximately:
python -m exstruct.cli.main --help:2195 ms -> 64 ms(34.4xfaster)import exstruct:1039 ms -> 55 ms(19.0xfaster)python -m exstruct.cli.main ops list:1143 ms -> 207 ms(5.5xfaster)import exstruct.engine:1005 ms -> 223 ms(4.5xfaster)
These measurements were taken locally against v0.7.0 and the current
v0.7.1 code using the same virtual environment and direct source imports, so
exact numbers will vary by machine and startup conditions.
Notes
- No new CLI commands were added in
v0.7.1. - MCP tool names and payload shapes remain compatible in
v0.7.1. - Backend selection policy remains
auto/com/openpyxl. - The edit CLI
validatesubcommand once again propagatesRuntimeError
instead of converting it into handled CLI error output.
v0.7.0
v0.7.0 Release Notes
This release publishes the workbook editing work completed under issue #99,
including the new exstruct.edit API surface, the editing CLI, compatibility
follow-ups, and maintainer-facing documentation needed to keep the layering
clear for future changes.
Next steps
Currently, the CLI startup is slow due to issues with the initial implementation,
so we plan to release an update in the near future to improve its performance.
Highlights
- Added a first-class workbook editing API under
exstruct.edit, including:- public
patch_workbook()/make_workbook()entrypoints - public request/result models
- shared patch-op schema discovery helpers
- public
- Added public editing CLI commands under
exstruct:patchmakeopsvalidate
- Clarified the editing architecture split so
exstruct.editis the canonical
core and MCP remains the host-managed integration / compatibility layer. - Updated public docs to explain canonical usage across Python, CLI, and MCP,
including the recommendeddry_run -> inspect -> applyworkflow and the
backend="auto"caveat for same-engine comparisons. - Added maintainer-facing documentation coverage for editing architecture,
specs, ADR alignment, and agent workflow expectations used during the issue
#99closeout. - Fixed release-significant review follow-ups, including:
- top-level
sheetfallback while preservingop.sheetprecedence - legacy monkeypatch compatibility across compatibility shims
- rename-reservation cleanup on openpyxl failure paths
- dry-run / backend / CLI failure wording drift in docs
- top-level
Notes
- The legacy extraction CLI invocation (
exstruct INPUT.xlsx ...) is unchanged. - MCP tool names and payload shapes remain compatible in
v0.7.0. - Patch backend policy remains
auto/com/openpyxl; this release does
not change backend selection semantics.
v0.6.1
v0.6.1 Release Notes
This patch release hardens Windows LibreOffice smoke coverage and runtime
resolution for the libreoffice extraction path.
Highlights
- Added a dedicated Windows GitHub Actions LibreOffice smoke job:
- runs on
windows-2025 - installs
libreoffice-fresh - discovers
EXSTRUCT_LIBREOFFICE_PATHand bundled LibreOffice Python - runs
tests/core/test_libreoffice_smoke.py -m libreofficewith
RUN_LIBREOFFICE_SMOKE=1
- runs on
- Improved Windows runtime discovery:
soffice.comis now preferred oversoffice.exewhen available- bundled LibreOffice Python detection now covers
python-core-*layouts
- Hardened LibreOffice bridge subprocess startup on Windows:
- bridge probe, handshake, and extraction now run from the runtime Python
directory - the runtime directory is prepended to
PATHfor UNO import and DLL
resolution
- bridge probe, handshake, and extraction now run from the runtime Python
- Reduced smoke-gate false negatives:
- slow
soffice --versionprobes are retried with a longer timeout - if version probing still times out, the smoke gate falls back to a
short-lived LibreOffice session probe before marking the runtime
unavailable
- slow
- Added regression tests for Windows runtime normalization, bundled Python
discovery, bridge subprocess environment handling, and timeout fallback.
Notes
- This is a reliability-focused patch release for Windows LibreOffice CI and
runtime detection. - No public API or output schema changes were introduced in this release.
v0.6.0
v0.6.0 Release Notes
This release adds a new best-effort libreoffice extraction mode for
non-COM environments and extends shape/chart metadata with provenance fields.
Highlights
- Added
mode="libreoffice"across the Python API, CLI, and MCP server. - Added early validation for
.xls+mode="libreoffice"with a clear error. - Added extraction-only validation for
mode="libreoffice":- rejects PDF/PNG rendering
- rejects auto page-break export
- Added
FallbackReason.LIBREOFFICE_UNAVAILABLEand
FallbackReason.LIBREOFFICE_PIPELINE_FAILED. - Added backend metadata to shapes/charts:
provenanceapproximation_levelconfidence- serialized output now keeps these fields opt-in via
include_backend_metadata
- Added OOXML-based best-effort reconstruction for:
- shapes
- connectors
- charts
- Added a LibreOffice runtime helper so server/Linux/macOS environments can
opt into rich extraction without Excel COM. - Added bundled bridge compatibility probing for LibreOffice Python runtime
selection, including fail-fast handling for incompatible
EXSTRUCT_LIBREOFFICE_PYTHON_PATHoverrides. - Added a required Linux GitHub Actions smoke job that installs LibreOffice
python3-unoand runs thepytest.mark.libreofficesample smoke test.
Notes
libreofficeis available for.xlsx/.xlsmonly.libreofficeis best-effort and not a strict subset of COM output.- v1 does not add LibreOffice PDF/PNG rendering or auto page-break extraction.
v0.5.3
v0.5.3 Release Notes
This patch release improves capture_sheet_images subprocess reliability and
observability, and updates MCP guidance for safe production rollout.
Highlights
- Added dedicated subprocess worker entrypoint:
python -m exstruct.render.subprocess_workeris now used for
capture_sheet_imagessubprocess mode.- worker bootstrap is decoupled from parent
__main__restoration.
- Updated default runtime behavior:
- MCP now defaults
EXSTRUCT_RENDER_SUBPROCESS=1based on profile-comparison
runs showing stable behavior in both modes. EXSTRUCT_RENDER_SUBPROCESS=0remains available to force in-process mode.
- MCP now defaults
- Improved timeout and failure diagnostics:
- wait ordering now prioritizes result receipt before join wait to reduce
false timeout failures after successful worker output. - stage-aware error reporting (
startup/join/result/worker) now
includes actionable context and stderr snippets when available.
- wait ordering now prioritizes result receipt before join wait to reduce
- Documentation updates:
- MCP
exstruct_capture_sheet_imagesis marked Experimental. - README/MCP docs now include subprocess timeout tuning guidance, including
EXSTRUCT_RENDER_SUBPROCESS_STARTUP_TIMEOUT_SEC.
- MCP
Notes
- No new patch operations were added in this release.
- MCP tools include the experimental
exstruct_capture_sheet_imagespath updated in this release. - This is a reliability-focused patch release for image-capture execution paths.
v0.5.2
v0.5.2 Release Notes
This patch release restores mixed chart/table patch workflows on COM-backed
execution and clarifies error behavior for non-COM environments.
Highlights
- Restored mixed-op support:
create_chartandapply_table_stylecan be executed in one request when
the backend resolves to COM (backend="com"or COM-available
backend="auto").
- Improved error behavior:
- when COM is unavailable, mixed
create_chart+apply_table_style
requests now return a clear COM-required error.
- when COM is unavailable, mixed
- Added and updated regression coverage:
- mixed request succeeds on COM
- mixed request fails clearly when COM is unavailable
backend="openpyxl"continues to rejectcreate_chartrequests
Notes
- This release does not add new MCP tools or patch operations.
- Documentation (README and MCP guide) has been aligned with the restored
mixed-op behavior.