Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions diff_diff/business_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,12 +353,21 @@ def full_report(self) -> str:
"""Return a structured multi-section markdown report."""
base = _render_full_report(self.to_dict())
if self._include_appendix:
appendix_text = None
try:
appendix = self._results.summary()
except Exception: # noqa: BLE001
appendix = None
if appendix:
base = base + "\n\n## Technical Appendix\n\n```\n" + str(appendix) + "\n```\n"
if appendix:
appendix_text = str(appendix)
except Exception as exc: # noqa: BLE001
appendix_error = type(exc).__name__ or "Exception"
base = (
base
+ "\n\n## Technical Appendix\n\n"
+ "Technical appendix unavailable: estimator summary rendering failed "
+ f"({appendix_error}).\n"
)
if appendix_text:
base = base + "\n\n## Technical Appendix\n\n```\n" + appendix_text + "\n```\n"
return base

def export_markdown(self) -> str:
Expand Down
34 changes: 34 additions & 0 deletions tests/test_business_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,40 @@ def test_include_appendix_false_omits(self, event_study_fit):
md = br.full_report()
assert "## Technical Appendix" not in md

def test_appendix_summary_failure_is_visible(self, event_study_fit):
fit, _ = event_study_fit
with patch.object(fit, "summary", side_effect=RuntimeError("internal path /tmp/private")):
br = BusinessReport(fit, auto_diagnostics=False, include_appendix=True)
md = br.full_report()

assert "## Technical Appendix" in md
assert "Technical appendix unavailable" in md
assert "RuntimeError" in md

def test_appendix_summary_failure_does_not_leak_exception_details(self, event_study_fit):
fit, _ = event_study_fit
with patch.object(fit, "summary", side_effect=RuntimeError("secret-token-123")):
br = BusinessReport(fit, auto_diagnostics=False, include_appendix=True)
md = br.full_report()

assert "secret-token-123" not in md
assert "Traceback" not in md

def test_appendix_stringification_failure_is_visible(self, event_study_fit):
class _BadSummary:
def __str__(self):
raise ValueError("sensitive-render-context")

fit, _ = event_study_fit
with patch.object(fit, "summary", return_value=_BadSummary()):
br = BusinessReport(fit, auto_diagnostics=False, include_appendix=True)
md = br.full_report()

assert "## Technical Appendix" in md
assert "Technical appendix unavailable" in md
assert "ValueError" in md
assert "sensitive-render-context" not in md


# ---------------------------------------------------------------------------
# BaconDecompositionResults
Expand Down
Loading