Workbook API¶
Version scope
This API reference describes WolfXL 2.2.0.
Constructors¶
Workbook()¶
Creates a new workbook in write mode.
load_workbook(filename, read_only=False, data_only=False, keep_links=True, modify=False)¶
Opens existing workbook.
modify=False: read mode-
modify=True: modify mode (read + patch/save) -
read_only=True: uses the streaming reader surface for row iteration workflows. data_only=True: read cached formula values where present.keep_links=True: preserves external-link parts in modify-mode saves.keep_links=False: hides external links when reading and drops external-link parts on modify-mode save.
With data_only=True, read saved values from ordinary formulas, array-formula
anchors, and data-table anchors. Receive None for an absent cache and the
stored error for an error cache. In ordinary formula mode, inspect array and
data-table anchors as typed formula objects. Newly assigned formula objects
remain available even after a data-only load.
External-link cache refresh¶
Workbook.refresh_external_links(*, resolver=None) -> int¶
Refresh existing external-link caches in a modify-mode XLSX workbook loaded
with keep_links=True. Return the number of refreshed links; save the workbook
to persist the updated caches.
Without a resolver, use a filesystem-backed workbook and bounded sibling XLSX targets. Resolve every target before opening any source; refuse non-local targets, path traversal, and symlinks.
Supply resolver(target) to resolve original target strings explicitly. Return
XLSX bytes or a filesystem path; return None for an unresolved target. This
mode also accepts bytes-backed destination workbooks and opaque URI targets.
No network access, credential lookup, recursive dependency refresh, or fallback
filesystem lookup is performed by the engine. Reentrant refresh is refused.
Preflight link/cache structures and resolve all targets before opening source readers. Publish cache updates together only after every source succeeds; unresolved targets, callback exceptions, or invalid source packages leave the existing caches unchanged. Preserve link targets and unrelated package parts. Persisted caches are not automatically imported into the calculation engine.
from pathlib import Path
from wolfxl import load_workbook
sources = {"external_source.xlsx": Path("external_source.xlsx").read_bytes()}
with load_workbook(Path("linked.xlsx").read_bytes(), modify=True) as workbook:
refreshed = workbook.refresh_external_links(resolver=sources.get)
workbook.save("refreshed.xlsx")
Copying protected worksheets¶
Use Workbook.copy_worksheet(source, name="Copied") within a modify-mode
workbook to retain sheet protection and editable ranges. Edit the copied
worksheet's protected_ranges or protection.protected_ranges independently
of the source, including after renaming the source before copying. Preserve
range passwords and security descriptors through save/reopen.
This metadata-preservation contract does not establish native Excel permission enforcement, encryption, or cross-workbook protection behavior.
Conversion¶
ConversionOptions(destination_format, sheet=None, cell_range=None, values=False, encoding="utf-8", bom=False)¶
Immutable conversion controls. destination_format accepts xlsx, ods,
xlsb, xls, pdf, png, html, txt, csv, or tsv. Static
destinations emit one worksheet; sheet=None selects the first worksheet.
Structural destinations convert the complete workbook.
CSV and TSV use RFC 4180 quoting and CRLF row terminators. cell_range selects
one bounded A1 rectangle. values=False writes formula text, while
values=True writes cached formula results. encoding accepts any registered
Python codec with explicit byte order for UTF-16/UTF-32; bom=True adds the
matching UTF byte-order marker.
For compatibility with supported Python 3.10 CSV tooling, U+0000 is rendered as
the literal text \x00.
Conversion entry points¶
preflight_conversion(source, destination=None, *, destination_format=None, sheet=None, options=None) -> ConversionPreflightconvert(source, destination, *, destination_format=None, sheet=None, options=None) -> ConversionReportconvert_to_bytes(source, *, destination_format=None, sheet=None, options=None) -> ConversionBytesconvert_to_stream(source, destination, *, destination_format=None, sheet=None, options=None) -> ConversionReport
source accepts a path, bytes-like object, or readable binary stream.
Destinations may be paths or replacement-safe binary streams that support
read(), seek(), tell(), write(), and truncate(). A destination path
selects its format by extension unless destination_format or
ConversionOptions names it explicitly. Conflicting selectors are rejected
before publication.
All routes complete backend generation before publishing. Path destinations use
a same-directory temporary file and atomic replacement. Stream routes check
every partial write and report a typed SDKIOError if publication stalls or
fails. convert_to_bytes() returns an immutable ConversionBytes containing
both data and its ConversionReport.
ConversionPreflight and ConversionReport expose ordered preservation and
diagnostics tuples. Every discovered feature is classified as preserved,
rewritten, or dropped; losses remains the compatibility projection of
dropped features. Scanner coverage limits are reported explicitly instead of
being treated as proof of lossless conversion.
from wolfxl import ConversionOptions, convert_to_bytes, preflight_conversion
options = ConversionOptions(destination_format="pdf", sheet="Summary")
plan = preflight_conversion("template.xlsx", options=options)
artifact = convert_to_bytes("template.xlsx", options=options)
assert artifact.report.selected_sheets == plan.selected_sheets
PDF and PNG require a render-enabled build. The committed high-value conversion evidence matrix covers these routes:
| Source | Verified destinations |
|---|---|
| XLSX | XLSX, ODS, XLSB, XLS, PDF, PNG, HTML, TXT, CSV, TSV |
| ODS | XLSX, HTML, TXT, CSV, TSV |
| XLSB | XLSX, XLSB, HTML, TXT, CSV, TSV |
| XLS | XLSX, HTML, TXT, CSV, TSV |
The matrix checks public dispatch, deterministic repeated bytes, structural reopenability, output signatures, range/encoding semantics, and explicit feature-loss reporting. Routes outside this matrix may be accepted by a bounded backend but do not carry this matrix-level evidence.
Batch conversion¶
convert_many(sources, destination_dir, *, destination_format, options=None,
overwrite=False, fail_fast=False) -> BatchConversionReport converts several
sources into one existing destination directory. Items are reported in the
order supplied. Destination names and existing files are preflighted before
the first conversion. With overwrite=False, publication also uses an atomic
no-clobber operation, so a destination created concurrently is preserved. Each
success contains only source and destination formats, output hash and size,
fidelity tier, and a lossless flag. It excludes sheet names, diagnostic scopes,
and cell values. Failures are typed and value-free. fail_fast=True stops after
the first failure.
The same outcome is available from the command line:
The command prints one canonical JSON document of the form
{"schema_version", "command", "status", "result"}. Exit code 0 means every
item converted, 5 means at least one item failed, and 2 means the request
was rejected before any conversion. The CLI does not create the destination
directory and does not select per-item formats; one invocation writes one
format.
Workbook comparison¶
compare_workbooks(before, after, *, policy=None) -> WorkbookComparison
evaluates the existing Guard policy in memory without writing a report file.
The immutable result contains status, passed, issue counts and codes, and
path-free workbook identities with filename, SHA-256, and byte size. Omitting
policy selects the Guard default policy. This comparison does not assess
calculation correctness, rendered appearance, intended-change authorization,
or macro execution.
XLSB fidelity ladder¶
The XLSB writer advances cumulatively. XLSB_FIDELITY_TIERS exposes the same
machine-readable table used by the SDK.
| Level | Boundary | Implemented |
|---|---|---|
| T0 | Blank, number, Boolean, error, and text values | Yes |
| T1 | Shared strings, scalar formulas, and cached formula results | Yes |
| T2 | Number formats, font name/size, 1904 dates, sheet visibility, and document properties | Yes |
| T3 | Merged cells and row/column dimensions | Yes |
| T4 | Supported hyperlinks, legacy comments, list validations, cellIs/expression conditional formats, and simple tables |
Yes |
T4 is the current bounded rewrite boundary. Unsupported subtypes and richer variants fail preflight rather than being silently discarded.
Reporting workflows¶
ReportOptions(strict=True, calculate=False, recompute_pivots=False, max_expanded_rows=1048576)¶
workbook.reporter returns a cached ReportService. preflight(sources,
options=None) validates template markers and returns an immutable ReportPlan
without changing the workbook. populate(sources, options=None) applies the
plan and returns a ReportReport.
Templates support:
- scalar markers with optional literal defaults such as
{{ customer.discount ?? 0.15 }} - repeated row bands using
{{#each orders}}and{{/each}} - nested repeated bands
- exact-Boolean conditional bands using
{{#if customer.active}} - caller-owned local images using
{{image customer.logo}}andReportImage - deterministic
group_records(...)groups withsum,count,min,max, andaveragesubtotals
Expansion translates formulas and updates supported row-scoped merges, tables, defined names, validations, conditional formats, page breaks, charts, and drawing anchors. Dotted lookup reads mapping keys, dataclass fields, or stored object attributes. It does not evaluate Python expressions, properties, or methods. The reporting engine does not fetch URLs or refresh external data. Image sources must be caller-supplied bytes, a local path, or a binary stream.
A scalar default is a literal JSON scalar only: null, true or false, a
finite integer or float, or a quoted string with escaped quotes (embedded }}
and ?? inside quotes are literal text). A missing path or an explicit None
applies the default; 0, False, and empty strings are real values and never
trigger it. Applied defaults are not reported as unresolved fields. A whole-cell
default keeps its native Excel type (42 stays a number, false a boolean),
while a default inside surrounding text substitutes str(value). Fallbacks
that are not scalar literals (NaN, Infinity, arrays, objects) and ?? on
#each, #each-col, #if, closing, or image directives are refused as
malformed before any mutation. The verified wolfxl.Template runtime does not
admit the ?? operator; see Verified Template Runtime.
max_expanded_rows is checked before structural mutation. Strict preflight
rejects unresolved fields, malformed or overlapping blocks, non-Boolean
conditions, invalid image sources, and expansions beyond the configured or
Excel row limit.
Complete XLSX and PDF workflow¶
from wolfxl import ReportOptions, ReportOutputOptions, load_workbook
workbook = load_workbook("monthly-pack-template.xlsx", modify=True)
result = workbook.reporter.generate(
{
"company": {"name": "Acme"},
"orders": [
{"sku": "A-1", "quantity": 2, "price": 12.5},
{"sku": "B-4", "quantity": 1, "price": 8.0},
],
},
ReportOutputOptions("monthly-pack.xlsx", "monthly-pack.pdf"),
ReportOptions(calculate=True, recompute_pivots=True),
)
print(result.calculation.report.diagnostics.engine)
generate(...) preflights XLSX, optional PDF, and requested calculation
capabilities before population. It then runs population, formula calculation,
supported pivot recomputation, a final calculation pass when a pivot changed
cells, XLSX serialization, and optional paginated PDF rendering. The immutable
ReportWorkflowReport exposes population, calculation, pivots, and
outputs. result.outputs.publication records the publication scope and the
byte count and SHA-256 identity of each completed artifact.
Pivot recomputation is native only for worksheet-backed sources. External,
OLAP, and other non-worksheet sources are preserved without network access and
reported in result.pivots.diagnostics. A failure in a supported native pivot
is raised instead of silently falling back.
Paired path outputs are fully serialized before either caller destination is
changed. Replacement failure restores every destination already changed, or
removes it when no prior file existed, and raises ReportPublicationError with
rollback_complete and cleanup_complete state. This is an in-process
all-new-or-all-restored publication guarantee, not a crash-safe transaction
across filesystems. The in-memory workbook remains populated if calculation,
pivot recomputation, serialization, or publication fails.
Invalid options, malformed templates, unresolved strict-mode fields, unsafe
image sources, and row-limit violations raise ReportError, a stable
InvalidSDKRequestError subtype. Unsupported calculation, save, and rendering
capabilities fail before template population.
Structured data exchange¶
wolfxl.data provides the module-level import_records, range_to_records,
import_json, range_to_json, import_pandas, export_pandas,
import_polars, import_arrow, and import_sql_cursor adapters. These are
distinct from the only structured-data Worksheet methods:
Worksheet.import_records(...) and Worksheet.to_records(...).
Imports use bounded Worksheet.write_rows batches. Pandas, Polars, PyArrow,
and NumPy remain lazy optional dependencies. Pandas, Polars, and PyArrow reject
bytes, bytearray, and memoryview cell values before any worksheet
mutation; binary payloads must be explicitly converted to a supported Excel
scalar by the caller. The Polars and PyArrow adapters have a positive
max_rows bound (default 1,048,576): a LazyFrame collects at most one
additional row to prove the bound, while a PyArrow batch reader is consumed and
validated within that bound before it writes cells. SQL cursor imports consume
and advance the caller-owned cursor only through fetchmany(chunk_size), support
optional on_progress and should_cancel callbacks, and never execute SQL,
commit, roll back, or close the cursor or its connection. Cancellation before
completion raises SqlImportCancelled carrying the exact partial DataWriteReport.
import_json(worksheet, document, ...) writes one JSON document — records, row
arrays, or columns — into a worksheet and range_to_json(worksheet, ...) reads
one rectangle back as canonical JSON text. Declared schema types are the only
conversions performed, nested objects and arrays are stored as canonical JSON
text, and text is never retyped into a formula by accident. The full shape,
schema, nesting, literal-text, and refusal contract is documented in
docs/api/data-bridge.md.
The native file-boundary API is available from both wolfxl.data and the
package root:
scan_arrow(filename, sheet_name=None, *, header=True, batch_rows=65536, columns=None, start_row=1, max_rows=None, temporal="native", strict=True)read_arrow(...), with exactly the same scan options, materializes one PyArrowRecordBatch.scan_polars(...), with exactly the same scan options, returns a PolarsLazyFrame.write_arrow(data, filename, *, sheet_name="Sheet1", header=True)writes a single-sheet XLSX file from an Arrow C stream or C array.
scan_arrow(...) is a native, one-shot Arrow stream for XLSX worksheets. It
implements the Arrow PyCapsule stream protocol without importing PyArrow.
batch_rows is a positive per-batch bound; start_row is 1-based before
optional header removal; max_rows is a positive bound on emitted data rows;
and columns is a non-empty ordered projection of field names or 1-based Excel
column indices. temporal="native" exposes format-classified serials as Arrow
date, time, and timestamp arrays; temporal="serial" retains Float64 serials
and records logical type, number format, and date system metadata. Sparse
cells are null. With strict=False, later incompatible values become null and
the schema records that loss mode; with strict=True, the scan names the
offending sheet, field, and cell. read_arrow imports PyArrow only after the
shared scan contract validates. scan_polars imports Polars only when called
and ingests the same PyCapsule stream without importing PyArrow.
write_arrow(...) is intentionally distinct from the existing
import_arrow(worksheet, table, ...) adapter: the latter writes validated
Python-level values into an already-owned worksheet, while write_arrow
performs native Arrow-to-XLSX path I/O without constructing Python Cell
objects. It requires an object exposing __arrow_c_stream__ or
__arrow_c_array__, consumes batches incrementally, requires one schema,
writes nulls as blank cells, and maps native Arrow date/time/timestamp values
to Excel serials with stable styles. Unsupported types, temporal units or time
zones, non-finite numbers, schema changes, and Excel row/column or
exact-numeric limits fail without replacing the destination path. DuckDB can
object returned by scan_arrow(...) directly through the Arrow stream
protocol; WolfXL does not install or monkeypatch a DuckDB engine.
Publishing a dataset as a workbook¶
records_to_workbook(records, destination, *, sheet_name="Sheet",
table_name=None, columns=None, strict=True, overwrite=False) ->
DatasetExportReport turns records into one published .xlsx file in a single
call. The destination directory is proven writable before the records iterable
is consumed. Schema and value validation complete against a sibling staging
file before publication. With overwrite=False, an atomic no-clobber operation
also preserves a destination created concurrently. table_name defines a real
Excel table and rejects identifiers that conflict with Excel cell-reference
grammar. DatasetExportReport is immutable and reports only row_count,
column_count, sheet_name, table_name, artifact_sha256, and
artifact_size.
The command-line form consumes UTF-8 JSONL, one JSON object per line, from a
file or -:
wolfxl-ops records-to-workbook records.jsonl \
--output dataset.xlsx \
--sheet Units \
--table UnitsTable \
--columns region,units
Limits: one worksheet per invocation, every line must be a JSON object, and
--columns is a comma-separated order, so column names containing commas
require the Python API. Rejected input returns exit code 2 with the offending
line number and no cell values in the JSON document.
Calculation¶
CalculationOptions(sheets=(), ranges=(), dirty_only=False, tolerance=1e-10)¶
Immutable controls for one calculation. sheets filters returned formula cells
by worksheet. ranges accepts qualified A1 selections such as
"Summary!B2:D20" and filters returned values further. The complete dependency
graph is still evaluated before selection. dirty_only=True recalculates from
changed input cells when a compatible evaluator cache exists.
workbook.calculator¶
Returns a CalculationService bound to the workbook:
calculate(options=None) -> CalculationReportdependency_graph() -> DependencyGraphSnapshot
CalculationReport.values is an immutable mapping of qualified cell references
to computed values. diagnostics records the selected engine, calculation mode,
fallback reason, unsupported formulas, cycles, formula counts, and maximum
dependency depth. Dirty calculation also returns ordered CellDelta records.
Public failures raise CalculationError, which is both an SDKError and a
RuntimeError.
DependencyGraphSnapshot¶
dependency_graph() returns an immutable, deterministically ordered snapshot of
the workbook formula graph. Inspecting it never mutates workbook or dirty state.
| Member | Purpose |
|---|---|
formulas |
Sorted (cell, formula) pairs for every formula cell |
dependencies |
Sorted forward edges, (formula cell, precedent cells) |
dependents |
Sorted reverse edges, (precedent cell, formula cells) |
topological_order |
Calculation order; empty when a cycle is reported |
cycles |
Circular-reference messages naming the involved cells |
precedents_of(cell) |
Direct precedents of one formula cell |
dependents_of(cell) |
Direct formula dependents of any cell |
affected_cells(changed) |
Transitively dirty formulas in calculation order |
Edges are complete rather than range endpoints: a range precedent contributes
every cell it covers, across sheets, so an interior edit still propagates.
References are canonicalized to the workbook's own worksheet spelling, so
case variants and quoted sheet tokens such as 'O''Brien Data'!A1:A2 resolve to
one identity. A formula whose cumulative unique precedents exceed 100000 cells
raises CalculationError before any edge is materialized.
snapshot = workbook.calculator.dependency_graph()
snapshot.precedents_of("Summary!C1")
snapshot.affected_cells(("Input Data!A2",))
The compatibility methods Workbook.calculate() and
Workbook.recalculate(perturbations, tolerance) remain available.
from wolfxl import CalculationOptions, load_workbook
workbook = load_workbook("model.xlsx", modify=True)
report = workbook.calculator.calculate(
CalculationOptions(sheets=("Summary",), dirty_only=True)
)
print(report.values, report.diagnostics.engine)
Rendering¶
RenderOptions(...)¶
RenderOptions selects exactly one workbook, sheet, range, paginated-sheet, or
chart target. Its public fields are:
| Field | Purpose |
|---|---|
format |
png, pdf, jpeg, or svg for sheet, range, and chart selection; workbook-level multi-sheet and paginated selections do not accept jpeg |
sheet |
Worksheet title or object; None selects the workbook route |
cell_range |
One A1 rectangle |
chart_index |
Zero-based native chart order |
paginated |
Print-aware multi-page PDF, PNG, or SVG route |
output |
None for returned bytes, or a path/binary stream |
dpi, scale |
Positive output controls |
background |
Optional RGB color such as #FFFFFF |
width, height |
Optional chart-only pixel dimensions |
jpeg_quality |
Optional integer quality between 1 and 100 (default 90) for JPEG rendering |
png_compression |
Optional integer compression level between 0 and 9 for PNG rendering |
workbook.renderer¶
workbook.renderer.render(options=None) -> RenderReport routes through the same
native renderer used by the direct methods. RenderReport.result contains bytes,
an immutable sheet-to-bytes mapping, or None after path/stream publication.
RenderDiagnostics records the selected route, format, rendered sheet count,
typed diagnostics, and any omitted or substituted visual features.
Direct workbook methods remain available:
render_workbook_to_pdf(...)render_sheet_to_png(...),render_sheet_to_pdf(...),render_sheet_to_svg(...),render_sheet_to_jpeg(...)render_sheet_to_paginated_pdf(...),render_sheet_to_paginated_pngs(...),render_sheet_to_paginated_svgs(...)render_layout_inventory(...),render_layout_object_to_png(...)render_range_to_png(...),render_range_to_pdf(...),render_range_to_svg(...),render_range_to_jpeg(...)render_chart_to_png(...),render_chart_to_pdf(...)render_chart_to_jpeg(...),render_chart_to_svg(...)
JPEG output is genuine raster JPEG, and SVG output is vector/text SVG. Whole-worksheet
and rectangular cell-range JPEG rendering are supported via render_sheet_to_jpeg(...)
and render_range_to_jpeg(...) as well as RenderService. render_range_to_jpeg(sheet, cell_range, ...)
normalizes same-sheet rectangular A1 coordinates (such as "A1:C3"), applies the native layout,
and preserves merged-cell geometry and overlapping drawings. jpeg_quality selects the encoder
quality from 1 to 100 (default 90); out-of-range or non-integer values raise ValueError or
TypeError.
Whole-workbook and multi-sheet JPEG rendering are refused, paginated rendering does not accept
JPEG, chart-only width and height dimensions refuse on sheet and range rendering, and incompatible
encoder options (such as png_compression on JPEG or jpeg_quality on PNG/PDF) are rejected.
Unsupported raster formats (BMP, TIFF, GIF) fail with ValueError. Chart width or height
alone preserves aspect ratio subject to integer-pixel rounding; passing both sets the exact canvas size.
from wolfxl import RenderOptions, load_workbook
workbook = load_workbook("dashboard.xlsx")
report = workbook.renderer.render(
RenderOptions(format="svg", sheet="Summary", chart_index=0, width=1200)
)
svg_bytes = report.result
render_layout_inventory(sheet=None, *, dpi=96.0, scale=1.0)¶
Reports one worksheet's native full-sheet layout: the rendered canvas, the
effective density, scale, and direction, the laid-out extent, and one record
per source object: every materialized cell, every merged range, every chart,
and every floating image. sheet names one worksheet or takes a Worksheet
object; None selects the active worksheet, and an unknown name raises
ValueError. Rendering owns an immutable requested-sheet snapshot, so the
workbook and any pending modify state are unchanged. The frozen
LayoutInventory and its LayoutObject, LayoutProvenance, and LayoutRect
records are importable from wolfxl.layout.
Every rectangle lives in the rendered_output_pixels coordinate space: a
top-left origin, already carrying the requested density, the uniform output
scale, and the right-to-left mirror a right-to-left sheet renders with. Each
record carries rect in that rendered pixel space and grid_rect in the
unscaled layout space the native crop consumes. dpi and scale must be
finite and greater than zero, and they are the only values that change
geometry: read the inventory with the same two values used to render, and a
reported rectangle is the region that render actually paints. Geometry is the
renderer's own, derived from the same native layout the full-sheet routes
rasterize; it is bounded by the native layout cell limit (1,000,000 cells),
and an oversized extent refuses rather than truncating. Hidden rows and
columns lay out at zero extent, so an object made only of hidden bands
reports a zero-area rect.
Identity is the worksheet's own and source-versioned: cell:B2 addresses a
cell coordinate, merged_cell:0:B2:D4 a merge ordinal plus its range, and
chart:0 / image:1 an ordinal in the worksheet's own chart or image list,
never a filtered paint position. Each record's provenance carries the
worksheet-expressed coordinates (cell_range, anchor_cell, merge_index,
chart_index, image_index, anchor_kind) that tie the rectangle to its
source object.
Editability is explicit. cell and merged_cell records report editable
true and a target_selector in the existing guarded target-discovery
vocabulary; chart and image records report editable false with an
editable_reason, because no guarded authoring route edits a rendered chart
canvas or a floating image. Resolution to an authoritative target descriptor
is the resolve operation of wolfxl-ops layout below; the inventory itself
never fabricates an authoring operation.
One inventory does not assess print pagination or page geometry, shape and
conditional-format geometry, or visual content the native renderer itself
omits. The CLI result spells these out as not_assessed; the same tuple is
wolfxl.layout.LAYOUT_NOT_ASSESSED.
from wolfxl import load_workbook
workbook = load_workbook("dashboard.xlsx")
inventory = workbook.render_layout_inventory("Summary", dpi=144.0, scale=1.5)
print(inventory.width_px, inventory.height_px, inventory.coordinate_space)
entry = inventory.object("merged_cell:0:B2:C3")
assert entry is not None and entry.editable
print(entry.rect, entry.grid_rect, entry.provenance)
render_layout_object_to_png(sheet, object_id, *, output=None, dpi=96.0, scale=1.0, background=None, png_compression=None, font_dirs=(), font_substitutions=None, use_system_fonts=False)¶
Crop one layout object's region out of this worksheet's full render. sheet
is required, and object_id is one id render_layout_inventory reported for
that sheet under the same dpi and scale. The full worksheet is rasterized
once, then cropped from its actual pixels rather than rendered a second time
from the object's geometry. Each edge of the inventory record's rect is rounded
to the nearest pixel, with half-pixel ties away from zero. The returned pixels
match exactly that integral rectangle in the full render, including its
antialiasing. Pass output to write to a path-like destination or binary file-like
object; omit it to return the PNG bytes.
An unknown object_id, an object with no rendered area (for example one made
only of hidden rows or columns), an unknown sheet, or an invalid render option
refuses with ValueError instead of emitting a blank or substituted image. A
wheel built without the render Cargo feature raises RuntimeError, and an
unsupported loaded format raises NotImplementedError.
Layout geometry from the command line¶
Use wolfxl-ops layout --request REQUEST --input SOURCE [--output-dir
DESTINATION] or wolfxl.operations.layout_workbook to access the same
native geometry as a source-bound operation. One strict version-1 request
names the caller's whole-source SHA-256, exactly one operation
(inventory, resolve, or crop), that operation's object_id where it
needs one, and the effective native render options for one explicit
worksheet:
{
"schema_version": 1,
"source_sha256": "<sha256 of report.xlsx>",
"operation": "inventory",
"options": {"sheet": "Summary", "dpi": 144.0, "scale": 1.5}
}
The worksheet is always explicit: a request without options.sheet refuses
rather than rendering the active tab. options admits only the native render
vocabulary (dpi, scale, background, png_compression, font_dirs,
font_substitutions, use_system_fonts) and rejects process-owned names
such as output or destination. dpi and scale are the only options
that change geometry; the rest change the crop's pixels only. The source is
read once and bound to the caller's digest: the admitted bytes are what the
renderer consumes, and the original path is guarded again before any
publication, so a source replaced mid-run refuses instead of publishing
superseded content. --request accepts a JSON file path or - for stdin.
The command prints one canonical JSON document and exits 0 on success.
inventory writes nothing and refuses --output-dir; its result carries the
admitted source_sha256, the coordinate space, canvas, extent, one
source-versioned record per object, and the explicit not_assessed
boundaries. resolve requires object_id and returns exactly that record
plus the target descriptor the existing target discovery produced for the
record's own coordinates (target and target_operation), or a
target_reason when no target is admitted or the object is a chart or image
with no guarded authoring route; an unknown object_id or a stale source
refuses. crop requires --output-dir naming a directory that does not yet
exist: the object's region is rasterized, cropped, verified against the
staged bytes, and published as one PNG (layout-1.png) through the shared
no-replace publisher, so an existing destination is preserved rather than
replaced and a failed run removes only its own staging. The result carries
the object record, the crop's own width_px and height_px, and the
established outputs inventory (relative_path, sha256, media_type,
size_bytes).
Rejected requests exit 2; a stale source or unsupported native request
exits 4; a failed operation, including an existing destination, exits
5.
Inspection, capabilities, and binary I/O¶
Standalone inspection and loading¶
inspect_workbook(source) -> WorkbookPreflightload_workbook_from_bytes(data, ...) -> Workbookload_workbook_from_stream(stream, ...) -> Workbook
inspect_workbook() accepts a path, bytes-like object, or readable binary
stream. It returns source format, package parts, preservation inventory,
diagnostics, and operation capability results without mutating a workbook.
Seekable streams are scanned from offset zero and restored to their original
position.
Workbook capability and save methods¶
workbook.preflight_save(destination_format="xlsx") -> CapabilityResultworkbook.capability(operation, destination_format=None) -> CapabilityResultworkbook.save_to_bytes(*, destination_format="xlsx", password=None) -> bytesworkbook.save_to_stream(stream, *, destination_format="xlsx", password=None) -> None
Capability results use supported, supported_with_warnings, unsupported, or
unknown tiers and carry typed diagnostics plus preservation records. Queries
do not flush pending writes or publish artifacts. Explicit byte and stream saves
accept only destination_format="xlsx" and reject other formats before
publication. save_to_stream() destinations must support read(), seek(),
tell(), write(), and truncate() so failed replacement can restore existing
bytes and position. Legacy Workbook.save(binary_stream) also accepts
forward-only binary sinks, preserving its established compatibility. After a
failed write-only workbook publication, WolfXL retains the completed artifact
for a same-options retry, but a forward-only sink cannot roll back bytes it
already accepted before a write or flush failure.
Shared diagnostics¶
Calculation, rendering, conversion, inspection, and save operations share these root exports:
SDKDiagnostic,DiagnosticCode,DiagnosticSeverity,DiagnosticContextSDKError,SDKIOError,InvalidSDKRequestError,UnsupportedSDKOperationErrorSDKWarning,SDKRuntimeWarning,SDKDeprecationWarningCapabilityResult,SupportTierPreservationRecord,PreservationDisposition,WorkbookPreflightCancellationToken,OperationProgress,ProgressCallback,OperationCancelledError
Diagnostics and reports expose deterministic to_dict() payloads. Context uses
normalized package-part and A1 fields rather than host filesystem paths.
Long-operation control and concurrency¶
CalculationOptions, RenderOptions, and ConversionOptions accept:
cancellation_token: CancellationToken | Noneprogress: ProgressCallback | None
OperationProgress reports a stable operation name, phase, completed count, and
optional total. CancellationToken.cancel() is thread-safe and monotonic.
Cancellation raises OperationCancelledError, which is also an SDKError and
RuntimeError, with diagnostic code operation_cancelled.
Cancellation is cooperative at Python-owned service checkpoints. A request
cancelled before dispatch does not call the evaluator, renderer, or converter.
Once a monolithic native method has started, it runs to its next service
checkpoint; the current API does not promise per-formula, per-page, or
mid-conversion interruption. Direct legacy methods such as
Workbook.calculate(), Workbook.save(), and Workbook.render_sheet_to_png()
do not accept these controls.
A mutable Workbook and its worksheets belong to one thread. Create, use, and
close a workbook on that owner thread; do not share one workbook across worker
threads. Independent workbooks are supported in parallel. close() releases
thread-affine native calculation, reader, writer, and patcher handles on the
calling thread.
Scale boundaries¶
Workbook(write_only=True) streams row XML into a per-sheet spool. Each active
sheet retains up to 512 KiB of row XML in memory, then rolls to an unnamed OS
temporary file that is removed when its final handle closes. Shared strings and
styles remain resident, so workloads with many unique strings or styles are not
constant-memory. Write-only workbooks are append-only and consumed after a
successful save.
load_workbook(..., read_only=True) streams worksheet rows. Normal eager loads,
calculation, rendering, conversion, images, shared strings, and style tables are
not covered by a bounded-memory guarantee. Worksheet coordinates are limited to
1,048,576 rows and 16,384 columns; out-of-range writes fail instead of silently
truncating.
Properties¶
sheetnames -> list[str]active -> Worksheet | Nonepower_queries -> PowerQueryCollectionconnections -> tuple[ConnectionMetadata, ...]
Methods¶
create_sheet(title: str) -> Worksheet(write mode)copy_worksheet(source: Worksheet, name: str | None = None) -> Worksheetsave(filename: str) -> Noneclose() -> None__getitem__(name: str) -> Worksheetadd_power_query(name: str, formula: str) -> PowerQueryDefinition(modify mode)update_power_query(name: str, formula: str) -> PowerQueryDefinition(modify mode)remove_power_query(name: str) -> PowerQueryDefinition(modify mode)execute_power_query(name: str | None = None) -> None(always raisesNotImplementedError)create_connection(name: str, connection_type: int, *, connection_string: str | None = None, command: str | None = None, source_file: str | None = None, description: str | None = None) -> ConnectionMetadata(modify mode)retarget_connection(connection_id: int, *, connection_string: str | None = None, command: str | None = None, source_file: str | None = None) -> ConnectionMetadata(modify mode)remove_connection(connection_id: int) -> None(modify mode)
Worksheet copying¶
Use destination.copy_worksheet(source, name="Summary") to copy a self-contained
worksheet into a write-mode or modify-mode workbook. The source may be an
in-memory worksheet or a worksheet loaded in ordinary read or modify mode.
Copy values, explicit cell types, direct style components, row and column
dimensions, merged ranges, page settings, print setup, and freeze panes.
Rewrite genuine self-sheet formula references and internal hyperlinks to the
new title while preserving string literals. Explicit name collisions and
read-only/write-only modes are refused.
from wolfxl import Workbook, load_workbook
source = load_workbook("source.xlsx")
destination = Workbook()
copied = destination.copy_worksheet(source["Summary"], name="Imported Summary")
destination.save("combined.xlsx")
source.close()
destination.close()
Cross-workbook copying supports bounded sheet-scoped defined names belonging to
the source worksheet. Admitted defined names must have valid non-empty names,
cannot be macros or procedures (vb_procedure, xlm), and must resolve closed
local dependencies (absolute or relative cell/range coordinates or admitted
sheet-scoped defined names). Self-sheet references in defined-name formulas are
rewritten to the destination worksheet name, and admitted defined names are
registered on the destination worksheet (ws.defined_names) and in the destination
workbook's local defined names registry with retargeted localSheetId. Formulas
in copied cells referencing admitted sheet-scoped defined names are resolved and admitted.
Cross-workbook copying refuses unsupported foreign-sheet, external-workbook,
3D, and structured-table dependencies before destination mutation.
Dynamic INDIRECT and HYPERLINK formulas are also refused. Case-insensitive
defined-name collisions against existing names in the destination worksheet or
workbook (global or local) fail closed and refuse before mutation. It also refuses
drawings, images, charts, comments, tables, conditional formatting, validations,
pivots, query tables, slicers, sparklines, and scenarios.
The existing same-workbook dependency-copy behavior is unchanged.
For a same-workbook copy in modify mode, assign edited legacy or threaded
comments through the copied cell's comment or threaded_comment property
before saving. Preserve independent source/copy comments and their package
relationships through repeated save/reopen.
Non-Normal named styles used by copied cells are migrated into the destination workbook's named style registry. Semantically identical styles are deduplicated, while conflicting style definitions with matching names or incompatible theme schemes fail closed and refuse before mutation. Direct cell formatting overrides and source workbook style bindings are preserved.
For an existing target worksheet, use
WorksheetCopy(source, target).copy_worksheet() from wolfxl.worksheet.copier.
Replace copied cells' direct styles, including defaults, and preserve the
target on refusal or failure. Copying does not calculate formulas or migrate
unsupported dependent resources.
Power Query¶
WolfXL inspects embedded, connection-only Power Query definitions without
executing connectors or accessing data sources. Inventory records expose names,
connector kinds, load destinations, formula sizes, and formula hashes. They do
not expose formulas, locations, credentials, or raw DataMashup bytes.
Path-backed workbooks opened with modify=True can add, update, and remove
bounded definitions:
from wolfxl import load_workbook
workbook = load_workbook("template.xlsx", modify=True)
workbook.update_power_query("Sales", "let Value = 1 in Value")
workbook.save("updated.xlsx")
Authoring accepts a restricted, offline subset of M. It rejects connector calls, dynamic evaluation, credential-like tokens, source locations, worksheet loads, data-model loads, and native queries before writing output. WolfXL preserves unrelated package parts and validates the source package again during save.
Workbook Connections¶
WolfXL provides immutable inspection, authoring, bounded retargeting, and owner-validated removal of workbook connections (xl/connections.xml):
from wolfxl import load_workbook
wb = load_workbook("model.xlsx", modify=True)
# Inspect immutable connection metadata snapshots
for conn in wb.connections:
print(conn.connection_id, conn.name, conn.connection_type, conn.description)
# Create a new connection (types: 1=ODBC, 5=OLE DB, 6=Text)
new_conn = wb.create_connection(
name="SalesWarehouse",
connection_type=1,
connection_string="ODBC;DSN=SalesData;",
command="SELECT * FROM [Orders]",
)
# Retarget an existing connection
wb.retarget_connection(
1,
connection_string="OLEDB;Provider=Microsoft.ACE.OLEDB.16.0;Data Source=new_db.accdb",
command="SELECT * FROM [UpdatedData]",
)
# Remove an unreferenced connection
wb.remove_connection(new_conn.connection_id)
wb.save("updated_model.xlsx")
Connection Lifecycle Guarantees¶
- Creation (
wb.create_connection): Authors new connections in modify mode for supported normative types: 1 (ODBC) and 5 (OLE DB) with<dbPr>, and 6 (Text) with<textPr>. Connection type 4 (Web query) is refused. Allocates deterministic unused positive IDs safely even when existing connection IDs are sparse, and enforces non-empty unique names (case-insensitive). Creating the first connection in a connection-free workbook handles the 0-to-1 package transition by addingxl/connections.xml, workbook relationship parts, and content type declarations. - Retargeting (
wb.retarget_connection): Updates existingdbPrconnection strings and commands as well as text connectionsourceFilemetadata in modify mode. All IDs, connection types, opaque attributes, child elements, and<extLst>extensions are preserved. - Removal (
wb.remove_connection): Stages connection removal by connection ID in modify mode. Refuses before mutation (ValueError) if the connection is currently referenced by any active query table, pivot cache, or other owner. Removing the last remaining connection handles the 1-to-0 package transition by cleanly removingxl/connections.xml, relationship parts, and content type declarations. - Staged Operation Composition: Staged create, retarget, and removal operations compose safely within a single session (for example, retargeting an owning query table to a new connection and then removing the former connection).
- Credential Safety: Inspect connection strings, commands, and source locations only through explicit attributes. Sensitive values are excluded from implicit
reprand error messages. - Save Workflows & Publication: Connection authoring and lifecycle updates are preserved across repeat path saves,
save_to_bytes(), binary streams (save_to_stream()/BytesIO), and password-protected encrypted saves (save(..., password="...")). Completed package output is staged atomically before destination replacement. - Authorized-execution boundary: WolfXL does not evaluate Power Query M or manage credentials.
refresh_query_tables()refreshes database and web query tables only through caller-supplied authorization; WolfXL performs no network I/O itself.
Local and Authorized Query Table Refresh¶
WolfXL supports atomic, bounded refresh of local text query tables and caller-authorized database or web query tables in path-backed modify-mode workbooks. This source-qualified contract does not assert installed-wheel availability or release adoption:
from wolfxl import load_workbook
wb = load_workbook("report.xlsx", modify=True)
count = wb.refresh_query_tables()
print(f"Refreshed {count} query tables")
wb.save("report_updated.xlsx")
For database query tables, pass database_connections, a mapping from the exact workbook connection ID to a caller-owned DB-API connection. WolfXL executes the connection's stored command through that connection; the cursor must provide execute(), fetchmany(), and a description whose column names exactly match the query-table headers. A database connection not present in the mapping is refused.
For web query tables, pass web_authorizer, a callable receiving the exact connection ID and endpoint. It must return bytes or a UTF-8 str response; WolfXL parses the returned payload but never performs network I/O. Omitting the callback refuses web refresh.
Supported Text Dialects and Delimiters¶
Refreshes inspect the connection's OOXML <textPr> element in xl/connections.xml to determine the delimited format. Per the OOXML CT_TextPr schema, tab defaults to true. Supported delimited dialects include:
- TSV (Tab-separated values): default when no delimiter attribute is declared, or explicitly declared via tab="1", delimiter="	", or delimiter="\t".
- CSV (Comma-separated values): declared via tab="0" comma="1" or tab="0" delimiter=",".
- Semicolon-separated values: declared via tab="0" semicolon="1" or tab="0" delimiter=";".
- Pipe-separated values: declared via tab="0" delimiter="|".
Multiple active separators (for example, declaring comma="1" without setting tab="0") or unsupported delimiters are rejected. Delimiter flags set to 0 with a custom delimiter attribute allow the custom delimiter to independently activate that character.
Qualifiers and Text Fields¶
- Text Qualifiers: Documented OOXML
qualifier="doubleQuote"(default"),qualifier="singleQuote"('), andqualifier="none"(no quoting stripped). - Schema Representation: Canonical OOXML
<textFields count="N"><textField type="text"/>...</textFields>container within<textPr>.
Execution and Safety Guarantees¶
- Strict UTF-8 and Character Set: Source files are read strictly as UTF-8. Canonical
characterSetoverridescodePageper primary documentation. UTF-8 BOM (\xef\xbb\xbf) is preserved and not silently stripped; source headers must match the query table field headers byte-for-byte. - All-Text Values: Refreshed data cells are inserted strictly as text cells (
inlineStr). Formula-like strings (such as=SUM(...)) are preserved as text values without formula evaluation. Empty strings are populated as blank cells. - Atomic Preflight: Multi-table and single-table refreshes validate all source paths, headers, dimensions, cell corridors, and destination table boundaries prior to queueing any changes. If any validation or parsing step fails, no destination worksheets are modified.
- Local-Source Security: Refreshes only access local regular files within the workbook's source directory tree. Directory traversal (
..), absolute paths, device files, and files exceeding 16 MiB or 100,000 rows are rejected.
Context manager¶
Workbook supports with statements.