Compatibility Matrix
Last rendered: 2026-09-29 from docs/migration/_compat_spec.py. Do not edit this file by hand; run python scripts/render_compat_matrix.py.
This page is the public scoreboard for wolfxl's openpyxl-API compatibility. Each row maps an openpyxl idiom to its wolfxl equivalent and the current implementation status. Gap IDs (e.g. G11) are stable identifiers for tracked compatibility work.
Status legend
| Symbol |
Meaning |
| โ
Supported |
Implemented and covered by tests / fixtures. |
| ๐ก Partial |
Common case works; edge cases tracked under a gap ID. |
| โ Not Yet |
Not implemented; tracked under a gap ID. |
| โ Out of Scope |
Explicitly excluded from the roadmap. |
Totals
- โ
Supported: 76 / 83
- ๐ก Partial: 0 / 83
- โ Not Yet: 0 / 83
- โ Out of Scope: 7 / 83
To try the supported rows on your own workbook, install WolfXL with the quickstart. openpyxl alternatives, measured compares WolfXL with openpyxl and four other Python Excel libraries on committed benchmark receipts.
Workbook + Worksheet
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
Workbook() |
wolfxl.Workbook() |
โ
Supported |
|
Write-mode default. |
load_workbook(path) |
wolfxl.load_workbook(path) |
โ
Supported |
|
|
load_workbook(path, data_only=True) |
wolfxl.load_workbook(path, data_only=True) |
โ
Supported |
|
|
Implicit (full DOM rewrite) |
wolfxl.load_workbook(path, modify=True) |
โ
Supported |
|
Surgical patcher; faster than DOM rewrite. |
load_workbook(path, read_only=True) |
wolfxl.load_workbook(path, read_only=True) |
โ
Supported |
|
Streaming reads (auto-engages > 50k rows). |
wb.save(path) |
wb.save(path) |
โ
Supported |
|
Eager workbooks can be edited and saved repeatedly; write_only=True remains consumed-on-save like openpyxl. |
wb["Sheet"], wb.active, wb.sheetnames |
wb["Sheet"], wb.active, wb.sheetnames |
โ
Supported |
|
|
wb.create_sheet(title) |
wb.create_sheet(title) |
โ
Supported |
|
|
wb.create_sheet(title, index=...) on loaded workbook |
wb.create_sheet(title, index=...) on modify=True workbook |
โ
Supported |
|
Modify-mode blank sheet creation updates workbook.xml, workbook rels, content-types, tab order, and accepts cell edits before save. |
wb.remove(ws) on loaded workbook |
wb.remove(ws) on modify=True workbook |
โ
Supported |
|
Modify-mode sheet removal prunes the workbook tab, workbook rel, worksheet part, local defined-name indexes, and reachable sheet subgraph parts. |
ws.title = 'Renamed' on loaded workbook |
ws.title = 'Renamed' on modify=True workbook |
โ
Supported |
|
Modify-mode sheet title changes update workbook.xml and subsequent queued sheet mutations target the renamed tab. |
wb.copy_worksheet(ws) |
wb.copy_worksheet(ws) |
โ
Supported |
|
Diverges from openpyxl in 5 documented ways (always more preservation). |
Cell + style API
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
ws["A1"].value, ws.cell(r, c).value |
ws["A1"].value, ws.cell(r, c).value |
โ
Supported |
|
|
cell.font / fill / border / alignment / number_format |
cell.font / fill / border / alignment / number_format |
โ
Supported |
|
All five attributes (plus protection and border) round-trip together on one cell. Python flush layer merges format and border keys into a single dict so the native writer interns one combined xf record, instead of minting two ids that overwrite each other. |
Border(diagonal=Side(...), diagonalUp=True) |
Border(diagonal=Side(...), diagonalUp=True) |
โ
Supported |
|
Round-trips through both write mode and modify mode; diagonalUp/diagonalDown attrs and the child of persist via the patcher's BorderSpec and the writer's intern path. |
cell.protection = Protection(...) |
cell.protection = Protection(...) |
โ
Supported |
|
Round-trips through wolfxl reload; locked/hidden flags persist via child of with applyProtection="1". |
NamedStyle(name=...) + wb.add_named_style(...) |
NamedStyle(name=...) + wb.add_named_style(...) |
โ
Supported |
|
cell.style binds a registered NamedStyle and round-trips through cellStyleXfs/cellStyles + the xfId attr on ; reader resurfaces the name via cell.style. |
GradientFill(stop=(...)) |
GradientFill(stop=(...)) |
โ
Supported |
|
GradientFill round-trips: writer emits with type/degree/path attrs and ordered stops; reader parses gradient back into Python GradientFill via the format dict's gradient sub-dict. |
Charts
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
BarChart / LineChart / PieChart / DoughnutChart |
BarChart / LineChart / PieChart / DoughnutChart |
โ
Supported |
|
|
AreaChart / ScatterChart / BubbleChart / RadarChart |
AreaChart / ScatterChart / BubbleChart / RadarChart |
โ
Supported |
|
|
BarChart3D / LineChart3D / PieChart3D / AreaChart3D |
BarChart3D / LineChart3D / PieChart3D / AreaChart3D |
โ
Supported |
|
|
SurfaceChart / SurfaceChart3D / StockChart / ProjectedPieChart |
SurfaceChart / SurfaceChart3D / StockChart / ProjectedPieChart |
โ
Supported |
|
|
ws.add_chart / remove_chart / replace_chart |
ws.add_chart / remove_chart / replace_chart |
โ
Supported |
|
Covers pending charts plus source-loaded worksheet chart removal, replacement, and title edits in modify mode. |
wb.create_chartsheet(...).add_chart(chart) |
wb.create_chartsheet(...).add_chart(chart) |
โ
Supported |
|
Chartsheet authoring emits chartsheet, drawing, chart, rels, and content-type parts that openpyxl reloads; existing chartsheet tabs load back as workbook.chartsheets rather than worksheets. |
bar + line on shared category axis with secondary value axis |
bar + line on shared category axis with secondary value axis |
โ
Supported |
|
Combination charts ship as a multi-family <plotArea> (RFC-069 ยง6) for real Excel/LibreOffice rendering, plus per-family standalone shadow chartspaces parked at row 1048576 so openpyxl's reader exposes each family as a distinct ws._charts entry without the shadows visually overlapping the real combo. Secondary value axis (right side) honored when line.y_axis.crosses='max' and line.y_axis.axId is set. Closes G15. |
data label + axis label rich-text runs |
data label + axis label rich-text runs |
โ
Supported |
G10 |
Data label <c:txPr> runs and axis-title rich text round-trip end-to-end (G10). |
pivot chart with per-point overrides |
pivot chart with per-point overrides |
โ
Supported |
|
Per-point <c:dPt> overrides round-trip through openpyxl on pivot-source charts (G16). Bar/line/scatter all share the same dict bridge: Series.dPt -> data_points -> Rust DataPoint -> <c:dPt> with <c:spPr>. Pivot vs non-pivot only differ in the chart-level <c:pivotSource> block. |
Pivot tables
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
PivotCache + PivotTable construction |
wolfxl.pivot.PivotCache + PivotTable |
โ
Supported |
|
v2.0 ships pre-aggregated records emit (no refresh-on-open required). |
chart.pivot_source = pt |
chart.pivot_source = pt |
โ
Supported |
|
|
edit existing pivot's source range, field order, etc. |
edit existing pivot source + layout fields |
โ
Supported |
|
Source-range mutation is supported; layout edits that change cache semantics stamp refresh-on-open rather than regenerating pivot cache records. |
edit existing pivot row/column fields |
PivotTableHandle.row_fields / column_fields |
โ
Supported |
|
Mutates existing pivot table layout XML and marks the linked cache refresh-on-open. |
edit existing pivot page field filter |
PivotTableHandle.page_fields / set_filter |
โ
Supported |
|
|
edit existing pivot data-field aggregation |
PivotTableHandle.data_fields / set_aggregation |
โ
Supported |
|
|
copy_worksheet of pivot-bearing sheet (drops in openpyxl) |
copy_worksheet of pivot-bearing sheet (deep-clone) |
โ
Supported |
|
|
Images + drawings
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
Image("logo.png") + ws.add_image(img, "B5") |
Image("logo.png") + ws.add_image(img, "B5") |
โ
Supported |
|
|
ws.replace_image / remove_image |
ws.replace_image / remove_image |
โ
Supported |
|
|
Worksheet structural ops
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
ws.insert_rows / delete_rows |
ws.insert_rows / delete_rows |
โ
Supported |
|
|
ws.insert_cols / delete_cols |
ws.insert_cols / delete_cols |
โ
Supported |
|
|
ws.move_range(range, rows, cols) |
ws.move_range(range, rows, cols) |
โ
Supported |
|
|
Modify-mode mutations
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
wb.properties.title = ... |
wb.properties.title = ... |
โ
Supported |
|
|
wb.defined_names[name] = DefinedName(...) |
wb.defined_names[name] = DefinedName(...) |
โ
Supported |
|
|
ws.add_table(Table(...)) |
ws.add_table(Table(...)) |
โ
Supported |
|
|
ws.data_validations.append(...) |
ws.data_validations.append(...) |
โ
Supported |
|
|
Read-side parity
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
load_workbook(path) |
load_workbook(path) |
โ
Supported |
|
|
not supported in openpyxl |
native BIFF12; values, cached formulas, read-side styles |
โ
Supported |
|
|
not supported in openpyxl |
calamine; value-only, styles raise |
โ Out of Scope |
|
Style accessors raise; values + formulas read. openpyxl does not support this surface; tracked as a wolfxl-extra roadmap item, not an openpyxl-parity gap. |
not supported in openpyxl |
not supported (out of scope) |
โ Out of Scope |
G27 |
|
Utility functions
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
openpyxl.utils.get_column_letter |
wolfxl.utils.cell.get_column_letter |
โ
Supported |
|
|
openpyxl.utils.column_index_from_string |
wolfxl.utils.cell.column_index_from_string |
โ
Supported |
|
|
openpyxl.utils.range_boundaries |
wolfxl.utils.cell.range_boundaries |
โ
Supported |
|
|
openpyxl.utils.coordinate_to_tuple |
wolfxl.utils.cell.coordinate_to_tuple |
โ
Supported |
|
|
Streaming write (write_only=True)
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
Workbook(write_only=True) |
wolfxl.Workbook(write_only=True) |
โ
Supported |
|
Bounded-memory streaming append-only path (G20 / RFC-073). Per-sheet temp files splice into the <sheetData> slot; row data scales O(rows on disk), not O(rows in RAM). SST and styles still grow in-memory as in openpyxl's lxml.xmlfile model. |
Protection
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
ws.protection = SheetProtection(...) |
ws.protection = SheetProtection(...) |
โ
Supported |
|
Round-trips through openpyxl reload (sheet flag, formatCells override, password hash). |
wb.security = WorkbookProtection(...) |
wb.security = WorkbookProtection(...) |
โ
Supported |
|
camelCase aliases (lockStructure, workbookPassword, etc.) accepted alongside snake_case; round-trips through openpyxl reload. |
External links
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
wb._external_links + xl/externalLinks/* parts |
wb._external_links + xl/externalLinks/* parts |
โ
Supported |
|
Inspection (target / sheet_names / cached_data), opaque modify-mode preservation when unchanged, and append/remove/edit authoring. keep_links=False strips preserved source links, and file-like/bytes-backed xlsx reads expose the same collection. |
append/remove/edit external workbook links |
wb._external_links append/remove/update_target |
โ
Supported |
|
Authoring emits externalLink parts, workbook rels/references, content-types, and per-link rels. Cached data is preserved when provided but linked workbooks are not dereferenced. |
VBA macros
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
.xlsm preserved on read+write |
.xlsm preserved on modify-mode save |
โ
Supported |
|
|
workbook.vba_archive (read-only inspection) |
workbook.vba_archive (read-only inspection) |
โ
Supported |
|
v1.0: modify-mode loads only; returns raw xl/vbaProject.bin bytes. No authoring API (tracked under G28). |
not supported in openpyxl |
macro authoring from Python |
โ Out of Scope |
G28 |
Decision-gated (S11). openpyxl does not support this surface; tracked as a wolfxl-extra roadmap item, not an openpyxl-parity gap. |
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
not supported |
write.xlsb`` |
โ Out of Scope |
G25 |
Decision-gated (S9). openpyxl does not support this surface; tracked as a wolfxl-extra roadmap item, not an openpyxl-parity gap. |
not supported |
write.xls`` |
โ Out of Scope |
G26 |
Decision-gated (S9). openpyxl does not support this surface; tracked as a wolfxl-extra roadmap item, not an openpyxl-parity gap. |
not supported |
read+write.ods`` |
โ Out of Scope |
G27 |
Decision-gated (S10). openpyxl does not support this surface; tracked as a wolfxl-extra roadmap item, not an openpyxl-parity gap. |
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
cell.comment = Comment(text, author) |
cell.comment = Comment(text, author) |
โ
Supported |
|
|
threaded comment write + modify (xl/threadedComments) |
threaded comment write + modify (xl/threadedComments) |
โ
Supported |
|
|
Rich text
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
cell.value = CellRichText(...) |
cell.value = CellRichText(...) |
โ
Supported |
|
|
ws.oddHeader / oddFooter rich-text runs |
ws.oddHeader / oddFooter rich-text runs |
โ
Supported |
|
|
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
cellIs / containsText / expression / colorScale (basic) |
cellIs / containsText / expression / colorScale (basic) |
โ
Supported |
|
|
IconSetRule (3 / 4 / 5 icons + percentile / number ladders) |
IconSetRule (3 / 4 / 5 icons + percentile / number ladders) |
โ
Supported |
|
|
DataBarRule (gradient + solid; min / max / percent / formula) |
DataBarRule (gradient + solid; min / max / percent / formula) |
โ
Supported |
|
Round-trips through openpyxl reload; secondary probes cover cfvo edge cases and minLength/maxLength. |
DataBarRule with percent / num / formula cfvo + showValue=False |
DataBarRule with percent / num / formula cfvo + showValue=False |
โ
Supported |
|
|
ColorScaleRule with 3-stop, percentile / formula / number cfvo |
ColorScaleRule with 3-stop, percentile / formula / number cfvo |
โ
Supported |
|
|
stopIfTrue + explicit priority + dxf integration |
stopIfTrue + explicit priority + dxf integration |
โ
Supported |
|
|
Defined names
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
wb.defined_names + DefinedName(name=, value=) |
wb.defined_names + DefinedName(name=, value=) |
โ
Supported |
|
|
full ECMA-376 ยง18.2.5 attribute surface (13 attrs) |
full ECMA-376 ยง18.2.5 attribute surface (13 attrs) |
โ
Supported |
|
|
Print settings + page setup
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
ws.print_options / page_setup / page_margins / print_title_rows |
ws.print_options / page_setup / page_margins / print_title_rows |
โ
Supported |
|
|
full PageSetup / PrintOptions surface (~30 attrs) |
full PageSetup / PrintOptions surface (~30 attrs) |
โ
Supported |
|
|
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
ArrayFormula(ref, text) |
ArrayFormula(ref, text) |
โ
Supported |
|
Round-trips through openpyxl reload (ref + text preserved as openpyxl ArrayFormula). |
DataTableFormula(...) |
DataTableFormula(...) |
โ
Supported |
|
|
Worksheet.array_formulae + DataTableFormula del flags |
Worksheet.array_formulae + DataTableFormula del flags |
โ
Supported |
|
openpyxl 3.1.5 exposes array/spill ranges via Worksheet.array_formulae; formula_attributes is not part of the supported reference surface. |
Calc chain
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
calcChain rebuild on modify |
calcChain rebuild on modify |
โ
Supported |
|
|
cross-sheet calc-chain ordering, deleted-cell pruning |
cross-sheet calc-chain ordering, deleted-cell pruning |
โ
Supported |
|
Rebuild scans post-mutation sheet XML across tab order, prunes stale refs, and preserves a source calcChain extLst when emitting a non-empty chain. |
Slicers
| openpyxl |
wolfxl |
Status |
Gap |
Notes |
Slicer + SlicerCache wired to a PivotCache |
Slicer + SlicerCache wired to a PivotCache |
โ
Supported |
|
|
Slicer outside pivot context (table-driven, etc.) |
Slicer outside pivot context (table-driven, etc.) |
โ Out of Scope |
G21 |
openpyxl 3.1.5 exposes no public slicer modules/classes or table-driven slicer authoring API; tracked as a possible wolfxl-extra, not an openpyxl parity gap. |
How this page is maintained
This file is generated from _compat_spec.py. To update a row, edit the spec module and run:
python scripts/render_compat_matrix.py
The accompanying tests/test_openpyxl_compat_oracle.py harness imports ENTRIES from the same module and uses each entry's probe field to drive a live test. Adding a new probe means adding a new entry in the spec and a matching probe function in the harness.