Skip to content

Python API

Import the names documented here from chooong.datasift. Implementation modules models, reader and rust do not define additional stable import paths. The package includes py.typed and native stubs.

Readers and writers

iter_matching_rows(xlsx_path: PathInput, mask: MaskInput, *, batch_size: int = 1024)
    -> Generator[Row, None, None]
iter_matching_batches(xlsx_path: PathInput, mask: MaskInput, *, batch_size: int = 1024)
    -> BatchIterator
scan_batches(paths: PathInput, mask: MaskInput, *, batch_size: int = 1024,
             rejected: bool = True) -> ScanIterator
export(paths: PathInput, mask: MaskInput, output: str | Path, *,
       rejected_output: str | Path | None = None, batch_size: int = 1024) -> Statistics
recover_export(output: str | Path, *, journal_sha256: str,
               rejected_output: str | Path | None = None) -> None

PathInput is str | Path | Sequence[str | Path]. Strings can include glob patterns. MaskInput is FilterMask | Mapping[str, object] | str | Path: strings beginning with { after whitespace are JSON; other strings are filenames. Mask files use UTF-8. Validated models are revalidated at the reader boundary; constructed/copied invalid models cannot bypass validation. Mapping/JSON input uses wire aliases.

iter_matching_rows initializes on its first next(). The other iterators validate mask/regex configuration before return and wait for native readiness. First-schema/file validation can occur before return or during iteration, depending on named/all-sheet, source, free-discovery and multi-file modes. Use error timing when handling partial output.

BatchIterator yields list[Row]; ScanIterator yields list[ScanRecord]. Both expose close(), a context manager, and read-only .statistics (Statistics | None). rejected=False suppresses rejected event records while retaining rejection statistics. export returns completed statistics or raises. Recovery semantics and its required digest are in file safety.

Early termination

from contextlib import closing
from pathlib import Path

from chooong.datasift import iter_matching_batches, iter_matching_rows

Path("data.csv").write_text("ID,Amount\na,1\nb,2\n", encoding="utf-8")
with closing(iter_matching_rows("data.csv", {"sheet": None})) as rows:
    first = next(rows)
    assert first["ID"] == "a"
with iter_matching_batches("data.csv", {"sheet": None}, batch_size=1) as batches:
    assert next(batches)[0] == first

See streaming for cancellation, backpressure, schema boundaries and thread ownership.

Results

Row(cells: Mapping[str, CellValue]) is a frozen immutable mapping. Use row["ID"], len(row), iteration over keys or dict(row). It copies the supplied mapping and exposes a read-only view. Column selection/order is defined in filter models.

Public value/model Fields
Money amount: Decimal, currency: str
PreciseDateTime value: str, an ISO civil/offset datetime retaining nanoseconds
ScanRecord kind, file, sheet, table, row, values, reasons=(), source_types
RejectReason code: RejectCode, column: str \| None = None
Diagnostic code, file, optional sheet, table, complete
Statistics rows_read, matched, rejected, defaults, conversions, reasons, tables, diagnostics, complete, seconds, row_classes
TableExplanation file, sheet, table, header_row, bindings, defaults, types, type_rules, header, raw_headers, data_start_row, data_end_row

RecordKind is matched | rejected. RejectCode values and diagnostic codes are listed in errors. Rejected values are original; matched values are converted/projected. Event values use validated tagged literal models for exact types. source_types describes originals; row is a physical Excel row or a logical CSV record. Table bindings use zero-based physical column indices.

Statistics counters

Counters accumulate across input files, worksheets and tables in one scan. They describe processing, including rejected records, rather than just exported matches. Missing keys in reasons and row_classes mean zero occurrences.

Counter Counting unit and scope
rows_read Input rows delivered by the parser, including structural rows; missing Excel rows are not synthesized
matched, rejected Records per eligible table/source; overlapping sources can count one physical row more than once
defaults Default assignments to empty cells, before conversion and filtering; includes later rejected records
conversions Successful type-rule applications that change a cell value/type; failures and already-compatible unchanged values do not increment it
reasons Occurrences of each rejection code; several columns can contribute the same code to one rejected record
row_classes Classified table/source rows before include/exclude filtering; structural header counts also depend on discovery mode
seconds Worker processing time including channel backpressure, rather than isolated CPU time

Defaults and conversions count cells, not rows, and are not limited to projected columns. A default followed by a successful conversion increments both counters. Summed reason counts can exceed rejected; they are not counts of distinct rows. rejected=False suppresses rejected events, without changing these counters.

row_classes includes excluded classifications. Its sum need not equal rows_read: overlapping sources count independently, discovery can consume rows before classification, and quarantined regions emit schema rejects outside the normal classification path. Header accounting varies between automatic discovery and explicit sources; these counters are not a partition of physical input rows.

For example, the first row below receives a default and converts it before another column fails. The second row performs two successful conversions; the third fails in two columns with the same rejection code:

from pathlib import Path

from chooong.datasift import RejectCode, scan_batches

Path("counters.csv").write_text("ID,A,B\na,,bad\nb,0,1\nc,bad,bad\n", encoding="utf-8")
mask = {
    "sheet": None,
    "header": {"anchors": ["ID", "A", "B"]},
    "defaults": {"A": "2"},
    "types": {
        "A": {"type": "integer", "convert": True},
        "B": {"type": "integer", "convert": True},
    },
}
with scan_batches("counters.csv", mask, rejected=False) as scan:
    matches = [record for batch in scan for record in batch]
    statistics = scan.statistics
assert len(matches) == 1
assert statistics is not None and statistics.complete
assert (statistics.matched, statistics.rejected) == (1, 2)
assert (statistics.defaults, statistics.conversions) == (1, 3)
assert statistics.reasons[RejectCode.CONVERSION_FAILED] == 3

Statistics availability and incomplete scans follow the streaming contract.

Value types

CellValue is str | bool | int | float | None | Decimal | Money | date | datetime | PreciseDateTime. Expression and MaskInput are also public type aliases. Data types defines representations and precision.

Filter constructors

The supported filter constructors are FilterMask, Rules, TableSource, ColumnBinding, HeaderConfig, CsvConfig, RowPolicy, RowRule, TypeRule, RegexSelection, ScalarCondition, MembershipCondition, NumericCondition, TextCondition, EmptyCondition, TypeCondition, AllOf, AnyOf, Not, DecimalLiteral, MoneyLiteral, DateLiteral and DatetimeLiteral. Enums are Operator, DataType and RowClass. Their exact fields and defaults are documented in filter models.