Tutorial

Get Started

From zero to a professional PDF test report in under five minutes.


1 · Install

Requires Python 3.10 or later.

$ pip install instrumation-report

For PDF export, install the [pdf] extra which pulls in weasyprint (also needs system libraries — see weasyprint docs):

$ pip install "instrumation-report[pdf]"

2 · Minimal example

The simplest possible report — no header, no sections, just a flat list of measurements.

minimal.py
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:

$ python minimal.py

You'll get report.html and report.xlsx in the current directory. Open report.html in a browser.

No header needed. If you skip ReportHeader, the report title defaults to "Measurement Report" and the date is filled in automatically.

3 · Add a report header

ReportHeader captures UUT metadata — engineer name, serial number, revision, and any custom fields you need. All fields except title are optional.

with_header.py
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")

4 · Structure with Sections and Tables

For multi-section test plans, nest measurements inside Section → TestTable → Measurement. This maps directly to how numbered test procedures are written.

structured.py
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

5 · Condition types

The condition parameter on a Measurement controls pass/fail evaluation. Three formats are supported:

TypeExamplePasses when
Range tuplecondition=(1.0, 5.0)lo <= value <= hi
Lambdacondition=lambda v: v > 4.0callable returns True
Threshold floatcondition=3.0value >= threshold
None (omit)no condition argumentalways N/A — informational only
Lambda tip: use a lambda for anything a tuple can't express — inequalities, compound checks, absolute tolerance, etc. lambda v: abs(v - 5.0) < 0.1 is perfectly valid.

6 · PDF export

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.

shell
# 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]"
weasyprint on Python 3.14 may require the latest weasyprint release. If you see a cffi import error, check the weasyprint troubleshooting page.

Ready to see a full real-world example? Browse the examples →