Coverage for src/countdown/digits.py: 100%
36 statements
« prev ^ index » next coverage.py v7.11.1, created at 2026-03-27 20:23 -0700
« prev ^ index » next coverage.py v7.11.1, created at 2026-03-27 20:23 -0700
1"""Utilities for generating large digits."""
3from importlib.resources import files
4from itertools import zip_longest
6DIGIT_SIZES = []
7CHARS_BY_SIZE = {}
10def paragraphs(lines):
11 """Return groups of non-blank lines."""
12 group = []
13 for line in lines:
14 if line.strip():
15 group.append(line)
16 elif group:
17 yield group
18 group = []
19 yield group
22def transpose(lines):
23 """Transpose a list of strings (columns become rows)."""
24 return ("".join(column) for column in zip_longest(*lines, fillvalue=" "))
27def center(text, width):
28 """Center text so that each line will have the given width."""
29 return "\n".join(f"{line:^{width}}" for line in text.splitlines())
32def populate_constants():
33 """Populate CHARS_BY_SIZE and DIGIT_SIZES from glyphs.txt."""
34 lines = (
35 files("countdown")
36 .joinpath("glyphs.txt")
37 .read_text(encoding="utf-8")
38 .splitlines()
39 )
40 number_types = list(paragraphs(lines))
41 for group in number_types:
42 columns = transpose(group)
43 numbers = ["\n".join(transpose(p)) for p in paragraphs(columns)]
44 heights = [len(n.splitlines()) for n in numbers]
45 widths = [max(len(line) for line in n.splitlines()) for n in numbers]
46 max_width = max(widths)
47 [height] = set(heights)
48 DIGIT_SIZES.append(height)
49 chars = CHARS_BY_SIZE[height] = {}
50 for digit, text in enumerate(numbers, start=-1):
51 if digit == -1:
52 colon_width = max(len(line) for line in text.splitlines())
53 chars[":"] = (
54 center(text, colon_width + 2) # 2 spaces around :
55 if len(text) > 1
56 else text
57 )
58 else:
59 chars[str(digit)] = center(text, max_width)
60 DIGIT_SIZES.sort(reverse=True)
63populate_constants()