diff --git a/README.md b/README.md index 4efbefb..c3fede9 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,10 @@ options: Average Usage (RSS): {average_rss!S} Virtual Memory Peak Usage (VSZ): {peak_vsz!S} Virtual Memory Average Usage (VSZ): {average_vsz!S} Memory Peak Percentage: - {peak_pmem!N}% Memory Average Percentage: - {average_pmem!N}% CPU Peak Usage: {peak_pcpu!N}% - Average CPU Usage: {average_pcpu!N}% ) + {peak_pmem:.2f!N}% Memory Average Percentage: + {average_pmem:.2f!N}% CPU Peak Usage: + {peak_pcpu:.2f!N}% Average CPU Usage: + {average_pcpu:.2f!N}% ) --colors Use colors in duct output. (default: False) --clobber Replace log files if they already exist. (default: False) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 89ec63e..402aa76 100755 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -45,10 +45,10 @@ "Memory Average Usage (RSS): {average_rss!S}\n" "Virtual Memory Peak Usage (VSZ): {peak_vsz!S}\n" "Virtual Memory Average Usage (VSZ): {average_vsz!S}\n" - "Memory Peak Percentage: {peak_pmem!N}%\n" - "Memory Average Percentage: {average_pmem!N}%\n" - "CPU Peak Usage: {peak_pcpu!N}%\n" - "Average CPU Usage: {average_pcpu!N}%\n" + "Memory Peak Percentage: {peak_pmem:.2f!N}%\n" + "Memory Average Percentage: {average_pmem:.2f!N}%\n" + "CPU Peak Usage: {peak_pcpu:.2f!N}%\n" + "Average CPU Usage: {average_pcpu:.2f!N}%\n" ) @@ -648,13 +648,21 @@ def format_field(self, value: Any, format_spec: str) -> Any: if value is None: # TODO: could still use our formatter and make it red or smth like that return self.NONE + # if it is a composite :format!conversion, we need to split it + if "!" in format_spec and format_spec.index("!") > 1: + format_spec, conversion = format_spec.split("!", 1) + else: + conversion = None try: - return super().format_field(value, format_spec) - except ValueError: + value_ = super().format_field(value, format_spec) + except ValueError as exc: lgr.warning( - f"Value: {value} is invalid for format spec {format_spec}, falling back to `str`" + f"Falling back to `str` formatting for {value!r} due to exception: {exc}" ) return str(value) + if conversion: + return self.convert_field(value_, conversion) + return value_ @dataclass diff --git a/test/test_formatter.py b/test/test_formatter.py index 878c56d..642aad5 100644 --- a/test/test_formatter.py +++ b/test/test_formatter.py @@ -297,20 +297,51 @@ def test_summary_formatter_N_e2e_colors() -> None: @mock.patch("con_duct.__main__.LogPaths") @mock.patch("con_duct.__main__.subprocess.Popen") -def test_execution_summary_formatted_wall_clock_time_invalid( - mock_popen: mock.MagicMock, mock_log_paths: mock.MagicMock +@pytest.mark.parametrize("colors", [True, False]) +def test_execution_summary_formatted_wall_clock_time_nowvalid( + mock_popen: mock.MagicMock, mock_log_paths: mock.MagicMock, colors: bool ) -> None: mock_log_paths.prefix = "mock_prefix" - wall_clock_format_string = "Invalid rounding of string: {wall_clock_time:.3f!X}" - report = Report("_cmd", [], mock_log_paths, wall_clock_format_string, clobber=False) - # It should not crash and it would render even where no wallclock time yet + wall_clock_format_string = "Rendering: {wall_clock_time:.3f!X}" + report = Report( + "_cmd", + [], + mock_log_paths, + wall_clock_format_string, + clobber=False, + colors=colors, + ) report.process = mock_popen report.process.returncode = 0 report.start_time = 1727221840.0486171 report.end_time = report.start_time + 1.111111111 + if colors: + GREEN, STOP = GREEN_START, SummaryFormatter.RESET_SEQ + else: + GREEN, STOP = "", "" + # Assert ValueError not raised - assert ( - "Invalid rounding of string: 1.1111111640930176" - == report.execution_summary_formatted + assert f"Rendering: {GREEN}1.111{STOP}" == report.execution_summary_formatted + + # It should not crash and it would render even where no wallclock time yet + report = Report( + "_cmd", + [], + mock_log_paths, + wall_clock_format_string, + clobber=False, + colors=colors, + ) + assert f"Rendering: {GREEN}nan{STOP}" == report.execution_summary_formatted + + # or if we really provide bad formatting, e.g. the opposite order of conversion and formatting + report = Report( + "_cmd", + [], + mock_log_paths, + "Rendering: {wall_clock_time!X:.3f}", + clobber=False, + colors=colors, ) + assert f"Rendering: {GREEN}nan{STOP}" == report.execution_summary_formatted