Source code for mirar.errors.error_stack

"""
Module for ErrorStack objects.

A :class:`~mirar.errors.error_stack.ErrorStack` object will contain a list of
:class:`~mirar.errors.error_report.ErrorReport` objects, and can correspond to
 multiple errors raised by the code.
"""

import logging
from typing import Optional

import numpy as np

from mirar.errors.error_report import ErrorReport
from mirar.paths import PACKAGE_NAME, __version__

logger = logging.getLogger(__name__)


[docs] class ErrorStack: """ Container class to hold multiple :class:`~mirar.errors.error_report.ErrorReport` objects """ def __init__(self, reports: Optional[list[ErrorReport]] = None): self.reports = [] self.noncritical_reports = [] self.failed_images = [] if reports is not None: for report in reports: self.add_report(report)
[docs] def add_report(self, report: ErrorReport): """ Adds a new ErrorReport :param report: ErrorReport to add :return: None """ if report.non_critical_bool: self.noncritical_reports.append(report) else: self.reports.append(report) all_failed_images = self.failed_images + report.contents self.failed_images = sorted(list(set(all_failed_images)))
def __add__(self, other): for report in other.reports: self.add_report(report) return self
[docs] def get_all_reports(self) -> list[ErrorReport]: """ Returns the full list of error reports (both critical and non-critical). :return: list of ErrorReports """ return self.reports + self.noncritical_reports
[docs] def summarise_error_stack(self, output_path=None, verbose: bool = True) -> str: """ Returns a string summary of all ErrorReports. :param output_path: Path to write summary in .txt format (optional) :param verbose: boolean whether to provide a verbose summary :return: String summary of errors """ is_known_error = [x.known_error_bool for x in self.reports] summary = ( f"Error report summarising {len(self.reports)} errors. \n" f"Code version: {PACKAGE_NAME}=={__version__} \n \n" f"{int(len(is_known_error) - np.sum(is_known_error))}/{len(is_known_error)}" f" errors were errors not raised by {PACKAGE_NAME}. \n" f"The remaining {int(np.sum(is_known_error))}/{len(is_known_error)} " f"errors were known errors raised by {PACKAGE_NAME}. \n" f"An additional {len(self.noncritical_reports)} non-critical " f"errors were raised. \n" ) all_reports = self.get_all_reports() if len(all_reports) > 0: summary += ( f"The following {len(self.failed_images)} images were affected " f"by at least one error during processing: \n " ) if verbose: summary += f"{self.failed_images} \n \n" summary += "Summarising each error: \n\n" logger.error(f"Found {len(self.reports)} errors caught by code.") error_lines = [err.get_error_message() for err in all_reports] for error_type in list(set(error_lines)): matching_errors = [ x for x in all_reports if x.get_error_message() == error_type ] img_paths = [] error_name = None for err in matching_errors: img_paths += err.contents if error_name is None: error_name = err.get_error_name() img_paths = list(set(img_paths)) line = error_type.split("\n")[0] summary += ( f"Found {error_lines.count(error_type)} counts of error " f"{error_name}, " f"affecting {len(img_paths)} images: \n{line}.\n \n" ) summary += " \n" if verbose: for report in all_reports: summary += str(report.generate_full_traceback()) else: msg = "No raised errors found in processing" logger.info(msg) summary += f"\n {msg}" if output_path is not None: logger.error(f"Saving tracebacks of caught errors to {output_path}") with open(output_path, "w", encoding="utf-8") as err_file: err_file.write(summary) return summary