Skip to content

Commit

Permalink
Allow an optional separator splitting the value and unit of the resul…
Browse files Browse the repository at this point in the history
…t of `ByteSize.human_readable`. (#8706)

Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com>
  • Loading branch information
jks15satoshi and sydney-runkle committed Feb 6, 2024
1 parent 178823b commit 28ed33f
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 18 deletions.
11 changes: 7 additions & 4 deletions pydantic/types.py
Expand Up @@ -1740,6 +1740,8 @@ class MyModel(BaseModel):
#> 44.4PiB
print(m.size.human_readable(decimal=True))
#> 50.0PB
print(m.size.human_readable(separator=' '))
#> 44.4 PiB
print(m.size.to('TiB'))
#> 45474.73508864641
Expand Down Expand Up @@ -1818,12 +1820,13 @@ def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> ByteSiz

return cls(int(float(scalar) * unit_mult))

def human_readable(self, decimal: bool = False) -> str:
def human_readable(self, decimal: bool = False, separator: str = '') -> str:
"""Converts a byte size to a human readable string.
Args:
decimal: If True, use decimal units (e.g. 1000 bytes per KB). If False, use binary units
(e.g. 1024 bytes per KiB).
separator: A string used to split the value and unit. Defaults to an empty string ('').
Returns:
A human readable string representation of the byte size.
Expand All @@ -1841,12 +1844,12 @@ def human_readable(self, decimal: bool = False) -> str:
for unit in units:
if abs(num) < divisor:
if unit == 'B':
return f'{num:0.0f}{unit}'
return f'{num:0.0f}{separator}{unit}'
else:
return f'{num:0.1f}{unit}'
return f'{num:0.1f}{separator}{unit}'
num /= divisor

return f'{num:0.1f}{final_unit}'
return f'{num:0.1f}{separator}{final_unit}'

def to(self, unit: str) -> float:
"""Converts a byte size to another unit, including both byte and bit units.
Expand Down
29 changes: 15 additions & 14 deletions tests/test_types.py
Expand Up @@ -4436,23 +4436,23 @@ class FrozenSetModel(BaseModel):


@pytest.mark.parametrize(
'input_value,output,human_bin,human_dec',
'input_value,output,human_bin,human_dec,human_sep',
(
(1, 1, '1B', '1B'),
('1', 1, '1B', '1B'),
('1.0', 1, '1B', '1B'),
('1b', 1, '1B', '1B'),
('1.5 KB', int(1.5e3), '1.5KiB', '1.5KB'),
('1.5 K', int(1.5e3), '1.5KiB', '1.5KB'),
('1.5 MB', int(1.5e6), '1.4MiB', '1.5MB'),
('1.5 M', int(1.5e6), '1.4MiB', '1.5MB'),
('5.1kib', 5222, '5.1KiB', '5.2KB'),
('6.2EiB', 7148113328562451456, '6.2EiB', '7.1EB'),
('8bit', 1, '1B', '1B'),
('1kbit', 125, '125B', '125B'),
(1, 1, '1B', '1B', '1 B'),
('1', 1, '1B', '1B', '1 B'),
('1.0', 1, '1B', '1B', '1 B'),
('1b', 1, '1B', '1B', '1 B'),
('1.5 KB', int(1.5e3), '1.5KiB', '1.5KB', '1.5 KiB'),
('1.5 K', int(1.5e3), '1.5KiB', '1.5KB', '1.5 KiB'),
('1.5 MB', int(1.5e6), '1.4MiB', '1.5MB', '1.4 MiB'),
('1.5 M', int(1.5e6), '1.4MiB', '1.5MB', '1.4 MiB'),
('5.1kib', 5222, '5.1KiB', '5.2KB', '5.1 KiB'),
('6.2EiB', 7148113328562451456, '6.2EiB', '7.1EB', '6.2 EiB'),
('8bit', 1, '1B', '1B', '1 B'),
('1kbit', 125, '125B', '125B', '125 B'),
),
)
def test_bytesize_conversions(input_value, output, human_bin, human_dec):
def test_bytesize_conversions(input_value, output, human_bin, human_dec, human_sep):
class Model(BaseModel):
size: ByteSize

Expand All @@ -4462,6 +4462,7 @@ class Model(BaseModel):

assert m.size.human_readable() == human_bin
assert m.size.human_readable(decimal=True) == human_dec
assert m.size.human_readable(separator=' ') == human_sep


def test_bytesize_to():
Expand Down

0 comments on commit 28ed33f

Please sign in to comment.