pyRegTab: pattern-based extraction of recordsets from tables (RTL / ATP / ITM)
Project description
pyRegTab
RegTab: pattern-driven data extraction from document tables with regular structure — the Python port of jRegTab with a native Rust core.
pyRegTab compiles RTL (Regular Table Language) patterns into abstract table patterns (ATP), matches them against a table's syntactic layer (ITM), and interprets the match into a relational recordset:
TableSyntax → RtlCompiler/TablePattern → AtpMatcher → TableInterpreter → Recordset
pyRegTab 0.1.x ≙ jRegTab 0.4.0 (same API, same semantics, same test
corpus). 0.2.0 adds the embedded RTL DSL (pyregtab.dsl), mirroring
jRegTab's ru.icc.regtab.dsl.
Installation
pip install pyregtab
Binary wheels are published for Windows, Linux and macOS (x86-64 / arm64),
CPython ≥ 3.10 (one abi3 wheel per platform). Building from the sdist
requires a Rust toolchain.
Example
from pyregtab import TableSyntax, RtlCompiler, AtpMatcher, TableInterpreter
syntax = TableSyntax(3, 3)
syntax.cell(0, 1).set_text("CA"); syntax.cell(0, 2).set_text("HU")
syntax.cell(1, 0).set_text("IKT"); syntax.cell(1, 1).set_text("0 Jan"); syntax.cell(1, 2).set_text("8 Feb")
syntax.cell(2, 0).set_text("SVO"); syntax.cell(2, 1).set_text("31 Jan"); syntax.cell(2, 2).set_text("40 Feb")
pattern = RtlCompiler.compile("""
[ [] [VAL : 'AIRLINE'->AVP]+ ]
[ [VAL : 'AIRPORT'->AVP]
[VAL : (COL, ROW, CL)->REC, 'ND'->AVP " " VAL : 'MON'->AVP]+ ]+
""")
itm = AtpMatcher.match(pattern, syntax) # InterpretableTable | None
rs = TableInterpreter().interpret(itm) # Recordset
rs.schema.attributes # ['ND', 'AIRLINE', 'AIRPORT', 'MON']
rs[0]["ND"] # '0'
df = rs.to_pandas() # extras: pip install pyregtab[pandas]
Patterns can also be built without RTL, via the fluent spec API
(TablePattern.of(SubtablePattern.of(...)) — same factories as in Java,
snake_case method names), and serialized back to RTL with
AtpToRtlSerializer.serialize(pattern).
For a terser, RTL-like way to build patterns in code, use the embedded RTL
DSL (pyregtab.dsl) — see Embedded RTL below.
Named Python predicates are attached to RTL via EXT('name'):
from pyregtab import Bindings
p = RtlCompiler.compile(
"{ [ [EXT('isTotal') ? VAL : ST*->REC] []+ ] }+",
Bindings.of().cell("isTotal", lambda cell: cell.text.startswith("Total")),
)
Embedded RTL
The pyregtab.dsl module is a fluent DSL that reads almost like RTL but is
ordinary Python — with IDE completion, structural typing, pattern composition
via plain variables, and Python callables as escape-hatch constraints. It builds
the same TablePattern objects as the compiler (verified byte-for-byte
against RtlCompiler.compile for a representative set of tasks in
tests/test_dsl.py).
from pyregtab.dsl import *
# RTL: { [ [VAL : ST*->REC] [VAL]{2} []+ ]
# [ [] [VAL]{4} []+ ] }+
p = table(
subtable(
row(cell(VAL, rec(ST.unbounded())), cell(VAL).exactly(2), skip().one_or_more()),
row(skip(), cell(VAL).exactly(4), skip().one_or_more()),
).one_or_more())
Method names are snake_case (.one_or_more(), .and_(), .split_by()); the
vocabulary constants (VAL, ST, COL, C(n), …) match RTL. See the
Embedded RTL guide for the full mapping and the
where(...) escape hatch.
API mapping (Java → Python)
| Java | Python |
|---|---|
RtlCompiler.compile(String) |
RtlCompiler.compile(str) / pyregtab.compile(...) |
AtpMatcher.match(p, s) → Optional<InterpretableTable> |
AtpMatcher.match(p, s) → InterpretableTable | None |
Quantifier.oneOrMore() |
Quantifier.one_or_more() |
new TableInterpreter().withStrategy(s).interpret(itm) |
TableInterpreter().with_strategy(s).interpret(itm) |
rs.records().get(0).get("Name") |
rs[0]["Name"], rs.records, record.get("Name") |
cell.text() / cell.setText(t) |
property cell.text (get/set); cell.set_text(t) also works |
RtlCompileException |
RtlCompileError |
Architecture
Everything after the Python call boundary runs in a native core written in
Rust (pyregtab._core, built with PyO3 and
maturin); the Python layer is a thin re-export.
grammar/RTL.g4— the normative specification of the RTL language (a verbatim copy from jRegTab; the upstream commit and the grammar's SHA-256 are recorded ingrammar/UPSTREAM). The core's parser is a hand-written lexer + recursive descent that structurally follows the grammar rules. A CI job (tools/check_grammar_sync.py) fails the build if the copy drifts from the pinned hash, and — when a jRegTab read token is available — cross-checks it byte-for-byte against the upstream commit.conformance/— the shared RTL conformance corpus (also pinned from jRegTab, seeconformance/UPSTREAMandconformance/README.md). Both implementations must compile every positive case to the same canonical form and reject every negative case; the corpus runs in CI of both projects. Any RTL language change flows:RTL.g4in jregtab → corpus extension → both parsers → green corpus in both CIs.- Regular expressions in RTL constraints are executed by the Rust
regexcrate (linear-time). The reference fixture corpus uses no lookaround/backreferences (audited), so the dialect is compatible withjava.util.regexon this corpus. Documented divergences from Java:\d/\s/\ware Unicode-aware inregex(ASCII in Java), andSUBSTRindices count code points (UTF-16 units in Java) — identical behavior on the entire reference corpus.
Testing
pytest tests runs (1 904 tests):
- the full benchmark suite — tasks 001–150 (Foofah, RegTab, Baikal),
every fixture variant, both via RTL patterns and via ATP patterns
built with the Python spec API (1 500 task variants in total; fixtures
are copied verbatim from jRegTab into
tests/fixtures/tasks, ATP builders are mechanically translated from the Java tests bytools/translate_atp.py); - embedded RTL DSL parity — 26 representative tasks/constructs built with
pyregtab.dslproduce byte-identical ATP toRtlCompiler.compile(tests/test_dsl.py); - the RTL conformance corpus (positive canonical forms, fixed points, negative rejections);
- RTL↔ATP round-trip for tasks 001–050;
- API unit tests (syntax layer, extractors, EXT bindings, custom predicates, transformations, interpreter options, GIL-released batch matching from a thread pool).
cargo test additionally runs the conformance corpus and an end-to-end
smoke test against the native core alone. Differential testing against the
Java reference (tools/differential.py + tools/RecordsetDumpMain.java)
compares recordsets cell-by-cell on all 750 task variants — zero
mismatches against jRegTab v0.4.0.
IDE support
ide/vscode/ is a VS Code extension (and IntelliJ/PyCharm TextMate bundle)
that highlights .rtl files and RTL embedded in Python strings passed to
RtlCompiler.compile(...). See ide/README.md. RTL is also
validated at compile time: RtlCompiler.compile(...) raises RtlCompileError
with a line:col position on an invalid pattern.
Development
python -m venv .venv && . .venv/bin/activate # or .venv\Scripts\activate
pip install maturin pytest
maturin develop --release
pytest tests -q
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyregtab-0.2.0-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: pyregtab-0.2.0-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 1.2 MB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab68393227eb36fd761bc0564a7b3bd6d6507fb4a7a368da802fbf6dc4d25979
|
|
| MD5 |
e1a2b41b044823ed613a19ea2f9b50d0
|
|
| BLAKE2b-256 |
d06baec4c6adc317c75dcbb35b58f5ed185e757fc00ab08acc66d1b8a7f6e603
|
File details
Details for the file pyregtab-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: pyregtab-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
632d630ea4635a991b35362802b57caa72cb4e972cc1dbc867299a81d7b7ea8b
|
|
| MD5 |
dd57e94bb4e99b8c5174b686ffaba547
|
|
| BLAKE2b-256 |
7c163408249dcec97c3e9f0370a6bb9f2da3fb0c230689ca401ac3d9d33a019d
|
File details
Details for the file pyregtab-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: pyregtab-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.3 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c9202aa66d1bd88991a79899187b4b22ded21ee3c3feeb901ac3568731cf617
|
|
| MD5 |
8251e262a18c0c94db3b0a5b03c3d80f
|
|
| BLAKE2b-256 |
664db4c00a66de50422eead90bbf445cb3bf59192138dd888e27a8f1970ff4c4
|
File details
Details for the file pyregtab-0.2.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: pyregtab-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.3 MB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f5b0503b2b002bc61c4d8a4f6b9241b9e0fac0695aba4c648a80530be9256f1
|
|
| MD5 |
c9d1f163103f60779c63c3bc4f41a07e
|
|
| BLAKE2b-256 |
27b06c4de7d680ab85450666ce417c31c1e9f72cc02c075e4fbd83d65d1d67c5
|
File details
Details for the file pyregtab-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: pyregtab-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 1.3 MB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3805135077e650c27a5e5ab09aa8f9b329b6a7cf69c28eabcb3c2af111f25834
|
|
| MD5 |
3ece60ae4eae8c0f46f50bee58a0822a
|
|
| BLAKE2b-256 |
9f7d4792c1cef821607a45ddb078877a4e38a5b2f9afb5f3054fb1fc2684c5ac
|