← SDKs
Python SDK

majorsilence-reporting

Render RDL reports to PDF, Excel, CSV, and more from Python — via subprocess, an AOT binary, or in-process ctypes FFI. No runtime dependencies beyond the standard library.


Installation

Install from PyPI (Python 3.10+ required):

# PyPI
pip install majorsilence-reporting

# Or editable from source
pip install -e .

No runtime dependencies — uses only the Python standard library (subprocess, ctypes, tempfile).


Integration modes

Three classes are available depending on how you deploy the reporting engine:

ClassMechanismRequirement
ReportSpawns dotnet RdlCmd.dll as a child process..NET runtime installed on the server.
ReportAotSpawns a self-contained AOT-compiled RdlCmd binary.AOT binary deployed alongside the app. No .NET runtime needed.
report_native.ReportLoads librdlnative.so in-process via ctypes.Native library + sibling .so files deployed. Call load_library() once at startup.

Report — subprocess (dotnet)

from majorsilence_reporting import Report

rpt = Report(
    report_path  = '/path/to/report.rdl',
    rdl_cmd_path = '/path/to/RdlCmd.dll',
)
rpt.set_connection_string('Data Source=/path/to/data.db')
rpt.set_parameter('StartDate', '2026-01-01')

# Save to a file
rpt.export('pdf', '/tmp/output.pdf')

# Or get bytes in memory (e.g. for a Flask response)
data = rpt.export_to_memory('pdf')
return Response(data, mimetype='application/pdf')

ReportAot — AOT binary

Same API as Report but points at a self-contained binary instead of a DLL. No .NET runtime required on the server.

from majorsilence_reporting import ReportAot

rpt = ReportAot(
    report_path  = '/path/to/report.rdl',
    rdl_cmd_path = '/path/to/RdlCmd',   # self-contained binary, no .dll extension
)
rpt.set_connection_string('Data Source=/path/to/data.db')
data = rpt.export_to_memory('xlsx')

# ReportAot also supports xlsx_table, tifb, and mht
data = rpt.export_to_memory('xlsx_table')

report_native — in-process FFI

The native mode loads librdlnative.so directly into the Python process via ctypes. No child process is spawned, making it the lowest-latency option. It also supports true in-memory rendering — the report bytes are returned directly from the native buffer without writing a temp file.

from majorsilence_reporting.report_native import load_library, Report

# Call once at process startup. Loads librdlnative.so and all sibling libraries.
lib = load_library('/path/to/librdlnative.so')

rpt = Report(lib, report_path='/path/to/report.rdl')
rpt.set_connection_string('Data Source=/path/to/data.db')

# Returns bytes directly from the native buffer — no temp file written
data = rpt.export_to_memory('pdf')

# Pass to Django / FastAPI / Flask as a binary response
return Response(data, media_type='application/pdf')
load_library() sets RDLNATIVE_LIB_DIR and pre-loads all sibling .so / .dylib files with RTLD_GLOBAL so .NET P/Invoke resolves correctly. Call it once per process before creating any Report instances.

Core API

All three classes share the same interface:

MethodDescription
set_connection_string(cs)Set the database connection string used by the report. Call before export.
set_parameter(name, value)Set a named report parameter. Both arguments are strings. Call as many times as needed.
export(format, path)Render the report and write the output to path on disk.
export_to_memory(format)Render and return the output as bytes (binary formats) or str (text formats). For report_native.Report, always returns bytes.

In-memory datasets

report_native.Report supports supplying data entirely in memory via add_data() — no database connection required. Pass a dataset name and a list of dicts where every value is a string.

from majorsilence_reporting.report_native import load_library, Report

lib = load_library('/path/to/librdlnative.so')
rpt = Report(lib, report_path='/path/to/report.rdl')

rpt.add_data('SalesData', [
    {'Product': 'PDF Library Pro',  'Revenue': '1200.00', 'Units': '3'},
    {'Product': 'Report Designer',   'Revenue': '250.00',  'Units': '1'},
    {'Product': 'Support (12 mo.)',  'Revenue': '500.00',  'Units': '1'},
])

data = rpt.export_to_memory('pdf')
add_data() sets SkipDatabaseSchemaValidation automatically. The dataset name must match a dataset defined in the .rdl file. All field values must be strings — convert numbers and dates before passing.

Export formats

Format stringOutputAvailable in
pdfPDFAll modes
csvCSV textAll modes
xlsxExcel workbookAll modes
xmlXML dataAll modes
rtfRich Text FormatAll modes
tifTIFF image (colour)All modes
htmlHTMLAll modes
xlsx_tableExcel (table style)ReportAot + native only
tifbTIFF image (black & white)ReportAot + native only
mhtMHTML web archiveReportAot + native only
Unrecognised format strings silently default to pdf. All methods are synchronous — wrap in asyncio.get_event_loop().run_in_executor(None, ...) if you need to call from async code without blocking.