Format file sizes for the attachments list
Python · Python · intermediate · greenfield
Adds the `format_size` helper used by the attachments list. Walks the binary units dividing by 1024, keeps bytes as integers, and rounds larger sizes to one decimal. Handles the boundary where rounding alone would display `1024.0 KB` by rolling over to the next unit.
Requirements
- Implement `format_size(num_bytes)` for the attachments list: `num_bytes` is a non-negative `int` no larger than `2**63` (sizes come from the filesystem, which caps file size well below that; values above the cap are out of contract). Reject non-ints, including `bool`, with `TypeError`; negatives with `ValueError`.
- Binary units, factor 1024: `B`, `KB`, `MB`, `GB`, `TB`. `TB` is the ceiling — anything larger still renders in `TB`.
- Below 1024 bytes render as an integer with the `B` unit (`0 B`, `512 B`); larger sizes render with exactly one decimal place (`1.0 KB`, `2.4 MB`).
- Pick the largest unit that keeps the displayed number below 1024 — the UI must never show a value like `1024.0 KB`, including when it is *rounding* that pushes the displayed value to 1024.0. The only exception is the `TB` ceiling, where there is no next unit and values of 1024.0 and above are expected.
- Decimal rounding follows Python's default float formatting (round-half-even on the underlying double); no custom rounding is required.
Files touched
- app/files/format_size.py
--- app/files/format_size.py
+UNITS = ["B", "KB", "MB", "GB", "TB"]
+
+
+def format_size(num_bytes):
+ """Human-readable binary size: bytes as an integer, larger units to one decimal."""
+ if isinstance(num_bytes, bool) or not isinstance(num_bytes, int):
+ raise TypeError("num_bytes must be an int")
+ if num_bytes < 0:
+ raise ValueError("num_bytes must be non-negative")
+ if num_bytes < 1024:
+ return f"{num_bytes} B"
+ size = float(num_bytes)
+ unit = 0
+ while size >= 1024 and unit < len(UNITS) - 1:
+ size /= 1024.0
+ unit += 1
+ # Rounding to one decimal can land exactly on 1024.0 (e.g. 1048571 bytes
+ # is 1023.995 KB); roll over so "1024.0 KB" never renders.
+ if round(size, 1) >= 1024 and unit < len(UNITS) - 1:
+ size /= 1024.0
+ unit += 1
+ return f"{size:.1f} {UNITS[unit]}"
+