Skip to content

Commit 1dee62d

Browse files
committed
Move human formatting functions under beets.util.units
1 parent 4f6f346 commit 1dee62d

File tree

7 files changed

+117
-110
lines changed

7 files changed

+117
-110
lines changed

beets/dbcore/types.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import beets
2626
from beets import util
27+
from beets.util.units import human_seconds_short, raw_seconds_short
2728

2829
from . import query
2930

@@ -437,14 +438,14 @@ class DurationType(Float):
437438

438439
def format(self, value):
439440
if not beets.config["format_raw_length"].get(bool):
440-
return util.human_seconds_short(value or 0.0)
441+
return human_seconds_short(value or 0.0)
441442
else:
442443
return value
443444

444445
def parse(self, string):
445446
try:
446447
# Try to format back hh:ss to seconds.
447-
return util.raw_seconds_short(string)
448+
return raw_seconds_short(string)
448449
except ValueError:
449450
# Fall back to a plain float.
450451
try:

beets/ui/__init__.py

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -435,48 +435,6 @@ def input_select_objects(prompt, objs, rep, prompt_all=None):
435435
return []
436436

437437

438-
# Human output formatting.
439-
440-
441-
def human_bytes(size):
442-
"""Formats size, a number of bytes, in a human-readable way."""
443-
powers = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "H"]
444-
unit = "B"
445-
for power in powers:
446-
if size < 1024:
447-
return f"{size:3.1f} {power}{unit}"
448-
size /= 1024.0
449-
unit = "iB"
450-
return "big"
451-
452-
453-
def human_seconds(interval):
454-
"""Formats interval, a number of seconds, as a human-readable time
455-
interval using English words.
456-
"""
457-
units = [
458-
(1, "second"),
459-
(60, "minute"),
460-
(60, "hour"),
461-
(24, "day"),
462-
(7, "week"),
463-
(52, "year"),
464-
(10, "decade"),
465-
]
466-
for i in range(len(units) - 1):
467-
increment, suffix = units[i]
468-
next_increment, _ = units[i + 1]
469-
interval /= float(increment)
470-
if interval < next_increment:
471-
break
472-
else:
473-
# Last unit.
474-
increment, suffix = units[-1]
475-
interval /= float(increment)
476-
477-
return f"{interval:3.1f} {suffix}s"
478-
479-
480438
# Colorization.
481439

482440
# ANSI terminal colorization code heavily inspired by pygments:

beets/ui/commands.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
normpath,
4444
syspath,
4545
)
46+
from beets.util.units import human_bytes, human_seconds, human_seconds_short
4647

4748
from . import _store_dict
4849

@@ -541,8 +542,8 @@ def make_track_lengths(item, track_info):
541542
cur_length0 = item.length if item.length else 0
542543
new_length0 = track_info.length if track_info.length else 0
543544
# format into string
544-
cur_length = f"({util.human_seconds_short(cur_length0)})"
545-
new_length = f"({util.human_seconds_short(new_length0)})"
545+
cur_length = f"({human_seconds_short(cur_length0)})"
546+
new_length = f"({human_seconds_short(new_length0)})"
546547
# colorize
547548
lhs_length = ui.colorize(highlight_color, cur_length)
548549
rhs_length = ui.colorize(highlight_color, new_length)
@@ -706,14 +707,14 @@ def show_match_tracks(self):
706707
for track_info in self.match.extra_tracks:
707708
line = f" ! {track_info.title} (#{self.format_index(track_info)})"
708709
if track_info.length:
709-
line += f" ({util.human_seconds_short(track_info.length)})"
710+
line += f" ({human_seconds_short(track_info.length)})"
710711
print_(ui.colorize("text_warning", line))
711712
if self.match.extra_items:
712713
print_(f"Unmatched tracks ({len(self.match.extra_items)}):")
713714
for item in self.match.extra_items:
714715
line = " ! {} (#{})".format(item.title, self.format_index(item))
715716
if item.length:
716-
line += " ({})".format(util.human_seconds_short(item.length))
717+
line += " ({})".format(human_seconds_short(item.length))
717718
print_(ui.colorize("text_warning", line))
718719

