SIENTIAPDE-1255: Add thread-safety documentation and I/O notes to Reports class, improve save_all_sections documentation and error handling.

This commit is contained in:
Bruno Domingues
2025-10-20 17:38:44 -03:00
parent 2292ae57cc
commit 0dbe179367

View File

@@ -55,6 +55,22 @@ def inject_content(main_html, section_id, content):
class Reports:
"""
Report generator using Evidently library.
Thread-safety: This class is NOT thread-safe. Multiple threads should not
call add_*_section() methods on the same instance simultaneously as they
modify shared state (self.metrics, self.sections, self.options).
For multi-threaded environments:
- Create separate Reports instances per thread
- Or synchronize access using locks
- After generation, instances are safe for read-only operations
I/O Note: This class relies on Evidently's report.save_html() method
for file operations. Ensure Evidently properly manages file handles.
"""
def __init__(
self, reference_data: Any, current_data: Any, base_path: str | None = None
) -> None:
@@ -95,6 +111,7 @@ class Reports:
report.run(reference_data=self.ref_data, current_data=self.cur_data)
self.sections['data_quality'] = report.as_dict()
if self.base_path:
# Note: Relies on Evidently's save_html() to properly manage file I/O
report.save_html(os.path.join(self.base_path, 'data_quality.html'))
def add_data_drift_section(self, columns: list[str] | None = None, run: bool = True) -> None:
@@ -111,6 +128,7 @@ class Reports:
report.run(reference_data=self.ref_data, current_data=self.cur_data)
self.sections['data_drift'] = report.as_dict()
if self.base_path:
# Note: Relies on Evidently's save_html() to properly manage file I/O
report.save_html(os.path.join(self.base_path, 'data_drift.html'))
def add_regression_section(self, run: bool = True) -> None:
@@ -135,6 +153,7 @@ class Reports:
report.run(reference_data=self.ref_data, current_data=self.cur_data)
self.sections['regression'] = report.as_dict()
if self.base_path:
# Note: Relies on Evidently's save_html() to properly manage file I/O
report.save_html(os.path.join(self.base_path, 'regression.html'))
def set_color_options(
@@ -201,10 +220,23 @@ class Reports:
Args:
report_path: The path to save the report HTML file.
Raises:
ValueError: If base_path is not set
OSError: If directory creation or file writing fails
Note:
This method uses context manager (with open) to ensure file is properly closed.
Creates parent directories if they don't exist.
"""
if not self.base_path:
raise ValueError('base_path is required to save all sections HTML')
# Ensure output directory exists
output_dir = os.path.dirname(report_path)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
# Load main HTML template
main_html_path = os.path.join(self.base_path, 'header.html')
main_html = load_html_from_file(main_html_path)
@@ -222,5 +254,6 @@ class Reports:
main_html = inject_content(main_html, 'regression', regression_content)
# Save the final HTML to a new file (report.html)
# Context manager ensures file is properly closed even if an error occurs
with open(report_path, 'w', encoding='utf-8') as report_file:
report_file.write(main_html)