From ed3a0c365e75e4364eeb284d68c5df0466773713 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 17:22:14 -0500 Subject: [PATCH 01/23] Remove units for machine readability https://github.com/con/duct/issues/116 Note: I put KiB into the field names of the execution summary, because `ps` uses KiB, this seemed cleaner than modifying the values into bytes. ``` $ duct -p ZZZZ_ --r-i 0.1 --s-i 0.01 --clobber sleep 1 duct is executing sleep 1... Log files will be written to ZZZZ_ Exit Code: 0 Command: sleep 1 Log files location: ZZZZ_ Wall Clock Time: 1.001 sec Memory Peak Usage (RSS) KiB: 1600 Memory Average Usage (RSS) KiB: 1561.905 Virtual Memory Peak Usage (VSZ) KiB: 221584 Virtual Memory Average Usage (VSZ) KiB: 216308.190 Memory Peak Percentage: 0.0% Memory Average Percentage: 0.000 CPU Peak Usage: 0.0 Average CPU Usage: 0.000 Samples Collected: 42 Reports Written: 11 $ cat ZZZZ_info.json|jq { "command": "sleep 1", "system": { "uid": "austin", "memory_total": 33336778752, "cpu_total": 20 }, "env": {}, "gpu": null, "duct_version": "0.1.1", "execution_summary": { "exit_code": 0, "command": "sleep 1", "logs_prefix": "ZZZZ_", "wall_clock_time": "1.001", "peak_rss_kib": "1760", "average_rss_kib": "1760.000", "peak_vsz_kib": "221584", "average_vsz_kib": "221584.000", "peak_pmem": "0.0%", "average_pmem": "0.000", "peak_pcpu": "0.0", "average_pcpu": "0.000", "num_samples": 41, "num_reports": 10 } } ``` --- src/con_duct/__main__.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index b0931b9a..13b4e7b7 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -28,10 +28,10 @@ "Command: {command}\n" "Log files location: {logs_prefix}\n" "Wall Clock Time: {wall_clock_time} sec\n" - "Memory Peak Usage (RSS): {peak_rss}\n" - "Memory Average Usage (RSS): {average_rss}\n" - "Virtual Memory Peak Usage (VSZ): {peak_vsz}\n" - "Virtual Memory Average Usage (VSZ): {average_vsz}\n" + "Memory Peak Usage (RSS) KiB: {peak_rss_kib}\n" + "Memory Average Usage (RSS) KiB: {average_rss_kib}\n" + "Virtual Memory Peak Usage (VSZ) KiB: {peak_vsz_kib}\n" + "Virtual Memory Average Usage (VSZ) KiB: {average_vsz_kib}\n" "Memory Peak Percentage: {peak_pmem}\n" "Memory Average Percentage: {average_pmem}\n" "CPU Peak Usage: {peak_pcpu}\n" @@ -345,24 +345,24 @@ def execution_summary(self) -> dict[str, Any]: "exit_code": self.process.returncode, "command": self.command, "logs_prefix": self.log_paths.prefix, - "wall_clock_time": f"{self.elapsed_time:.3f} sec", - "peak_rss": ( - f"{self.max_values.total_rss} KiB" + "wall_clock_time": f"{self.elapsed_time:.3f}", + "peak_rss_kib": ( + f"{self.max_values.total_rss}" if self.max_values.stats else "unknown" ), - "average_rss": ( - f"{self.averages.rss:.3f} KiB" + "average_rss_kib": ( + f"{self.averages.rss:.3f}" if self.averages.num_samples >= 1 else "unknown" ), - "peak_vsz": ( - f"{self.max_values.total_vsz} KiB" + "peak_vsz_kib": ( + f"{self.max_values.total_vsz}" if self.max_values.stats else "unknown" ), - "average_vsz": ( - f"{self.averages.vsz:.3f} KiB" + "average_vsz_kib": ( + f"{self.averages.vsz:.3f}" if self.averages.num_samples >= 1 else "unknown" ), @@ -372,17 +372,17 @@ def execution_summary(self) -> dict[str, Any]: else "unknown%" ), "average_pmem": ( - f"{self.averages.pmem:.3f}%" + f"{self.averages.pmem:.3f}" if self.averages.num_samples >= 1 else "unknown%" ), "peak_pcpu": ( - f"{self.max_values.total_pcpu}%" + f"{self.max_values.total_pcpu}" if self.max_values.stats else "unknown%" ), "average_pcpu": ( - f"{self.averages.pcpu:.3f}%" + f"{self.averages.pcpu:.3f}" if self.averages.num_samples >= 1 else "unknown%" ), From ba49b5cfa15d4216fd6389c75670e878ab666c2f Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 17:38:55 -0500 Subject: [PATCH 02/23] [DATALAD RUNCMD] Autoupdate reademe === Do not change lines below === { "chain": [], "cmd": "./.update-readme-help.py", "exit": 0, "extra_inputs": [], "inputs": [], "outputs": [], "pwd": "." } ^^^ Do not change lines above ^^^ --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0ea2bc0c..7b858126 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,14 @@ options: following execution. (default: Exit Code: {exit_code} Command: {command} Log files location: {logs_prefix} Wall Clock Time: {wall_clock_time} sec Memory Peak - Usage (RSS): {peak_rss} Memory Average Usage (RSS): - {average_rss} Virtual Memory Peak Usage (VSZ): - {peak_vsz} Virtual Memory Average Usage (VSZ): - {average_vsz} Memory Peak Percentage: {peak_pmem} - Memory Average Percentage: {average_pmem} CPU Peak - Usage: {peak_pcpu} Average CPU Usage: {average_pcpu} - Samples Collected: {num_samples} Reports Written: - {num_reports} ) + Usage (RSS) KiB: {peak_rss_kib} Memory Average Usage + (RSS) KiB: {average_rss_kib} Virtual Memory Peak Usage + (VSZ) KiB: {peak_vsz_kib} Virtual Memory Average Usage + (VSZ) KiB: {average_vsz_kib} Memory Peak Percentage: + {peak_pmem} Memory Average Percentage: {average_pmem} + CPU Peak Usage: {peak_pcpu} Average CPU Usage: + {average_pcpu} Samples Collected: {num_samples} + Reports Written: {num_reports} ) --clobber Replace log files if they already exist. (default: False) -q, --quiet Suppress duct output. (default: False) From 4349eca688926fc733d54beaf640a7eaa2596392 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 18:03:13 -0500 Subject: [PATCH 03/23] Switch to bytes $ duct -p ZZZZ_ --r-i 0.1 --s-i 0.01 --clobber sleep 1 duct is executing sleep 1... Log files will be written to ZZZZ_ Exit Code: 0 Command: sleep 1 Log files location: ZZZZ_ Wall Clock Time: 1.006 sec Memory Peak Usage (RSS): 1802240 Memory Average Usage (RSS): 1758282 Virtual Memory Peak Usage (VSZ): 226902016 Virtual Memory Average Usage (VSZ): 221367820 Memory Peak Percentage: 0.0 Memory Average Percentage: 0.000 CPU Peak Usage: 0.0 Average CPU Usage: 0.000 Samples Collected: 41 Reports Written: 11 $ cat ZZZZ_info.json| jq { "command": "sleep 1", "system": { "uid": "austin", "memory_total": 33336778752, "cpu_total": 20 }, "env": {}, "gpu": null, "duct_version": "0.1.1", "execution_summary": { "exit_code": 0, "command": "sleep 1", "logs_prefix": "ZZZZ_", "wall_clock_time": "1.006", "peak_rss": "1802240", "average_rss": "1758282", "peak_vsz": "226902016", "average_vsz": "221367820", "peak_pmem": "0.0", "average_pmem": "0.000", "peak_pcpu": "0.0", "average_pcpu": "0.000", "num_samples": 41, "num_reports": 11 } } --- src/con_duct/__main__.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 13b4e7b7..13d5d9e6 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -28,10 +28,10 @@ "Command: {command}\n" "Log files location: {logs_prefix}\n" "Wall Clock Time: {wall_clock_time} sec\n" - "Memory Peak Usage (RSS) KiB: {peak_rss_kib}\n" - "Memory Average Usage (RSS) KiB: {average_rss_kib}\n" - "Virtual Memory Peak Usage (VSZ) KiB: {peak_vsz_kib}\n" - "Virtual Memory Average Usage (VSZ) KiB: {average_vsz_kib}\n" + "Memory Peak Usage (RSS): {peak_rss}\n" + "Memory Average Usage (RSS): {average_rss}\n" + "Virtual Memory Peak Usage (VSZ): {peak_vsz}\n" + "Virtual Memory Average Usage (VSZ): {average_vsz}\n" "Memory Peak Percentage: {peak_pmem}\n" "Memory Average Percentage: {average_pmem}\n" "CPU Peak Usage: {peak_pcpu}\n" @@ -319,8 +319,8 @@ def collect_sample(self) -> Sample: ProcessStats( pcpu=float(pcpu), pmem=float(pmem), - rss=int(rss), - vsz=int(vsz), + rss=int(rss) * 1024, + vsz=int(vsz) * 1024, timestamp=datetime.now().astimezone().isoformat(), ), ) @@ -346,45 +346,45 @@ def execution_summary(self) -> dict[str, Any]: "command": self.command, "logs_prefix": self.log_paths.prefix, "wall_clock_time": f"{self.elapsed_time:.3f}", - "peak_rss_kib": ( + "peak_rss": ( f"{self.max_values.total_rss}" if self.max_values.stats else "unknown" ), - "average_rss_kib": ( - f"{self.averages.rss:.3f}" + "average_rss": ( + f"{int(self.averages.rss)}" if self.averages.num_samples >= 1 else "unknown" ), - "peak_vsz_kib": ( + "peak_vsz": ( f"{self.max_values.total_vsz}" if self.max_values.stats else "unknown" ), - "average_vsz_kib": ( - f"{self.averages.vsz:.3f}" + "average_vsz": ( + f"{int(self.averages.vsz)}" if self.averages.num_samples >= 1 else "unknown" ), "peak_pmem": ( - f"{self.max_values.total_pmem}%" + f"{self.max_values.total_pmem}" if self.max_values.stats - else "unknown%" + else "unknown" ), "average_pmem": ( f"{self.averages.pmem:.3f}" if self.averages.num_samples >= 1 - else "unknown%" + else "unknown" ), "peak_pcpu": ( f"{self.max_values.total_pcpu}" if self.max_values.stats - else "unknown%" + else "unknown" ), "average_pcpu": ( f"{self.averages.pcpu:.3f}" if self.averages.num_samples >= 1 - else "unknown%" + else "unknown" ), "num_samples": self.averages.num_samples, "num_reports": self.number, From 702ee4703af733e920c9cc11390f1308ed2560b0 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 18:04:25 -0500 Subject: [PATCH 04/23] [DATALAD RUNCMD] Autoupdate readme === Do not change lines below === { "chain": [], "cmd": "./.update-readme-help.py", "exit": 0, "extra_inputs": [], "inputs": [], "outputs": [], "pwd": "." } ^^^ Do not change lines above ^^^ --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7b858126..0ea2bc0c 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,14 @@ options: following execution. (default: Exit Code: {exit_code} Command: {command} Log files location: {logs_prefix} Wall Clock Time: {wall_clock_time} sec Memory Peak - Usage (RSS) KiB: {peak_rss_kib} Memory Average Usage - (RSS) KiB: {average_rss_kib} Virtual Memory Peak Usage - (VSZ) KiB: {peak_vsz_kib} Virtual Memory Average Usage - (VSZ) KiB: {average_vsz_kib} Memory Peak Percentage: - {peak_pmem} Memory Average Percentage: {average_pmem} - CPU Peak Usage: {peak_pcpu} Average CPU Usage: - {average_pcpu} Samples Collected: {num_samples} - Reports Written: {num_reports} ) + Usage (RSS): {peak_rss} Memory Average Usage (RSS): + {average_rss} Virtual Memory Peak Usage (VSZ): + {peak_vsz} Virtual Memory Average Usage (VSZ): + {average_vsz} Memory Peak Percentage: {peak_pmem} + Memory Average Percentage: {average_pmem} CPU Peak + Usage: {peak_pcpu} Average CPU Usage: {average_pcpu} + Samples Collected: {num_samples} Reports Written: + {num_reports} ) --clobber Replace log files if they already exist. (default: False) -q, --quiet Suppress duct output. (default: False) From 15f33a99d74f975edc52725051855eeeb6716a98 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 19:18:33 -0500 Subject: [PATCH 05/23] fixup comment --- src/con_duct/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 13d5d9e6..53038e40 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -83,8 +83,8 @@ class SystemInfo: class ProcessStats: pcpu: float # %CPU pmem: float # %MEM - rss: int # Memory Resident Set Size in KiB - vsz: int # Virtual Memory size in KiB + rss: int # Memory Resident Set Size in Bytes + vsz: int # Virtual Memory size in Bytes timestamp: str def max(self, other: ProcessStats) -> ProcessStats: From a015715bcbd49197a9bcc358aaa3cede0b0eff15 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 19:33:16 -0500 Subject: [PATCH 06/23] Add ProcessStats validation --- src/con_duct/__main__.py | 15 +++++++++++++++ test/test_report.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 53038e40..6137942a 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -96,6 +96,21 @@ def max(self, other: ProcessStats) -> ProcessStats: timestamp=max(self.timestamp, other.timestamp), ) + def __post_init__(self) -> None: + self._validate() + + def _validate(self) -> None: + if not isinstance(self.pcpu, (float, int)): + raise TypeError(f"Expected 'pcpu' to be of type 'float' or 'int', got {type(self.pcpu).__name__}") + if not isinstance(self.pmem, (float, int)): + raise TypeError(f"Expected 'pmem' to be of type 'float' or 'int', got {type(self.pmem).__name__}") + if not isinstance(self.rss, int): + raise TypeError(f"Expected 'rss' to be of type 'int', got {type(self.rss).__name__}") + if not isinstance(self.vsz, int): + raise TypeError(f"Expected 'vsz' to be of type 'int', got {type(self.vsz).__name__}") + if not isinstance(self.timestamp, str): + raise TypeError(f"Expected 'timestamp' to be of type 'str', got {type(self.timestamp).__name__}") + @dataclass class LogPaths: diff --git a/test/test_report.py b/test/test_report.py index 94d4b83d..2c0e1915 100644 --- a/test/test_report.py +++ b/test/test_report.py @@ -1,4 +1,6 @@ from __future__ import annotations +from datetime import datetime +import pytest from con_duct.__main__ import Averages, ProcessStats, Sample stat0 = ProcessStats( @@ -89,3 +91,36 @@ def test_averages_three_samples() -> None: averages.update(sample2) averages.update(sample2) assert averages.pcpu == (stat0.pcpu + (2 * stat1.pcpu)) / 3 + + +def test_process_stats_green() -> None: + # Assert does not raise + ProcessStats( + pcpu=1.0, + pmem=1.1, + rss=1024, + vsz=1025, + timestamp=datetime.now().astimezone().isoformat(), + ) + + +def test_process_stats_handle_ints() -> None: + # Assert does not raise + ProcessStats( + pcpu=1, + pmem=1, + rss=1024, + vsz=1025, + timestamp=datetime.now().astimezone().isoformat(), + ) + + +def test_process_stats_incorrect_type() -> None: + with pytest.raises(TypeError): + ProcessStats( + pcpu="oopsie", # type: ignore[arg-type] + pmem=1.1, + rss=1024, + vsz=1025, + timestamp=datetime.now().astimezone().isoformat(), + ) From e83566329ae348f78e34313ad89214b572f3f63f Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 19:36:57 -0500 Subject: [PATCH 07/23] fixup sample total field, no longer kib --- src/con_duct/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 6137942a..99489433 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -232,8 +232,8 @@ def for_json(self) -> dict[str, Any]: "totals": { # total of all processes during this sample "pmem": self.total_pmem, "pcpu": self.total_pcpu, - "rss_kb": self.total_rss, - "vsz_kb": self.total_vsz, + "rss": self.total_rss, + "vsz": self.total_vsz, }, "averages": asdict(self.averages) if self.averages.num_samples >= 1 else {}, } From b548c5a25727e659a92bebbdee6226a92c0d9a94 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 19:59:25 -0500 Subject: [PATCH 08/23] Ensure averages stay numbers --- src/con_duct/__main__.py | 7 +++++++ test/test_report.py | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 99489433..27335b2b 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -173,6 +173,7 @@ class Averages: num_samples: int = 0 def update(self: Averages, other: Sample) -> None: + self._assert_num(other.total_rss, other.total_vsz, other.total_pmem, other.total_pcpu) self.num_samples += 1 self.rss += (other.total_rss - self.rss) / self.num_samples self.vsz += (other.total_vsz - self.vsz) / self.num_samples @@ -181,6 +182,7 @@ def update(self: Averages, other: Sample) -> None: @classmethod def from_sample(cls, sample: Sample) -> Averages: + cls._assert_num(sample.total_rss, sample.total_vsz, sample.total_pmem, sample.total_pcpu) return cls( rss=sample.total_rss, vsz=sample.total_vsz, @@ -189,6 +191,11 @@ def from_sample(cls, sample: Sample) -> Averages: num_samples=1, ) + @staticmethod + def _assert_num(*values: Any) -> None: + for value in values: + assert isinstance(value, (float, int)) + @dataclass class Sample: diff --git a/test/test_report.py b/test/test_report.py index 2c0e1915..93a6a8b4 100644 --- a/test/test_report.py +++ b/test/test_report.py @@ -124,3 +124,13 @@ def test_process_stats_incorrect_type() -> None: vsz=1025, timestamp=datetime.now().astimezone().isoformat(), ) + + +def test_averages_assert_num_green() -> None: + # Assert does not raise + Averages._assert_num(0, 1, 1.0, 0.1) + + +def test_averages_assert_num_invalid() -> None: + with pytest.raises(AssertionError): + Averages._assert_num("oops") # type: ignore[arg-type] From 39fc2335ef8dbac5d51e1805f0b1ca293ade4f78 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 20:03:54 -0500 Subject: [PATCH 09/23] hit all the fields for coverage --- test/test_report.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test/test_report.py b/test/test_report.py index 93a6a8b4..79d0140a 100644 --- a/test/test_report.py +++ b/test/test_report.py @@ -115,7 +115,7 @@ def test_process_stats_handle_ints() -> None: ) -def test_process_stats_incorrect_type() -> None: +def test_process_stats_incorrect_pcpu_type() -> None: with pytest.raises(TypeError): ProcessStats( pcpu="oopsie", # type: ignore[arg-type] @@ -126,6 +126,39 @@ def test_process_stats_incorrect_type() -> None: ) +def test_process_stats_incorrect_pmem_type() -> None: + with pytest.raises(TypeError): + ProcessStats( + pcpu=1.1, + pmem="oopsie", # type: ignore[arg-type] + rss=1024, + vsz=1025, + timestamp=datetime.now().astimezone().isoformat(), + ) + + +def test_process_stats_incorrect_rss_type() -> None: + with pytest.raises(TypeError): + ProcessStats( + pcpu=1.1, + pmem=1.0, + rss="oopsie", # type: ignore[arg-type] + vsz=1025, + timestamp=datetime.now().astimezone().isoformat(), + ) + + +def test_process_stats_incorrect_vsz_type() -> None: + with pytest.raises(TypeError): + ProcessStats( + pcpu=1.1, + pmem=1.0, + rss=1025, + vsz="oopsie", # type: ignore[arg-type] + timestamp=datetime.now().astimezone().isoformat(), + ) + + def test_averages_assert_num_green() -> None: # Assert does not raise Averages._assert_num(0, 1, 1.0, 0.1) From cc3622d570b7531f64355823045847bb3075d4b2 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Mon, 12 Aug 2024 20:10:38 -0500 Subject: [PATCH 10/23] Add ts type validation --- test/test_report.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/test_report.py b/test/test_report.py index 79d0140a..b3b3c672 100644 --- a/test/test_report.py +++ b/test/test_report.py @@ -159,6 +159,17 @@ def test_process_stats_incorrect_vsz_type() -> None: ) +def test_process_stats_incorrect_ts_type() -> None: + with pytest.raises(TypeError): + ProcessStats( + pcpu=1.1, + pmem=1.0, + rss=1025, + vsz=1024, + timestamp=1 # type: ignore[arg-type] + ) + + def test_averages_assert_num_green() -> None: # Assert does not raise Averages._assert_num(0, 1, 1.0, 0.1) From 098a581e19589e8bb6b79ec70933366ec42a35d9 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 14:01:34 -0500 Subject: [PATCH 11/23] execution summary file should contain raw values This is intended to be machine readable, better to keep the values as they are without rounding or truncation. --- src/con_duct/__main__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 27335b2b..f9472dbb 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -367,44 +367,44 @@ def execution_summary(self) -> dict[str, Any]: "exit_code": self.process.returncode, "command": self.command, "logs_prefix": self.log_paths.prefix, - "wall_clock_time": f"{self.elapsed_time:.3f}", + "wall_clock_time": self.elapsed_time, "peak_rss": ( - f"{self.max_values.total_rss}" + self.max_values.total_rss if self.max_values.stats else "unknown" ), "average_rss": ( - f"{int(self.averages.rss)}" + self.averages.rss if self.averages.num_samples >= 1 else "unknown" ), "peak_vsz": ( - f"{self.max_values.total_vsz}" + self.max_values.total_vsz if self.max_values.stats else "unknown" ), "average_vsz": ( - f"{int(self.averages.vsz)}" + self.averages.vsz if self.averages.num_samples >= 1 else "unknown" ), "peak_pmem": ( - f"{self.max_values.total_pmem}" + self.max_values.total_pmem if self.max_values.stats else "unknown" ), "average_pmem": ( - f"{self.averages.pmem:.3f}" + self.averages.pmem if self.averages.num_samples >= 1 else "unknown" ), "peak_pcpu": ( - f"{self.max_values.total_pcpu}" + self.max_values.total_pcpu if self.max_values.stats else "unknown" ), "average_pcpu": ( - f"{self.averages.pcpu:.3f}" + self.averages.pcpu if self.averages.num_samples >= 1 else "unknown" ), From 48e17947b95a907bed549c4e9c29502cf22cff5c Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 14:09:38 -0500 Subject: [PATCH 12/23] Add rounding and units for user-facing (rendered) summary --- src/con_duct/__main__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index f9472dbb..06aeac94 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -27,15 +27,15 @@ "Exit Code: {exit_code}\n" "Command: {command}\n" "Log files location: {logs_prefix}\n" - "Wall Clock Time: {wall_clock_time} sec\n" - "Memory Peak Usage (RSS): {peak_rss}\n" - "Memory Average Usage (RSS): {average_rss}\n" - "Virtual Memory Peak Usage (VSZ): {peak_vsz}\n" - "Virtual Memory Average Usage (VSZ): {average_vsz}\n" - "Memory Peak Percentage: {peak_pmem}\n" - "Memory Average Percentage: {average_pmem}\n" - "CPU Peak Usage: {peak_pcpu}\n" - "Average CPU Usage: {average_pcpu}\n" + "Wall Clock Time: {wall_clock_time:.3f} sec\n" + "Memory Peak Usage (RSS): {peak_rss} bytes\n" + "Memory Average Usage (RSS): {average_rss:.0f} bytes\n" + "Virtual Memory Peak Usage (VSZ): {peak_vsz} bytes\n" + "Virtual Memory Average Usage (VSZ): {average_vsz:.0f} bytes\n" + "Memory Peak Percentage: {peak_pmem:.3f}\n" + "Memory Average Percentage: {average_pmem:.3f}\n" + "CPU Peak Usage: {peak_pcpu:.3f}\n" + "Average CPU Usage: {average_pcpu:.3f}\n" "Samples Collected: {num_samples}\n" "Reports Written: {num_reports}\n" ) From bac8087ef3e321a92f48449943a1ce8a8bccd064 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 14:13:27 -0500 Subject: [PATCH 13/23] [DATALAD RUNCMD] Autoupdate readme === Do not change lines below === { "chain": [], "cmd": "./.update-readme-help.py", "exit": 0, "extra_inputs": [], "inputs": [], "outputs": [], "pwd": "." } ^^^ Do not change lines above ^^^ --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0ea2bc0c..0cb56962 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,15 @@ options: Output template to use when printing the summary following execution. (default: Exit Code: {exit_code} Command: {command} Log files location: {logs_prefix} - Wall Clock Time: {wall_clock_time} sec Memory Peak - Usage (RSS): {peak_rss} Memory Average Usage (RSS): - {average_rss} Virtual Memory Peak Usage (VSZ): - {peak_vsz} Virtual Memory Average Usage (VSZ): - {average_vsz} Memory Peak Percentage: {peak_pmem} - Memory Average Percentage: {average_pmem} CPU Peak - Usage: {peak_pcpu} Average CPU Usage: {average_pcpu} - Samples Collected: {num_samples} Reports Written: + Wall Clock Time: {wall_clock_time:.3f} sec Memory Peak + Usage (RSS): {peak_rss} bytes Memory Average Usage + (RSS): {average_rss:.0f} bytes Virtual Memory Peak + Usage (VSZ): {peak_vsz} bytes Virtual Memory Average + Usage (VSZ): {average_vsz:.0f} bytes Memory Peak + Percentage: {peak_pmem:.3f} Memory Average Percentage: + {average_pmem:.3f} CPU Peak Usage: {peak_pcpu:.3f} + Average CPU Usage: {average_pcpu:.3f} Samples + Collected: {num_samples} Reports Written: {num_reports} ) --clobber Replace log files if they already exist. (default: False) From 417e493b9d2abdf1b0a354bc01aa0f6b315f627f Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 14:44:24 -0500 Subject: [PATCH 14/23] type check func --- src/con_duct/__main__.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 06aeac94..eb01c518 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -100,16 +100,11 @@ def __post_init__(self) -> None: self._validate() def _validate(self) -> None: - if not isinstance(self.pcpu, (float, int)): - raise TypeError(f"Expected 'pcpu' to be of type 'float' or 'int', got {type(self.pcpu).__name__}") - if not isinstance(self.pmem, (float, int)): - raise TypeError(f"Expected 'pmem' to be of type 'float' or 'int', got {type(self.pmem).__name__}") - if not isinstance(self.rss, int): - raise TypeError(f"Expected 'rss' to be of type 'int', got {type(self.rss).__name__}") - if not isinstance(self.vsz, int): - raise TypeError(f"Expected 'vsz' to be of type 'int', got {type(self.vsz).__name__}") - if not isinstance(self.timestamp, str): - raise TypeError(f"Expected 'timestamp' to be of type 'str', got {type(self.timestamp).__name__}") + for fname, types in (("pcpu", (float, int)), ("pmem", (float, int)), ("rss", int), ("vsz", int), ("timestamp", str)): + value = getattr(self, fname) + if not isinstance(value, types): + msg = f"Expected '{fname}' to be of type {' or '.join(map(repr(types)))}, got {type(value).__name__}" + raise TypeError(msg) @dataclass From 9c29b6033d236d4f58f275611dcd5cdef80dd2ca Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 14:45:06 -0500 Subject: [PATCH 15/23] fixup rm execution formatting, must be ok with 'unknown' --- src/con_duct/__main__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index eb01c518..9df09a0b 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -29,13 +29,13 @@ "Log files location: {logs_prefix}\n" "Wall Clock Time: {wall_clock_time:.3f} sec\n" "Memory Peak Usage (RSS): {peak_rss} bytes\n" - "Memory Average Usage (RSS): {average_rss:.0f} bytes\n" + "Memory Average Usage (RSS): {average_rss} bytes\n" "Virtual Memory Peak Usage (VSZ): {peak_vsz} bytes\n" - "Virtual Memory Average Usage (VSZ): {average_vsz:.0f} bytes\n" - "Memory Peak Percentage: {peak_pmem:.3f}\n" - "Memory Average Percentage: {average_pmem:.3f}\n" - "CPU Peak Usage: {peak_pcpu:.3f}\n" - "Average CPU Usage: {average_pcpu:.3f}\n" + "Virtual Memory Average Usage (VSZ): {average_vsz} bytes\n" + "Memory Peak Percentage: {peak_pmem}\n" + "Memory Average Percentage: {average_pmem}\n" + "CPU Peak Usage: {peak_pcpu}\n" + "Average CPU Usage: {average_pcpu}\n" "Samples Collected: {num_samples}\n" "Reports Written: {num_reports}\n" ) From 4b5a28979fc7154b49a13ef5da9633976d9adb3c Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 15:28:00 -0500 Subject: [PATCH 16/23] fix map usage --- src/con_duct/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 9df09a0b..dcac4f9d 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -100,10 +100,10 @@ def __post_init__(self) -> None: self._validate() def _validate(self) -> None: - for fname, types in (("pcpu", (float, int)), ("pmem", (float, int)), ("rss", int), ("vsz", int), ("timestamp", str)): + for fname, types in (("pcpu", (float, int)), ("pmem", (float, int)), ("rss", (int)), ("vsz", (int)), ("timestamp", (str))): value = getattr(self, fname) if not isinstance(value, types): - msg = f"Expected '{fname}' to be of type {' or '.join(map(repr(types)))}, got {type(value).__name__}" + msg = f"Expected '{fname}' to be of type {' or '.join(map(repr, types))}, got {type(value).__name__}" raise TypeError(msg) From 7c211f084863495b4e6f7839d4f817945c4a3ba6 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 15:31:14 -0500 Subject: [PATCH 17/23] fixup mypy --- src/con_duct/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index dcac4f9d..fe7fcb63 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -100,7 +100,7 @@ def __post_init__(self) -> None: self._validate() def _validate(self) -> None: - for fname, types in (("pcpu", (float, int)), ("pmem", (float, int)), ("rss", (int)), ("vsz", (int)), ("timestamp", (str))): + for fname, types in (("pcpu", (float, int)), ("pmem", (float, int)), ("rss", (int,)), ("vsz", (int,)), ("timestamp", (str,))): value = getattr(self, fname) if not isinstance(value, types): msg = f"Expected '{fname}' to be of type {' or '.join(map(repr, types))}, got {type(value).__name__}" From 36222faa709de4328ef626e89a8485446d07b8bf Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 15:44:57 -0500 Subject: [PATCH 18/23] rerun pre-commit -a Accidentally left out of new venv --- README.md | 2 +- src/con_duct/__main__.py | 48 ++++++++++++++++++---------------------- test/test_report.py | 2 +- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 0cb56962..228f71ac 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![codecov](https://codecov.io/gh/con/duct/graph/badge.svg?token=JrPazw0Vn4)](https://codecov.io/gh/con/duct) [![PyPI version](https://badge.fury.io/py/con-duct.svg)](https://badge.fury.io/py/con-duct) -## Installation +## Installation pip install con-duct diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index fe7fcb63..7f87baa9 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -100,7 +100,13 @@ def __post_init__(self) -> None: self._validate() def _validate(self) -> None: - for fname, types in (("pcpu", (float, int)), ("pmem", (float, int)), ("rss", (int,)), ("vsz", (int,)), ("timestamp", (str,))): + for fname, types in ( + ("pcpu", (float, int)), + ("pmem", (float, int)), + ("rss", (int,)), + ("vsz", (int,)), + ("timestamp", (str,)), + ): value = getattr(self, fname) if not isinstance(value, types): msg = f"Expected '{fname}' to be of type {' or '.join(map(repr, types))}, got {type(value).__name__}" @@ -168,7 +174,9 @@ class Averages: num_samples: int = 0 def update(self: Averages, other: Sample) -> None: - self._assert_num(other.total_rss, other.total_vsz, other.total_pmem, other.total_pcpu) + self._assert_num( + other.total_rss, other.total_vsz, other.total_pmem, other.total_pcpu + ) self.num_samples += 1 self.rss += (other.total_rss - self.rss) / self.num_samples self.vsz += (other.total_vsz - self.vsz) / self.num_samples @@ -177,7 +185,9 @@ def update(self: Averages, other: Sample) -> None: @classmethod def from_sample(cls, sample: Sample) -> Averages: - cls._assert_num(sample.total_rss, sample.total_vsz, sample.total_pmem, sample.total_pcpu) + cls._assert_num( + sample.total_rss, sample.total_vsz, sample.total_pmem, sample.total_pcpu + ) return cls( rss=sample.total_rss, vsz=sample.total_vsz, @@ -364,44 +374,28 @@ def execution_summary(self) -> dict[str, Any]: "logs_prefix": self.log_paths.prefix, "wall_clock_time": self.elapsed_time, "peak_rss": ( - self.max_values.total_rss - if self.max_values.stats - else "unknown" + self.max_values.total_rss if self.max_values.stats else "unknown" ), "average_rss": ( - self.averages.rss - if self.averages.num_samples >= 1 - else "unknown" + self.averages.rss if self.averages.num_samples >= 1 else "unknown" ), "peak_vsz": ( - self.max_values.total_vsz - if self.max_values.stats - else "unknown" + self.max_values.total_vsz if self.max_values.stats else "unknown" ), "average_vsz": ( - self.averages.vsz - if self.averages.num_samples >= 1 - else "unknown" + self.averages.vsz if self.averages.num_samples >= 1 else "unknown" ), "peak_pmem": ( - self.max_values.total_pmem - if self.max_values.stats - else "unknown" + self.max_values.total_pmem if self.max_values.stats else "unknown" ), "average_pmem": ( - self.averages.pmem - if self.averages.num_samples >= 1 - else "unknown" + self.averages.pmem if self.averages.num_samples >= 1 else "unknown" ), "peak_pcpu": ( - self.max_values.total_pcpu - if self.max_values.stats - else "unknown" + self.max_values.total_pcpu if self.max_values.stats else "unknown" ), "average_pcpu": ( - self.averages.pcpu - if self.averages.num_samples >= 1 - else "unknown" + self.averages.pcpu if self.averages.num_samples >= 1 else "unknown" ), "num_samples": self.averages.num_samples, "num_reports": self.number, diff --git a/test/test_report.py b/test/test_report.py index b3b3c672..57d8183f 100644 --- a/test/test_report.py +++ b/test/test_report.py @@ -166,7 +166,7 @@ def test_process_stats_incorrect_ts_type() -> None: pmem=1.0, rss=1025, vsz=1024, - timestamp=1 # type: ignore[arg-type] + timestamp=1, # type: ignore[arg-type] ) From 774227cd0467817e5d777b56c61489eba7420c3a Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 15:48:25 -0500 Subject: [PATCH 19/23] shorten line, ignored by black, but flake8 failed --- src/con_duct/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 7f87baa9..ce8b5d9e 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -109,7 +109,8 @@ def _validate(self) -> None: ): value = getattr(self, fname) if not isinstance(value, types): - msg = f"Expected '{fname}' to be of type {' or '.join(map(repr, types))}, got {type(value).__name__}" + expected_types = " or ".join(map(repr, types)) + msg = f"Expected '{fname}' to be of type {expected_types}, got {type(value).__name__}" raise TypeError(msg) From 8dd4d7164ca19093264f0fc1396489c024235ac8 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 15:51:17 -0500 Subject: [PATCH 20/23] [DATALAD RUNCMD] Autoupdate readme === Do not change lines below === { "chain": [], "cmd": "./.update-readme-help.py", "exit": 0, "extra_inputs": [], "inputs": [], "outputs": [], "pwd": "." } ^^^ Do not change lines above ^^^ --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 228f71ac..0e12a89a 100644 --- a/README.md +++ b/README.md @@ -56,14 +56,13 @@ options: Command: {command} Log files location: {logs_prefix} Wall Clock Time: {wall_clock_time:.3f} sec Memory Peak Usage (RSS): {peak_rss} bytes Memory Average Usage - (RSS): {average_rss:.0f} bytes Virtual Memory Peak - Usage (VSZ): {peak_vsz} bytes Virtual Memory Average - Usage (VSZ): {average_vsz:.0f} bytes Memory Peak - Percentage: {peak_pmem:.3f} Memory Average Percentage: - {average_pmem:.3f} CPU Peak Usage: {peak_pcpu:.3f} - Average CPU Usage: {average_pcpu:.3f} Samples - Collected: {num_samples} Reports Written: - {num_reports} ) + (RSS): {average_rss} bytes Virtual Memory Peak Usage + (VSZ): {peak_vsz} bytes Virtual Memory Average Usage + (VSZ): {average_vsz} bytes Memory Peak Percentage: + {peak_pmem} Memory Average Percentage: {average_pmem} + CPU Peak Usage: {peak_pcpu} Average CPU Usage: + {average_pcpu} Samples Collected: {num_samples} + Reports Written: {num_reports} ) --clobber Replace log files if they already exist. (default: False) -q, --quiet Suppress duct output. (default: False) From 3f9099dc2c0697ee70dd38170c0cf4c77d900963 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 16:01:52 -0500 Subject: [PATCH 21/23] Consolodate number checking Removed check for timestamp being a string, and relax requirement for int rather than float for some fields. This significantly reduces complexity. --- src/con_duct/__main__.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index ce8b5d9e..3d8fca0d 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -41,6 +41,11 @@ ) +def assert_num(*values: Any) -> None: + for value in values: + assert isinstance(value, (float, int)) + + class Outputs(str, Enum): ALL = "all" NONE = "none" @@ -100,18 +105,7 @@ def __post_init__(self) -> None: self._validate() def _validate(self) -> None: - for fname, types in ( - ("pcpu", (float, int)), - ("pmem", (float, int)), - ("rss", (int,)), - ("vsz", (int,)), - ("timestamp", (str,)), - ): - value = getattr(self, fname) - if not isinstance(value, types): - expected_types = " or ".join(map(repr, types)) - msg = f"Expected '{fname}' to be of type {expected_types}, got {type(value).__name__}" - raise TypeError(msg) + assert_num(self.pcpu, self.pmem, self.rss, self.vsz) @dataclass @@ -175,7 +169,7 @@ class Averages: num_samples: int = 0 def update(self: Averages, other: Sample) -> None: - self._assert_num( + assert_num( other.total_rss, other.total_vsz, other.total_pmem, other.total_pcpu ) self.num_samples += 1 @@ -186,7 +180,7 @@ def update(self: Averages, other: Sample) -> None: @classmethod def from_sample(cls, sample: Sample) -> Averages: - cls._assert_num( + assert_num( sample.total_rss, sample.total_vsz, sample.total_pmem, sample.total_pcpu ) return cls( @@ -197,11 +191,6 @@ def from_sample(cls, sample: Sample) -> Averages: num_samples=1, ) - @staticmethod - def _assert_num(*values: Any) -> None: - for value in values: - assert isinstance(value, (float, int)) - @dataclass class Sample: From 3af906a6a6c0016a69c4157fcea231a669d45a18 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Tue, 13 Aug 2024 16:27:44 -0500 Subject: [PATCH 22/23] consolidate and parameterize tests --- test/test_report.py | 108 ++++++++++++---------------------------- test/test_validation.py | 13 ++++- 2 files changed, 44 insertions(+), 77 deletions(-) diff --git a/test/test_report.py b/test/test_report.py index 57d8183f..0132f2c7 100644 --- a/test/test_report.py +++ b/test/test_report.py @@ -93,88 +93,44 @@ def test_averages_three_samples() -> None: assert averages.pcpu == (stat0.pcpu + (2 * stat1.pcpu)) / 3 -def test_process_stats_green() -> None: - # Assert does not raise - ProcessStats( - pcpu=1.0, - pmem=1.1, - rss=1024, - vsz=1025, - timestamp=datetime.now().astimezone().isoformat(), - ) - - -def test_process_stats_handle_ints() -> None: +@pytest.mark.parametrize( + "pcpu, pmem, rss, vsz", + [ + (1.0, 1.1, 1024, 1025), + (0.5, 0.7, 20.48, 40.96), + (1, 2, 3, 4), + (0, 0.0, 0, 0.0), + (2.5, 3.5, 8192, 16384), + (100.0, 99.9, 65536, 131072), + ] +) +def test_process_stats_green(pcpu: float, pmem: float, rss: int, vsz: int) -> None: # Assert does not raise ProcessStats( - pcpu=1, - pmem=1, - rss=1024, - vsz=1025, + pcpu=pcpu, + pmem=pmem, + rss=rss, + vsz=vsz, timestamp=datetime.now().astimezone().isoformat(), ) -def test_process_stats_incorrect_pcpu_type() -> None: - with pytest.raises(TypeError): - ProcessStats( - pcpu="oopsie", # type: ignore[arg-type] - pmem=1.1, - rss=1024, - vsz=1025, - timestamp=datetime.now().astimezone().isoformat(), - ) - - -def test_process_stats_incorrect_pmem_type() -> None: - with pytest.raises(TypeError): - ProcessStats( - pcpu=1.1, - pmem="oopsie", # type: ignore[arg-type] - rss=1024, - vsz=1025, - timestamp=datetime.now().astimezone().isoformat(), - ) - - -def test_process_stats_incorrect_rss_type() -> None: - with pytest.raises(TypeError): - ProcessStats( - pcpu=1.1, - pmem=1.0, - rss="oopsie", # type: ignore[arg-type] - vsz=1025, - timestamp=datetime.now().astimezone().isoformat(), - ) - - -def test_process_stats_incorrect_vsz_type() -> None: - with pytest.raises(TypeError): +@pytest.mark.parametrize( + "pcpu, pmem, rss, vsz", + [ + ("only", 1.1, 1024, 1025), + (0.5, "takes", 20.48, 40.96), + (1, 2, "one", 4), + (1, 2, 3, "value"), + ("2", "fail", "or", "more"), + ] +) +def test_process_stats_red(pcpu: float, pmem: float, rss: int, vsz: int) -> None: + with pytest.raises(AssertionError): ProcessStats( - pcpu=1.1, - pmem=1.0, - rss=1025, - vsz="oopsie", # type: ignore[arg-type] + pcpu=pcpu, + pmem=pmem, + rss=rss, + vsz=vsz, timestamp=datetime.now().astimezone().isoformat(), ) - - -def test_process_stats_incorrect_ts_type() -> None: - with pytest.raises(TypeError): - ProcessStats( - pcpu=1.1, - pmem=1.0, - rss=1025, - vsz=1024, - timestamp=1, # type: ignore[arg-type] - ) - - -def test_averages_assert_num_green() -> None: - # Assert does not raise - Averages._assert_num(0, 1, 1.0, 0.1) - - -def test_averages_assert_num_invalid() -> None: - with pytest.raises(AssertionError): - Averages._assert_num("oops") # type: ignore[arg-type] diff --git a/test/test_validation.py b/test/test_validation.py index 00c28abd..f6ce7c21 100644 --- a/test/test_validation.py +++ b/test/test_validation.py @@ -1,6 +1,6 @@ import argparse import pytest -from con_duct.__main__ import Arguments, Outputs, RecordTypes +from con_duct.__main__ import Arguments, Outputs, RecordTypes, assert_num def test_sample_less_than_report_interval() -> None: @@ -52,3 +52,14 @@ def test_sample_equal_greater_than_report_interval() -> None: summary_format="", quiet=False, ) + + +@pytest.mark.parametrize("input_value", [0, 1, 2, -1, 100, 0.001, -1.68]) +def test_assert_num_green(input_value: int) -> None: + assert_num(input_value) + + +@pytest.mark.parametrize("input_value", ["hi", "0", "one"]) +def test_assert_num_red(input_value: int) -> None: + with pytest.raises(AssertionError): + assert_num(input_value) From ea0d04e558dec485ac1090346ad981aa070663b2 Mon Sep 17 00:00:00 2001 From: Austin Macdonald Date: Wed, 14 Aug 2024 11:19:10 -0500 Subject: [PATCH 23/23] Explicitly show that ps gives memory in KiB --- src/con_duct/__main__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 3d8fca0d..56e3b312 100644 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -330,14 +330,14 @@ def collect_sample(self) -> Sample: ) for line in output.splitlines()[1:]: if line: - pid, pcpu, pmem, rss, vsz, etime, cmd = line.split(maxsplit=6) + pid, pcpu, pmem, rss_kib, vsz_kib, etime, cmd = line.split(maxsplit=6) sample.add_pid( int(pid), ProcessStats( pcpu=float(pcpu), pmem=float(pmem), - rss=int(rss) * 1024, - vsz=int(vsz) * 1024, + rss=int(rss_kib) * 1024, + vsz=int(vsz_kib) * 1024, timestamp=datetime.now().astimezone().isoformat(), ), )