719720

@@ -795,8 +796,8 @@ def summarize_items(items, singleton):
795796
round(int(items[0].samplerate) / 1000, 1), items[0].bitdepth
796797
)
797798
summary_parts.append(sample_bits)
798-
summary_parts.append(util.human_seconds_short(total_duration))
799-
summary_parts.append(ui.human_bytes(total_filesize))
799+
summary_parts.append(human_seconds_short(total_duration))
800+
summary_parts.append(human_bytes(total_filesize))
800801

801802
return ", ".join(summary_parts)
802803

@@ -1906,7 +1907,7 @@ def show_stats(lib, query, exact):
19061907
if item.album_id:
19071908
albums.add(item.album_id)
19081909

1909-
size_str = "" + ui.human_bytes(total_size)
1910+
size_str = "" + human_bytes(total_size)
19101911
if exact:
19111912
size_str += f" ({total_size} bytes)"
19121913

@@ -1918,7 +1919,7 @@ def show_stats(lib, query, exact):
19181919
Albums: {}
19191920
Album artists: {}""".format(
19201921
total_items,
1921-
ui.human_seconds(total_time),
1922+
human_seconds(total_time),
19221923
f" ({total_time:.2f} seconds)" if exact else "",
19231924
"Total size" if exact else "Approximate total size",
19241925
size_str,

beets/util/__init__.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,27 +1019,6 @@ def case_sensitive(path: bytes) -> bool:
10191019
return not os.path.samefile(lower_sys, upper_sys)
10201020

10211021

1022-
def raw_seconds_short(string: str) -> float:
1023-
"""Formats a human-readable M:SS string as a float (number of seconds).
1024-
1025-
Raises ValueError if the conversion cannot take place due to `string` not
1026-
being in the right format.
1027-
"""
1028-
match = re.match(r"^(\d+):([0-5]\d)$", string)
1029-
if not match:
1030-
raise ValueError("String not in M:SS format")
1031-
minutes, seconds = map(int, match.groups())
1032-
return float(minutes * 60 + seconds)
1033-
1034-
1035-
def human_seconds_short(interval):
1036-
"""Formats a number of seconds as a short human-readable M:SS
1037-
string.
1038-
"""
1039-
interval = int(interval)
1040-
return "%i:%02i" % (interval // 60, interval % 60)
1041-
1042-
10431022
def asciify_path(path: str, sep_replace: str) -> str:
10441023
"""Decodes all unicode characters in a path into ASCII equivalents.
10451024

beets/util/units.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import re
2+
3+
4+
def raw_seconds_short(string: str) -> float:
5+
"""Formats a human-readable M:SS string as a float (number of seconds).
6+
7+
Raises ValueError if the conversion cannot take place due to `string` not
8+
being in the right format.
9+
"""
10+
match = re.match(r"^(\d+):([0-5]\d)$", string)
11+
if not match:
12+
raise ValueError("String not in M:SS format")
13+
minutes, seconds = map(int, match.groups())
14+
return float(minutes * 60 + seconds)
15+
16+
17+
def human_seconds_short(interval):
18+
"""Formats a number of seconds as a short human-readable M:SS
19+
string.
20+
"""
21+
interval = int(interval)
22+
return "%i:%02i" % (interval // 60, interval % 60)
23+
24+
25+
def human_bytes(size):
26+
"""Formats size, a number of bytes, in a human-readable way."""
27+
powers = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "H"]
28+
unit = "B"
29+
for power in powers:
30+
if size < 1024:
31+
return f"{size:3.1f} {power}{unit}"
32+
size /= 1024.0
33+
unit = "iB"
34+
return "big"
35+
36+
37+
def human_seconds(interval):
38+
"""Formats interval, a number of seconds, as a human-readable time
39+
interval using English words.
40+
"""
41+
units = [
42+
(1, "second"),
43+
(60, "minute"),
44+
(60, "hour"),
45+
(24, "day"),
46+
(7, "week"),
47+
(52, "year"),
48+
(10, "decade"),
49+
]
50+
for i in range(len(units) - 1):
51+
increment, suffix = units[i]
52+
next_increment, _ = units[i + 1]
53+
interval /= float(increment)
54+
if interval < next_increment:
55+
break
56+
else:
57+
# Last unit.
58+
increment, suffix = units[-1]
59+
interval /= float(increment)
60+
61+
return f"{interval:3.1f} {suffix}s"

