ltem (Python)
Reference Python implementation of LTEM: Markdown pipe tables with merged cells and multi-row headers. It follows the LTEM specification, version 1.0.
Status: version 0.1.1, for LTEM 1.0. The API may change before version 1.0.0.
Install
pip install ltem-core # no dependencies
pip install "ltem-core[markdown]" # also renders inline Markdown in cells (uses markdown-it-py)
Use
import ltem
result = ltem.parse_table("""\
| Region | Revenue | << |
| ^^ | Q1 | Q2 |
|--------|---------|-----|
| Asia | 100 | 120 |""")
table = result.table # the table model (see ast/ in the LTEM repository)
table.to_dict() # JSON-ready dict
ltem.render_html(table) # <table> with colspan and rowspan
ltem.write(table) # canonical LTEM
result.problems # validation problems, if any
for item in ltem.from_html(html): # HTML tables -> table models
print(ltem.write(item.table), item.warnings)
text, sources = ltem.write_with_sources(table) # canonical LTEM and where each cell is
Where cells are
ParseResult.sources and write_with_sources give, for each cell of the table and in the same order, a CellSource(row, col, rowspan, colspan, line, start, end):
linecounts lines from 0: lines of the parsed text forparse_tableandfind_tables, lines of the table forwrite_with_sources.startandendindex that line in Python string units (code points). They cover the cell's source without the spaces and tabs around it, including any span attribute and escapes.- For an empty cell,
startequalsend, at the end of its space between the pipes. A cell added because its row was short (rule V14) hasstart == end ==the length of its line.
Without markdown-it-py, cell text is HTML-escaped and backslash escapes are applied, but emphasis, links and code are shown as written.
ltem.find_tables(markdown) finds every LTEM table in a Markdown document. It is a light scan that skips code blocks. It is not a full Markdown parser.
Command line
ltem parse [FILE] # table models as JSON
ltem render [FILE] # HTML of each table
ltem validate [FILE] [--json] # validation problems; exit status 1 if any
ltem fmt [FILE] # rewrite every table in canonical form
ltem from-html [FILE] [--header-rows N] [--plain] # convert HTML tables to LTEM
from_html(html, header_rows=None, inline="markdown") options:
header_rows: the number of header rows. By default it is the<thead>rows; without<thead>, the leading rows made only of<th>cells; and if there are none, the first row (with a warning). With0, the table has no header: it gets one header row of empty cells and every HTML row becomes a body row (spec Section 11).inline:"markdown"keeps emphasis, code, links and images as Markdown;"plain"keeps text only. Either way, text is escaped so that it reads literally.
<th> cells below the header rows become ordinary cells, since LTEM 1.0 has no row headers; the converter reports this once per table in warnings.
from_html forms the grid as browsers do: cells take the next free position, rowspan="0" reaches the end of its row group, and spans stop at the edge of <thead>, <tbody> or <tfoot>. The header rows are the <thead> rows; without <thead>, the leading rows made only of <th> cells; and if there are none, the first row. Cell content becomes inline Markdown: <br> and block elements become line breaks, and <strong>, <em>, <code>, <a>, <img>, lists, <sup> and <sub> are kept. A table inside a cell is flattened to text with a warning, since LTEM 1.0 has no nested tables.
Development
From ltem-lib/python:
uv sync --all-packages
uv run --directory ltem pytest
The tests run the conformance cases in conformance/.
Metadata
Release files for ltem-core 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ltem_core-0.1.1.tar.gz | 27.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ltem_core-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.7 kB
Release files / ltem_core-0.1.1.tar.gz
| Download URL | ltem_core-0.1.1.tar.gz |
|---|---|
| Size | 27.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0c2ea5dce584dcfa98f3fc5edb3afc6b45f47c3b0e9e1d24620a8dd30b70674e
|
|
BLAKE2b-256 checksum How to use checksums |
1d8001fe5f4004cd6ad88f7083b67df117efd526bceff669dbfbbf4742db41a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / ltem_core-0.1.1-py3-none-any.whl
| Download URL | ltem_core-0.1.1-py3-none-any.whl |
|---|---|
| Size | 24.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5146b6223fe543ed1b3a4f9aac1fc07f5d45ddaeba2b0ac8d2522b8e0d8ffee5
|
|
BLAKE2b-256 checksum How to use checksums |
2795613b4d4ca2e755dd6c4b47b2d6fddc54adbaa750604c2ca24d14b9c5f574
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Security for ltem-core 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.