Skip to content

Releases: RexBytes/xldetect

xldetect 1.0.1 — Review-hardened bug-fix release

Choose a tag to compare

@RexBytes RexBytes released this 19 Jun 15:16
c59fb9c

xldetect 1.0.1

A bug-fix release. No API changes and no new features — xldetect still does
exactly what 1.0.0 did, but four defects surfaced by a competitive multi-model
review were fixed, each with a regression test. Drop-in upgrade from 1.0.0.

Fixes

  • Malformed-workbook errors no longer escape as a traceback. A zip-shaped
    file that is not a real .xlsx — a plain .zip renamed to .xlsx (no
    [Content_Types].xml), or a corrupt/truncated Office file — now raises a clean
    ValueError from inspect_path()/load_grids() and exits the CLI with the
    usual xldetect: error: … message instead of an uncaught KeyError /
    ParseError.
  • A first data row with a blank optional column is no longer mistaken for a
    header.
    Header detection treated a blank candidate cell over a populated
    column as "type-distinct", so an ordinary record whose optional numeric/date
    field was empty could be swallowed as a second header row — losing that row and
    taking the headers from it. Stacked-header detection now requires both the
    candidate and the body cell to be populated before comparing them.
  • --header-threshold validates like the other options. An out-of-range or
    non-numeric value is now a usage error (exit code 2), matching
    --min-blank-rows, instead of surfacing as a runtime error (exit code 1).
  • Docstring corrections for the decorative-trim and confidence-degeneracy
    behaviours (no behavioural change).

Upgrade

pip install --upgrade xldetect

Requires Python 3.11+. Runtime dependency: openpyxl>=3.1.

Quality

This release was put through a four-round competitive multi-model review
(opus, sonnet, haiku on the same brief each round; see CONTRIBUTING.md and
REVIEW_HISTORY.md):

  • Panels 1–2 found and fixed the four defects above (2 MEDIUM, 2 LOW). Every one
    was a singleton — found by a single model and missed by the other two — which
    is the case for running diverse reviewers rather than one.
  • Panels 3–4 came back clean at full diversity, satisfying the release rule (all
    gates green, RRS ≥ 90, two consecutive full-diversity clean panels).
  • scripts/readiness.py: gates green, RRS 94.3/100, convergence confidence
    0.86 → RELEASABLE.

151 tests (truth-table corners, threshold pinning, Hypothesis property
tests, golden CLI files, and a live xlfilldown round-trip), 99% coverage;
ruff + mypy clean. CI runs on Python 3.11 and 3.12 across an openpyxl
version matrix.

Full API, scope, and design rationale: see README.md, SKILL.md, and
LIMITATIONS.md. Review trajectory: see REVIEW_HISTORY.md.

xldetect 1.0.0 — Find the table in any Excel sheet

Choose a tag to compare

@RexBytes RexBytes released this 16 Jun 11:25
551f087

xldetect 1.0.0

First stable release. xldetect is the discovery step for messy Excel
workbooks: it finds where the data is — table regions, headers, and merged
cells — so a processing step like xlfilldown
can act on it. openpyxl tells you cell values; xldetect tells you where the
table starts.

Highlights

  • Multi-table detection — multiple rectangular regions per sheet (stacked
    and side-by-side), across sheets, via recursive blank-gap segmentation.
    Every occupied cell lands in exactly one non-overlapping region.
  • Header detection — content-based scoring (text-over-data type distinction)
    with formatting as corroborating evidence: bold/fill/border can only raise a
    header's confidence, never lower it, so a clean plain header scores as high as
    a styled one. Conservative multi-row (stacked) header support.
  • Merged cells — reported as ranges and forward-filled with the anchor value
    so banners are detected correctly.
  • Decorative content — title rows and full-width merged banners are trimmed;
    zero-data regions (separated banners, stray titles) are classified decorative
    and kept out of the tabular results (recoverable via decorative_regions).
  • Confidence score per region for easy filtering.
  • xlfilldown integrationto_xlfilldown_plan() maps a region onto
    xlfilldown's real API and emits honest caveats when a region can't be
    ingested losslessly (it reads one header row to end-of-sheet across all
    headered columns).
  • CLIxldetect inspect file.xlsx with text, --json, and --xlfilldown
    output. Bad workbooks and bad arguments produce clean errors, never tracebacks.
  • Typed results — dataclasses with JSON-safe to_dict() throughout.

Install

pip install xldetect

Requires Python 3.11+. Runtime dependency: openpyxl>=3.1.

Quick start

from xldetect import inspect_path

report = inspect_path("messy.xlsx")
for region in report.iter_regions():
    print(region.range_a1, region.headers, region.confidence)
xldetect inspect messy.xlsx              # human-readable preview
xldetect inspect messy.xlsx --json       # full report as JSON
xldetect inspect messy.xlsx --xlfilldown # one xlfilldown plan per region

Pairing with xlfilldown

from xldetect import inspect_path, to_xlfilldown_plan
import xlfilldown

report = inspect_path("messy.xlsx")
region = next(report.iter_regions())
plan = to_xlfilldown_plan(region, "messy.xlsx",
                          sheet_max_col=report.sheets[0].max_col)
if not plan["caveats"]:
    xlfilldown.ingest_excel_to_sqlite(
        file=plan["file"], sheet=plan["sheet"],
        header_row=plan["header_row"], fill_cols=plan["fill_cols"],
        db="out.db", table="data", if_exists="replace",
    )

Notes

  • Detection is structural, not semantic: no formula evaluation, no data
    transformation. Deliberate tradeoffs (single-blank-row splitting, all-text
    header ambiguity, summary rows kept as data, formula caching) are documented
    with rationale and overrides in LIMITATIONS.md.
  • AI assistants: SKILL.md ships in the package as an LLM-consumable guide.

Quality

138 tests (truth-table corners, threshold pinning, hypothesis property tests,
golden CLI files, and a live xlfilldown round-trip), 99% coverage. CI runs on
Python 3.11 and 3.12.

Full scope of in/out of scope, API, and design rationale: see README.md,
SKILL.md, and LIMITATIONS.md.