test/test_ui_init.py

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from beets import config, ui
2323
from beets.test import _common
24-
from beets.test.helper import BeetsTestCase, ItemInDBTestCase, control_stdin
24+
from beets.test.helper import BeetsTestCase, control_stdin
2525

2626

2727
class InputMethodsTest(BeetsTestCase):
@@ -88,42 +88,6 @@ def test_input_select_objects(self):
8888
assert items == ["1", "3"]
8989

9090

91-
class InitTest(ItemInDBTestCase):
92-
def test_human_bytes(self):
93-
tests = [
94-
(0, "0.0 B"),
95-
(30, "30.0 B"),
96-
(pow(2, 10), "1.0 KiB"),
97-
(pow(2, 20), "1.0 MiB"),
98-
(pow(2, 30), "1.0 GiB"),
99-
(pow(2, 40), "1.0 TiB"),
100-
(pow(2, 50), "1.0 PiB"),
101-
(pow(2, 60), "1.0 EiB"),
102-
(pow(2, 70), "1.0 ZiB"),
103-
(pow(2, 80), "1.0 YiB"),
104-
(pow(2, 90), "1.0 HiB"),
105-
(pow(2, 100), "big"),
106-
]
107-
for i, h in tests:
108-
assert h == ui.human_bytes(i)
109-
110-
def test_human_seconds(self):
111-
tests = [
112-
(0, "0.0 seconds"),
113-
(30, "30.0 seconds"),
114-
(60, "1.0 minutes"),
115-
(90, "1.5 minutes"),
116-
(125, "2.1 minutes"),
117-
(3600, "1.0 hours"),
118-
(86400, "1.0 days"),
119-
(604800, "1.0 weeks"),
120-
(31449600, "1.0 years"),
121-
(314496000, "1.0 decades"),
122-
]
123-
for i, h in tests:
124-
assert h == ui.human_seconds(i)
125-
126-
12791
class ParentalDirCreation(BeetsTestCase):
12892
def test_create_yes(self):
12993
non_exist_path = _common.os.fsdecode(

test/util/test_units.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import pytest
2+
3+
from beets.util.units import human_bytes, human_seconds
4+
5+
6+
@pytest.mark.parametrize(
7+
"input_bytes,expected",
8+
[
9+
(0, "0.0 B"),
10+
(30, "30.0 B"),
11+
(pow(2, 10), "1.0 KiB"),
12+
(pow(2, 20), "1.0 MiB"),
13+
(pow(2, 30), "1.0 GiB"),
14+
(pow(2, 40), "1.0 TiB"),
15+
(pow(2, 50), "1.0 PiB"),
16+
(pow(2, 60), "1.0 EiB"),
17+
(pow(2, 70), "1.0 ZiB"),
18+
(pow(2, 80), "1.0 YiB"),
19+
(pow(2, 90), "1.0 HiB"),
20+
(pow(2, 100), "big"),
21+
],
22+
)
23+
def test_human_bytes(input_bytes, expected):
24+
assert human_bytes(input_bytes) == expected
25+
26+
27+
@pytest.mark.parametrize(
28+
"input_seconds,expected",
29+
[
30+
(0, "0.0 seconds"),
31+
(30, "30.0 seconds"),
32+
(60, "1.0 minutes"),
33+
(90, "1.5 minutes"),
34+
(125, "2.1 minutes"),
35+
(3600, "1.0 hours"),
36+
(86400, "1.0 days"),
37+
(604800, "1.0 weeks"),
38+
(31449600, "1.0 years"),
39+
(314496000, "1.0 decades"),
40+
],
41+
)
42+
def test_human_seconds(input_seconds, expected):
43+
assert human_seconds(input_seconds) == expected

0 commit comments

Comments
 (0)