From zero to a professional PDF test report in under five minutes.
Requires Python 3.10 or later.
For PDF export, install the [pdf] extra which pulls in
weasyprint
(also needs system libraries — see
weasyprint docs):
The simplest possible report — no header, no sections, just a flat list of measurements.
from instrumation_report import Report, Measurement r = Report() r.add(Measurement("3.3V Rail", 3.31, "V", condition=(3.2, 3.4))) r.add(Measurement("5V Rail", 5.02, "V", condition=(4.9, 5.1))) r.add(Measurement("Frequency", 1002, "Hz", condition=lambda v: v > 1000)) r.generate_html("report.html") r.generate_excel("report.xlsx")
Run it:
You'll get report.html and report.xlsx in the current directory. Open report.html in a browser.
ReportHeader, the report title defaults to
"Measurement Report" and the date is filled in automatically.
ReportHeader captures UUT metadata — engineer name, serial number, revision, and any custom fields
you need. All fields except title are optional.
from instrumation_report import Report, ReportHeader, Measurement report = Report(header=ReportHeader( title="Power Supply Acceptance Test", subtitle="Final QA — Line 3", engineer="Abduznik", uut_name="PSU-500W", uut_serial="SN-00042", revision="B", extra_fields={"Batch": "2024-Q4", "Station": "TS-01"}, )) report.add(Measurement("Output Voltage", 12.01, "V", condition=(11.8, 12.2))) report.add(Measurement("Ripple", 18.5, "mV", condition=lambda v: v < 50)) report.add(Measurement("Efficiency", 88.4, "%", condition=85.0)) report.generate_html("report.html")
For multi-section test plans, nest measurements inside
Section → TestTable → Measurement.
This maps directly to how numbered test procedures are written.
from instrumation_report import Report, ReportHeader, Section, TestTable, Measurement report = Report(header=ReportHeader(title="RF Module Validation", engineer="Abduznik")) # Section 1 — Power s1 = Section(number="1", title="Power Supply") t1 = TestTable(title="Voltage Rails", sub_number="1.1") t1.add(Measurement("3.3V Rail", 3.31, "V", condition=(3.2, 3.4), test_number="1.1.1", expected="3.2–3.4V")) t1.add(Measurement("5V Rail", 5.02, "V", condition=(4.9, 5.1), test_number="1.1.2", expected="4.9–5.1V")) s1.add_table(t1) report.add_section(s1) # Section 2 — RF s2 = Section(number="2", title="RF Performance") t2 = TestTable(title="Frequency Response", sub_number="2.1") t2.add(Measurement("Center Freq", 2401.5, "MHz", condition=(2400, 2403), test_number="2.1.1")) t2.add(Measurement("Return Loss", 18.5, "dB", condition=15.0, test_number="2.1.2")) s2.add_table(t2) report.add_section(s2) report.generate_html("report.html") report.generate_excel("report.xlsx") report.generate_pdf("report.pdf") # requires [pdf] extra
The condition parameter on a Measurement controls pass/fail evaluation.
Three formats are supported:
| Type | Example | Passes when |
|---|---|---|
| Range tuple | condition=(1.0, 5.0) | lo <= value <= hi |
| Lambda | condition=lambda v: v > 4.0 | callable returns True |
| Threshold float | condition=3.0 | value >= threshold |
| None (omit) | no condition argument | always N/A — informational only |
lambda v: abs(v - 5.0) < 0.1 is perfectly valid.
PDF is rendered from the same HTML template via weasyprint. weasyprint needs system-level libraries (Pango, GObject) that aren't bundled with the Python package.
# macOS brew install pango pip install "instrumation-report[pdf]" # Ubuntu / Debian apt-get install libpango-1.0-0 libpangoft2-1.0-0 pip install "instrumation-report[pdf]"
Ready to see a full real-world example? Browse the examples →