From 368d422ed0515b163426206060945074cf32cac0 Mon Sep 17 00:00:00 2001 From: Vinicius Bernal Date: Mon, 17 Aug 2026 20:47:46 -0300 Subject: [PATCH 1/2] fix: keep the byte counts on NotEnoughFreeSpaceError The error accepted required/available/file_system, used them to build the message and then discarded them, so callers could only recover the numbers by parsing the message text. They are now kept as attributes, along with a precomputed shortfall. The message itself gained thousands separators and the shortfall, since a nine-digit byte count is hard to read and the remaining space was left for the reader to work out. The legacy min_space form is unchanged; its byte attributes are None. --- changes/426.changed | 1 + pyntc/errors.py | 26 +++++++++++++++++++++++-- tests/unit/test_errors.py | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 changes/426.changed diff --git a/changes/426.changed b/changes/426.changed new file mode 100644 index 00000000..b1b4b7b4 --- /dev/null +++ b/changes/426.changed @@ -0,0 +1 @@ +Kept the byte counts on `NotEnoughFreeSpaceError` as attributes (`required`, `available`, `file_system`, `shortfall`) so callers no longer have to parse the message, and its message now reports those counts with thousands separators along with the remaining shortfall. diff --git a/pyntc/errors.py b/pyntc/errors.py index 45c5fa98..b00cbe2b 100644 --- a/pyntc/errors.py +++ b/pyntc/errors.py @@ -210,6 +210,10 @@ def __init__(self, hostname, min_space=None, *, required=None, available=None, f """ Error for not having enough free space to transfer a file. + The byte counts are kept as attributes so callers can render their own message + (or compute the shortfall) without parsing ``message``. They are ``None`` when the + error is raised in the legacy ``min_space`` form. + Args: hostname (str): The hostname of the device being checked. min_space (str, optional): The minimum amount of space required. Retained for @@ -217,10 +221,28 @@ def __init__(self, hostname, min_space=None, *, required=None, available=None, f required (int, optional): Required bytes for the pending transfer. available (int, optional): Free bytes currently available on the target filesystem. file_system (str, optional): The target filesystem that was checked. + + Attributes: + hostname (str): The hostname of the device being checked. + min_space (str, optional): The minimum amount of space required, legacy form only. + required (int, optional): Required bytes for the pending transfer. + available (int, optional): Free bytes currently available on the target filesystem. + file_system (str, optional): The target filesystem that was checked. + shortfall (int, optional): Bytes still needed for the transfer to succeed. """ - if required is not None and available is not None: + self.hostname = hostname + self.min_space = min_space + self.required = required + self.available = available + self.file_system = file_system + self.shortfall = required - available if required is not None and available is not None else None + + if self.shortfall is not None: location = f"{file_system} " if file_system else "" - message = f"{hostname}: {location}has {available} bytes free; {required} bytes required for transfer" + message = ( + f"{hostname}: {location}has {available:,} bytes free; {required:,} bytes required for transfer " + f"({self.shortfall:,} more bytes required to succeed)" + ) else: message = f"{hostname} does not meet the minimum disk space requirements of {min_space}" super().__init__(message) diff --git a/tests/unit/test_errors.py b/tests/unit/test_errors.py index 2b4edc0b..5ae58d4a 100644 --- a/tests/unit/test_errors.py +++ b/tests/unit/test_errors.py @@ -138,6 +138,47 @@ def test_not_enough_free_space_error(): assert err.value.message == error_message +def test_not_enough_free_space_error_legacy_form_leaves_byte_counts_unset(): + error = ntc_errors.NotEnoughFreeSpaceError("host1", 1000) + + assert error.min_space == 1000 + assert error.required is None + assert error.available is None + assert error.file_system is None + assert error.shortfall is None + + +def test_not_enough_free_space_error_keeps_byte_counts_as_attributes(): + error = ntc_errors.NotEnoughFreeSpaceError( + hostname="host1", required=313456789, available=13456789, file_system="bootflash:" + ) + + assert error.hostname == "host1" + assert error.required == 313456789 + assert error.available == 13456789 + assert error.file_system == "bootflash:" + assert error.shortfall == 300000000 + + +def test_not_enough_free_space_error_message_reports_counts_and_shortfall(): + error = ntc_errors.NotEnoughFreeSpaceError( + hostname="host1", required=313456789, available=13456789, file_system="bootflash:" + ) + + assert error.message == ( + "host1: bootflash: has 13,456,789 bytes free; 313,456,789 bytes required for transfer " + "(300,000,000 more bytes required to succeed)" + ) + + +def test_not_enough_free_space_error_message_omits_file_system_when_unknown(): + error = ntc_errors.NotEnoughFreeSpaceError(hostname="host1", required=2500, available=500) + + assert error.message == ( + "host1: has 500 bytes free; 2,500 bytes required for transfer (2,000 more bytes required to succeed)" + ) + + def test_os_install_error(): error_message = "host1 was unable to boot into v1.2.3" error_class = ntc_errors.OSInstallError From 61d5744289436f5892c7287467bfad78ff932c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Bernal?= <30478051+bernalvinicius@users.noreply.github.com> Date: Tue, 18 Aug 2026 14:39:16 -0300 Subject: [PATCH 2/2] Update pyntc/errors.py Co-authored-by: Josh VanDeraa --- pyntc/errors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyntc/errors.py b/pyntc/errors.py index b00cbe2b..128ee5da 100644 --- a/pyntc/errors.py +++ b/pyntc/errors.py @@ -211,8 +211,8 @@ def __init__(self, hostname, min_space=None, *, required=None, available=None, f Error for not having enough free space to transfer a file. The byte counts are kept as attributes so callers can render their own message - (or compute the shortfall) without parsing ``message``. They are ``None`` when the - error is raised in the legacy ``min_space`` form. + (or compute the shortfall) without parsing `message`. They are `None` when the + error is raised in the legacy `min_space` form. Args: hostname (str): The hostname of the device being checked.