Generate an execution report from Python¶
Use the report-aware Python API when an embedding needs the same versioned summary on Windows, Linux, or macOS. The Python API constructs and validates the report, but your application owns file publication.
Prepare the checkout¶
The checked-in example uses local test fixtures and makes no network requests. Run it from the repository root after installing the locked environment:
uv sync --frozen
The example imports only from vexcalibur.api. It handles SBOM, findings,
rendering, package-metadata, and report-parse failures separately.
1#!/usr/bin/env python3
2"""Generate matching VEX and execution-report bytes with the Python API."""
3
4import argparse
5import os
6from pathlib import Path
7
8from vexcalibur.api import (
9 GenerationExecutionReportParseError,
10 GenerationReportMetadataError,
11 LocalFindingsError,
12 SbomError,
13 VexRenderError,
14 generate_vex_from_local_findings_result,
15 parse_generation_execution_report,
16)
17
18
19def _write_new_file_exclusively(path: Path, content: bytes) -> None:
20 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
21 flags |= getattr(os, "O_BINARY", 0)
22 flags |= getattr(os, "O_CLOEXEC", 0)
23 flags |= getattr(os, "O_NOFOLLOW", 0)
24 descriptor = os.open(path, flags, 0o600)
25 try:
26 if os.name != "nt":
27 os.fchmod(descriptor, 0o600)
28 with os.fdopen(descriptor, "wb", closefd=False) as stream:
29 stream.write(content)
30 stream.flush()
31 os.fsync(stream.fileno())
32 finally:
33 os.close(descriptor)
34
35
36def main(output_directory: Path) -> None:
37 try:
38 result = generate_vex_from_local_findings_result(
39 input_file=Path("tests/fixtures/sbom/cyclonedx-json-simple.json"),
40 findings_file=Path("tests/fixtures/findings/all-analysis-states.json"),
41 )
42 except GenerationReportMetadataError as exc:
43 raise SystemExit(f"package metadata cannot identify the report: {exc}") from exc
44 except (SbomError, LocalFindingsError, VexRenderError) as exc:
45 raise SystemExit(f"generation failed: {exc}") from exc
46
47 try:
48 report = result.execution_report()
49 serialized_report = report.to_json()
50 if parse_generation_execution_report(serialized_report) != report:
51 raise SystemExit("execution report did not round-trip through its parser")
52 except GenerationReportMetadataError as exc:
53 raise SystemExit(f"package metadata cannot identify the report: {exc}") from exc
54 except GenerationExecutionReportParseError as exc:
55 raise SystemExit(f"generated execution report is invalid: {exc}") from exc
56 except ValueError as exc:
57 raise SystemExit(f"generation facts cannot produce a report: {exc}") from exc
58
59 if not output_directory.parent.is_dir():
60 raise SystemExit("output parent directory must already exist")
61 output_directory.mkdir(mode=0o700)
62 if os.name != "nt":
63 output_directory.chmod(0o700)
64 document_path = output_directory / "vex.json"
65 report_path = output_directory / "execution-report.json"
66 _write_new_file_exclusively(document_path, result.rendered_bytes)
67 _write_new_file_exclusively(
68 report_path,
69 serialized_report.encode("utf-8"),
70 )
71 print(f"wrote {document_path} and {report_path}")
72
73
74def _output_directory() -> Path:
75 parser = argparse.ArgumentParser()
76 parser.add_argument("output_directory", type=Path)
77 return parser.parse_args().output_directory
78
79
80if __name__ == "__main__":
81 main(_output_directory())
Generate and validate both files¶
Choose a new child of the repository root so the parent already exists. The example refuses to replace the directory or either output file.
On POSIX, the example creates the directory with mode 0700 and each file with
mode 0600. On Windows, Python inherits access control lists (ACLs) from the
parent; the example does not make an existing parent private. Run it only from
a directory whose ACL already restricts access to the intended user.
The two final-path writes are independent and are not atomic. A write or
fsync failure can leave a partial VEX file or execution report. Use the CLI
instead when a POSIX embedding needs coordinated publication.
Run this one-line command from Bash or PowerShell:
uv run --frozen python docs/examples/generate_execution_report.py vexcalibur-python-report
A successful run prints both paths:
wrote vexcalibur-python-report/vex.json and vexcalibur-python-report/execution-report.json
The example parses the serialized report through
parse_generation_execution_report() before either file is written. A parse
failure stops the run.
Use result.rendered_bytes for the VEX file and serialize the report from the
same GenerationResult. Don’t render again or calculate counts from the VEX
document; either step can make the report describe different bytes.
Handle publication in the embedding¶
The example uses exclusive creation and refuses to replace existing paths. On Windows, privacy depends on the parent ACL described above. On every platform, a failed direct write can leave a partial file, and a report failure can leave the VEX file behind.
Applications that need coordinated replacement on Linux or macOS should invoke
the CLI with --execution-report. The CLI publishes VEX first and the report
last under its destination locks. The supported Python facade does not expose
that filesystem transaction.
Catch GenerationReportMetadataError when installed package metadata cannot
identify the loaded code. Catch GenerationExecutionReportParseError when
validating report bytes received from another process. The Python API
reference lists the complete report types and
failure contracts; the execution report reference
defines the JSON fields and security boundary.