From f77ffbd3033d122a375c3be63624ea80507d0541 Mon Sep 17 00:00:00 2001 From: Benjamin Klimczak Date: Mon, 7 Nov 2022 12:04:06 +0000 Subject: MLIA-275 Remove support for CSV output Change-Id: I212c9cad5f2ac28b75b1d9e2fb8f1f8b444b8397 --- README.md | 6 +- src/mlia/cli/options.py | 3 +- src/mlia/core/reporting.py | 126 ++------------------------------- src/mlia/core/typing.py | 2 +- tests/test_cli_options.py | 3 - tests/test_core_reporting.py | 79 +-------------------- tests/test_devices_ethosu_reporters.py | 87 +---------------------- 7 files changed, 14 insertions(+), 292 deletions(-) diff --git a/README.md b/README.md index 20f80fe..ec4080d 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ mlia operators --target-profile ethos-u55-256 ~/models/mobilenet_v1_1.0_224_quan * --output: Name of the file where the report will be saved. The report format is automatically detected based on the file extension. Supported formats are: - csv, json. + json. ##### Optional arguments @@ -260,7 +260,7 @@ mlia performance ~/models/mobilenet_v1_1.0_224_quant.tflite \ * --output: Name of the file where the report will be saved. The report format is automatically detected based on the file extension. Supported formats are: - csv, json. + json. ##### Debug options @@ -388,7 +388,7 @@ mlia all_tests --output ./report.json ~/models/ds_cnn_l.h5 * --output: Name of the file where the report will be saved. The report format is automatically detected based on the file extension. Supported formats are: - csv, json. + json. ##### Debug options diff --git a/src/mlia/cli/options.py b/src/mlia/cli/options.py index b28fa8f..f6dcf75 100644 --- a/src/mlia/cli/options.py +++ b/src/mlia/cli/options.py @@ -11,6 +11,7 @@ from typing import Callable from mlia.cli.config import get_available_backends from mlia.cli.config import get_default_backends from mlia.cli.config import is_corstone_backend +from mlia.core.reporting import OUTPUT_FORMATS from mlia.utils.filesystem import get_supported_profile_names from mlia.utils.types import is_number @@ -77,7 +78,7 @@ def add_tflite_model_options(parser: argparse.ArgumentParser) -> None: def add_output_options(parser: argparse.ArgumentParser) -> None: """Add output specific options.""" - valid_extensions = ["csv", "json"] + valid_extensions = OUTPUT_FORMATS def check_extension(filename: str) -> str: """Check extension of the provided file.""" diff --git a/src/mlia/core/reporting.py b/src/mlia/core/reporting.py index 0c8fabc..b96a6b5 100644 --- a/src/mlia/core/reporting.py +++ b/src/mlia/core/reporting.py @@ -3,7 +3,6 @@ """Reporting module.""" from __future__ import annotations -import csv import json import logging from abc import ABC @@ -36,6 +35,8 @@ from mlia.utils.types import is_list_of logger = logging.getLogger(__name__) +OUTPUT_FORMATS = ("json",) + class Report(ABC): """Abstract class for the report.""" @@ -44,10 +45,6 @@ class Report(ABC): def to_json(self, **kwargs: Any) -> Any: """Convert to json serializible format.""" - @abstractmethod - def to_csv(self, **kwargs: Any) -> list[Any]: - """Convert to csv serializible format.""" - @abstractmethod def to_plain_text(self, **kwargs: Any) -> str: """Convert to human readable format.""" @@ -134,10 +131,6 @@ class Cell: val = self._get_value() return self._apply_style(val) - def to_csv(self) -> Any: - """Cell definition for csv.""" - return self.value - def to_json(self) -> Any: """Cell definition for json.""" return self.value @@ -168,10 +161,6 @@ class CountAwareCell(Cell): super().__init__(value, Format(str_fmt=format_value)) - def to_csv(self) -> Any: - """Cell definition for csv.""" - return {"value": self.value, "unit": self.unit} - def to_json(self) -> Any: """Cell definition for json.""" return {"value": self.value, "unit": self.unit} @@ -239,41 +228,6 @@ class NestedReport(Report): self.alias = alias self.items = items - def to_csv(self, **kwargs: Any) -> list[Any]: - """Convert to csv serializible format.""" - result = {} - - def collect_item_values( - item: ReportItem, - _parent: ReportItem | None, - _prev: ReportItem | None, - _level: int, - ) -> None: - """Collect item values into a dictionary..""" - if item.value is None: - return - - if not isinstance(item.value, Cell): - result[item.alias] = item.raw_value - return - - csv_value = item.value.to_csv() - if isinstance(csv_value, dict): - csv_value = { - f"{item.alias}_{key}": value for key, value in csv_value.items() - } - else: - csv_value = {item.alias: csv_value} - - result.update(csv_value) - - self._traverse(self.items, collect_item_values) - - # make list out of the result dictionary - # first element - keys of the dictionary as headers - # second element - list of the dictionary values - return list(zip(*result.items())) - def to_json(self, **kwargs: Any) -> Any: """Convert to json serializible format.""" per_parent: dict[ReportItem | None, dict] = defaultdict(dict) @@ -474,32 +428,6 @@ class Table(Report): return title + formatted_table + footer - def to_csv(self, **kwargs: Any) -> list[Any]: - """Convert table to csv format.""" - headers = [[c.header for c in self.columns if c.supports_format("csv")]] - - def item_data(item: Any) -> Any: - if isinstance(item, Cell): - return item.value - - if isinstance(item, Table): - return ";".join( - str(item_data(cell)) for row in item.rows for cell in row - ) - - return item - - rows = [ - [ - item_data(item) - for (item, col) in zip(row, self.columns) - if col.supports_format("csv") - ] - for row in self.rows - ] - - return headers + rows - class SingleRow(Table): """Table with a single row.""" @@ -541,46 +469,6 @@ class CompoundReport(Report): return result - def to_csv(self, **kwargs: Any) -> list[Any]: - """Convert to csv serializible format. - - CSV format does support only one table. In order to be able to export - multiply tables they should be merged before that. This method tries to - do next: - - - if all tables have the same length then just concatenate them - - if one table has many rows and other just one (two with headers), then - for each row in table with many rows duplicate values from other tables - """ - csv_data = [item.to_csv() for item in self.reports] - lengths = [len(csv_item_data) for csv_item_data in csv_data] - - same_length = len(set(lengths)) == 1 - if same_length: - # all lists are of the same length, merge them into one - return [[cell for item in row for cell in item] for row in zip(*csv_data)] - - main_obj_indexes = [i for i, item in enumerate(csv_data) if len(item) > 2] - one_main_obj = len(main_obj_indexes) == 1 - - reference_obj_indexes = [i for i, item in enumerate(csv_data) if len(item) == 2] - other_only_ref_objs = len(reference_obj_indexes) == len(csv_data) - 1 - - if one_main_obj and other_only_ref_objs: - main_obj = csv_data[main_obj_indexes[0]] - return [ - item - + [ - ref_item - for ref_table_index in reference_obj_indexes - for ref_item in csv_data[ref_table_index][0 if i == 0 else 1] - ] - for i, item in enumerate(main_obj) - ] - - # write tables one after another if there is no other options - return [row for item in csv_data for row in item] - def to_plain_text(self, **kwargs: Any) -> str: """Convert to human readable format.""" return "\n".join(item.to_plain_text(**kwargs) for item in self.reports) @@ -624,12 +512,6 @@ def text_reporter(report: Report, output: FileLike, **kwargs: Any) -> None: print(report.to_plain_text(**kwargs), file=output) -def csv_reporter(report: Report, output: FileLike, **kwargs: Any) -> None: - """Produce report in csv format.""" - csv_writer = csv.writer(output) - csv_writer.writerows(report.to_csv(**kwargs)) - - def produce_report( data: Any, formatter: Callable[[Any], Report], @@ -639,7 +521,7 @@ def produce_report( ) -> None: """Produce report based on provided data.""" # check if provided format value is supported - formats = {"json": json_reporter, "plain_text": text_reporter, "csv": csv_reporter} + formats = {"json": json_reporter, "plain_text": text_reporter} if fmt not in formats: raise Exception(f"Unknown format {fmt}") @@ -764,7 +646,7 @@ def resolve_output_format(output: PathOrFileLike | None) -> OutputFormat: if isinstance(output, (str, Path)): format_from_filename = Path(output).suffix.lstrip(".") - if format_from_filename in ["json", "csv"]: + if format_from_filename in OUTPUT_FORMATS: return cast(OutputFormat, format_from_filename) return "plain_text" diff --git a/src/mlia/core/typing.py b/src/mlia/core/typing.py index bda995c..8244ff5 100644 --- a/src/mlia/core/typing.py +++ b/src/mlia/core/typing.py @@ -9,4 +9,4 @@ from typing import Union FileLike = TextIO PathOrFileLike = Union[str, Path, FileLike] -OutputFormat = Literal["plain_text", "csv", "json"] +OutputFormat = Literal["plain_text", "json"] diff --git a/tests/test_cli_options.py b/tests/test_cli_options.py index f898146..d75f7c0 100644 --- a/tests/test_cli_options.py +++ b/tests/test_cli_options.py @@ -147,9 +147,6 @@ def test_get_target_opts(args: dict | None, expected_opts: list[str]) -> None: [["--output", "report.json"], "report.json"], [["--output", "REPORT.JSON"], "REPORT.JSON"], [["--output", "some_folder/report.json"], "some_folder/report.json"], - [["--output", "report.csv"], "report.csv"], - [["--output", "REPORT.CSV"], "REPORT.CSV"], - [["--output", "some_folder/report.csv"], "some_folder/report.csv"], ], ) def test_output_options(output_parameters: list[str], expected_path: str) -> None: diff --git a/tests/test_core_reporting.py b/tests/test_core_reporting.py index da7998c..feff5cc 100644 --- a/tests/test_core_reporting.py +++ b/tests/test_core_reporting.py @@ -95,9 +95,6 @@ def test_table_representation(with_notes: bool, expected_text_report: str) -> No ) table = sample_table(with_notes) - csv_repr = table.to_csv() - assert csv_repr == [["Header 2", "Header 3"], [2, 3], [5, 123123]] - json_repr = table.to_json() assert json_repr == { "sample_table": [ @@ -110,48 +107,8 @@ def test_table_representation(with_notes: bool, expected_text_report: str) -> No assert text_report == expected_text_report -def test_csv_nested_table_representation() -> None: - """Test representation of the nested tables in csv format.""" - - def sample_table(num_of_cols: int) -> Table: - columns = [ - Column("Header 1", alias="header1"), - Column("Header 2", alias="header2"), - ] - - rows = [ - ( - 1, - Table( - columns=[ - Column(f"Nested column {i+1}") for i in range(num_of_cols) - ], - rows=[[f"value{i+1}" for i in range(num_of_cols)]], - name="Nested table", - ), - ) - ] - - return Table(columns, rows, name="Sample table", alias="sample_table") - - assert sample_table(num_of_cols=2).to_csv() == [ - ["Header 1", "Header 2"], - [1, "value1;value2"], - ] - - assert sample_table(num_of_cols=1).to_csv() == [ - ["Header 1", "Header 2"], - [1, "value1"], - ] - - assert sample_table(num_of_cols=0).to_csv() == [ - ["Header 1", "Header 2"], - [1, ""], - ] - - @pytest.mark.parametrize( - "report, expected_plain_text, expected_json_data, expected_csv_data", + "report, expected_plain_text, expected_json_data", [ ( NestedReport( @@ -168,10 +125,6 @@ Sample report: { "sample_report": {"item": "item_value"}, }, - [ - ("item",), - ("item_value",), - ], ), ( NestedReport( @@ -196,10 +149,6 @@ Sample report: "item": {"nested_item": "nested_item_value"}, }, }, - [ - ("item", "nested_item"), - ("item_value", "nested_item_value"), - ], ), ( NestedReport( @@ -224,10 +173,6 @@ Sample report: "item": {"nested_item": {"unit": "bytes", "value": 10}}, }, }, - [ - ("item", "nested_item_value", "nested_item_unit"), - ("item_value", 10, "bytes"), - ], ), ( NestedReport( @@ -260,10 +205,6 @@ Sample report: "item": {"nested_item": 10}, }, }, - [ - ("item", "nested_item"), - ("item_value", 10), - ], ), ( NestedReport( @@ -296,10 +237,6 @@ Sample report: "item": {"nested_item": 10}, }, }, - [ - ("item", "nested_item"), - ("item_value", 10), - ], ), ( NestedReport( @@ -326,10 +263,6 @@ Sample report: "item": {"nested_item": 10}, }, }, - [ - ("item", "nested_item"), - ("item_value", 10), - ], ), ( NestedReport( @@ -358,10 +291,6 @@ Sample report: "item": {"nested_item": 10}, }, }, - [ - ("item", "nested_item"), - ("item_value", 10), - ], ), ], ) @@ -369,7 +298,6 @@ def test_nested_report_representation( report: NestedReport, expected_plain_text: str, expected_json_data: dict, - expected_csv_data: list, ) -> None: """Test representation of the NestedReport.""" plain_text = report.to_plain_text() @@ -378,9 +306,6 @@ def test_nested_report_representation( json_data = report.to_json() assert json_data == expected_json_data - csv_data = report.to_csv() - assert csv_data == expected_csv_data - def test_single_row_representation() -> None: """Test representation of the SingleRow.""" @@ -398,7 +323,6 @@ Single row example: column1 value1 """.strip() assert single_row.to_plain_text() == expected_text - assert single_row.to_csv() == [["column1"], ["value1"]] assert single_row.to_json() == {"simple_row_example": [{"column1": "value1"}]} with pytest.raises(Exception, match="Table should have only one row"): @@ -423,7 +347,6 @@ Single row example: ["", "plain_text"], ["some_file", "plain_text"], ["some_format.some_ext", "plain_text"], - ["output.csv", "csv"], ["output.json", "json"], ], ) diff --git a/tests/test_devices_ethosu_reporters.py b/tests/test_devices_ethosu_reporters.py index f8a7d86..f04270c 100644 --- a/tests/test_devices_ethosu_reporters.py +++ b/tests/test_devices_ethosu_reporters.py @@ -67,11 +67,6 @@ from mlia.utils.console import remove_ascii_codes sys.stdout, doesnt_raise(), ], - [ - "csv", - sys.stdout, - doesnt_raise(), - ], [ "plain_text", "report.txt", @@ -82,17 +77,12 @@ from mlia.utils.console import remove_ascii_codes "report.json", doesnt_raise(), ], - [ - "csv", - "report.csv", - doesnt_raise(), - ], ], ) def test_report( data: Any, formatters: list[Callable], - fmt: Literal["plain_text", "json", "csv"], + fmt: Literal["plain_text", "json"], output: Any, expected_error: Any, tmp_path: Path, @@ -111,7 +101,7 @@ def test_report( @pytest.mark.parametrize( - "ops, expected_plain_text, expected_json_dict, expected_csv_list", + "ops, expected_plain_text, expected_json_dict", [ ( [ @@ -187,17 +177,6 @@ Operators: }, ] }, - [ - ["Operator name", "Operator type", "Placement", "Notes"], - ["npu_supported", "test_type", "NPU", ""], - ["cpu_only", "test_type", "CPU", "CPU only operator"], - [ - "npu_unsupported", - "test_type", - "CPU", - "Not supported operator;Reason why operator is not supported", - ], - ], ), ], ) @@ -205,7 +184,6 @@ def test_report_operators( ops: list[Operator], expected_plain_text: str, expected_json_dict: dict, - expected_csv_list: list, monkeypatch: pytest.MonkeyPatch, ) -> None: """Test report_operatos formatter.""" @@ -221,12 +199,9 @@ def test_report_operators( json_dict = report.to_json() assert json_dict == expected_json_dict - csv_list = report.to_csv() - assert csv_list == expected_csv_list - @pytest.mark.parametrize( - "device, expected_plain_text, expected_json_dict, expected_csv_list", + "device, expected_plain_text, expected_json_dict", [ [ EthosUConfiguration("ethos-u55-256"), @@ -322,58 +297,6 @@ def test_report_operators( }, } }, - [ - ( - "target", - "mac", - "memory_mode", - "const_mem_area", - "arena_mem_area", - "cache_mem_area", - "arena_cache_size_value", - "arena_cache_size_unit", - "system_config", - "accelerator_clock_value", - "accelerator_clock_unit", - "axi0_port", - "axi1_port", - "clock_scales", - "burst_length_value", - "burst_length_unit", - "read_latency_value", - "read_latency_unit", - "write_latency_value", - "write_latency_unit", - "permanent_storage_mem_area", - "feature_map_storage_mem_area", - "fast_storage_mem_area", - ), - ( - "ethos-u55", - 256, - "Shared_Sram", - "Axi1", - "Axi0", - "Axi0", - 2096768, - "bytes", - "Ethos_U55_High_End_Embedded", - 500000000.0, - "Hz", - "Sram", - "OffChipFlash", - 0.125, - 128, - "bytes", - 64, - "cycles", - 64, - "cycles", - "OffChipFlash", - "Sram", - "Sram", - ), - ], ], ], ) @@ -381,7 +304,6 @@ def test_report_device_details( device: EthosUConfiguration, expected_plain_text: str, expected_json_dict: dict, - expected_csv_list: list, ) -> None: """Test report_operatos formatter.""" report = report_device_details(device) @@ -393,9 +315,6 @@ def test_report_device_details( json_dict = report.to_json() assert json_dict == expected_json_dict - csv_list = report.to_csv() - assert csv_list == expected_csv_list - def test_get_reporter(tmp_path: Path) -> None: """Test reporter functionality.""" -- cgit v1.2.1