isis-report-lib
Python library for tabular PDF reports with customizable hierarchical layouts — extracted from the isis_report PoC (Robyn microservice), without the server.
- Input:
dict/ReportPayload(meta+ optionallayout+data) or.toontext - Rendering: Jinja2 (templates bundled in the package)
- PDF: Playwright (Chromium)
- API: synchronous (
generate_pdf) + asynchronous (agenerate_pdf)
Installation
pip install isis-report-lib
playwright install chromium # required to generate PDFs
After upgrading
playwright, reinstall the browser (playwright install chromium): each Playwright version pins a specific Chromium build.
Dev:
pip install -e ".[dev]"
Quickstart
from isis_report_lib import generate_pdf, generate_pdf_from_toon, render_html
payload = {
"meta": {"title": "Sales Order", "sub_title": "2024", "mode": "exact"},
"layout": {
"company": {"label": "Company", "format_mask": "str", "alignment": "left"},
"orders": {
"order": {"label": "Code", "format_mask": "int", "alignment": "right"},
"total": {"label": "Total", "format_mask": "float", "alignment": "right"},
},
},
"data": [{"company": "ACME", "orders": [{"order": 750, "total": 4000}]}],
}
pdf_bytes = generate_pdf(payload)
open("report.pdf", "wb").write(pdf_bytes)
# Without Playwright: just the intermediate HTML
html = render_html(payload)
# From .toon
toon = open("examples/sales_order.toon", encoding="utf-8").read()
open("report_toon.pdf", "wb").write(generate_pdf_from_toon(toon))
Async:
from isis_report_lib import agenerate_pdf, agenerate_pdf_from_toon
pdf = await agenerate_pdf(payload)
pdf = await agenerate_pdf_from_toon(toon_text)
CLI
# PDF from .toon or .json (format guessed from extension)
isis-report generate examples/sales_order.toon -o report.pdf
isis-report generate payload.json --page-format Letter
# Intermediate HTML without a browser (prints to stdout unless -o is given)
isis-report render examples/sales_order.toon -o report.html
PDF options (forwarded to Playwright's page.pdf):
generate_pdf(
payload,
pdf_options={
"format": "A4",
"margin": {"top": ".2in", "bottom": ".5in"},
"header_template": "<div>My header</div>",
"footer_template": "<div>Page <span class='pageNumber'></span></div>",
},
)
Via CLI: --page-format, --header-template FILE, --footer-template FILE. The report footer shows meta.printed_at, auto-filled with the current timestamp when absent.
Full example in examples/. Note: the sync API opens/closes the browser per call (no leaked processes); the async API reuses the shared pool.
Custom templates
Point template_dir at your own .jinja directory and pick a file with template_name:
generate_pdf(payload, template_dir="my_templates", template_name="invoice.jinja")
isis-report generate order.toon --template-dir my_templates --template-name invoice.jinja
The directory is layered over the bundled one, so you can also override tabular.jinja or reuse tabular_sections.jinja macros. Templates receive dataset, layout, meta, plus the fmt(value, mask) formatter and zip — see examples/custom.jinja.
Theming
Set meta.accent_color to rebrand the report header without a custom template:
payload = {"meta": {"title": "Sales Order", "accent_color": "#1B7A3D"}, ...}
It overrides the --brand-color CSS variable. More masks: str · int · float (pt-BR) · date · percent · currency (R$) · bool.
Payload
| Field | Type | Required | Description |
|---|---|---|---|
meta |
object | yes | title, sub_title, mode ("exact"/"gray"), company, locale ("pt-BR", "en-US"…), currency (override, e.g. "EUR"), accent_color, printed_at (auto-filled) |
layout |
object | no | hierarchical field definition; omit to infer from data |
data |
array | yes | raw records or template format ({"fields": ..., "groups": ...}) |
Masks (locale-aware via Babel/CLDR): str · int · float · date (localized for date/datetime objects) · percent · currency · bool (Yes/No).
payload = {
"meta": {"title": "Sales Order", "locale": "en-US"}, # numbers as 1,234.50, $1,234.50
"data": [...],
}
isis-report generate order.toon --locale en-US -o out.pdf # overrides meta.locale
Report parameters
Beyond layout and data, the payload supports declarative behavior — no rigid bands, the document adapts to the data:
payload = {
"meta": {
"title": "Sales Order",
"page": {"size": "A4", "orientation": "landscape", "margins": {"top": ".4in"}},
},
"layout": {
"company": {"label": "Company", "format_mask": "str", "alignment": "left"},
"orders": {
"order": {"label": "Code", "format_mask": "int", "alignment": "right"},
"status": {"label": "Status", "format_mask": "str", "alignment": "left", "visible": False},
"total": {"label": "Total", "format_mask": "float", "alignment": "right", "aggregate": "sum"},
"$print_when": {"field": "status", "not_equals": "cancelled"},
"$order_by": "-order",
},
},
"data": [...],
}
| Parameter | Where | Effect |
|---|---|---|
meta.page |
meta |
{size, orientation: portrait|landscape, margins} → PDF page (explicit pdf_options win) |
aggregate |
field def | sum|count|avg|min|max → group footers, detail tfoot, grand summary. Flat child lists render as tables (several per item allowed); deeper nesting renders as sections |
$print_when |
any level | keep only rows matching {field, equals|not_equals|in|not_in|gt|gte|lt|lte|is_null|not_null} |
$order_by |
any level | sort rows: [{field, direction}] or "field" / "-field" (nulls last) |
visible / visible_when |
field def | hide a column; visible_when supports {..., scope: any|all} |
.toon syntax
meta:
title: Sales Order
mode: exact
layout:
product{Product,str,left}
total{Total,float,right,sum}
data[1]:
- product: Pen
total: 5.50
data[n] holds the records (dados[n] still accepted as a legacy alias). The compact
layout form accepts an optional 4th part with the aggregate: total{Total,float,right,sum}.
See examples/sales_order.toon.
API
| Function | Description |
|---|---|
generate_pdf(payload, pdf_options?) -> bytes |
sync PDF from dict/ReportPayload |
agenerate_pdf(...) |
same, async |
generate_pdf_from_toon(text, ...) / agenerate_pdf_from_toon |
PDF from .toon |
render_html(payload) / arender_html / render_html_from_toon |
intermediate HTML (no browser) |
ToonParser().parse(text) / parse_toon(text) |
.toon -> dict |
TabularReportService(pdf_options?) |
pipeline execute(payload) (Fetch→Process→HTML→PDF) |
Tests
pytest -q
pytest -q --cov=isis_report_lib # coverage gate: 85% (enforced in the CI e2e job)
ruff check src tests
Real-PDF tests require playwright install chromium and are skipped (-m "not browser") when no browser is available.
License
MIT — see LICENSE.
Security
- Dependencies: audited with
pip-audit(no known vulnerabilities); Dependabot keeps actions and packages updated; releases are built and published by CI via OIDC trusted publishing (no stored tokens). - Trust boundary: templates (
.jinjafiles,template_dir) are code — only load them from sources you trust. Report data (dict/JSON/.toon) is never executed as template code, only interpolated. Jinja autoescaping is off by design (report HTML is rendered to PDF, not served to browsers). - Robustness: deeply nested
.tooninput is rejected withValueErrorinstead of crashing the interpreter; the Playwright browser runs headless in its own Chromium sandbox per call (sync API) or a shared pool (async API).
Metadata
Release files for isis-report-lib 0.5.0
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 | Actions |
|---|---|---|---|---|
| isis_report_lib-0.5.0.tar.gz | 36.7 kB | With provenance | Details for isis_report_lib-0.5.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| isis_report_lib-0.5.0-py3-none-any.whl | Python 3 | none | any | Details for isis_report_lib-0.5.0-py3-none-any.whl |
Total release size: 65.1 kB
Release files / isis_report_lib-0.5.0.tar.gz
| Download URL | isis_report_lib-0.5.0.tar.gz |
|---|---|
| Size | 36.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1b211ba90053949b4a09b879564a0f5b597e27fa1afa434c7ad5a321676271d3
|
|
BLAKE2b-256 checksum How to use checksums |
b5eb1e1f4ddda203862d3a9b280cd358dfbb52a312e68e8a04865c7a7074a2b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logRelease files / isis_report_lib-0.5.0-py3-none-any.whl
| Download URL | isis_report_lib-0.5.0-py3-none-any.whl |
|---|---|
| Size | 28.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4d1310eca4c7aa75c062815779d7cf9260547a4e9fd8e995fb4703e074f202b7
|
|
BLAKE2b-256 checksum How to use checksums |
3a9189a45e4c510984a241aabc93c15bbc97f09c3811293027ea2757574a94aa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logSecurity for isis-report-lib 0.5.0
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.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for isis_report_lib-0.5.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logA 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.