readalign.rules

 1from dataclasses import dataclass, fields
 2from functools import lru_cache
 3from pathlib import Path
 4
 5import yaml
 6
 7
 8@dataclass(frozen=True)
 9class Rules:
10    match_threshold: float
11    join_floor: float
12    gap_penalty: float
13    mismatch_penalty: float
14    room_enough: float
15    join_span: int
16    vouched_join_span: int
17    english_vowels: str
18    silent_ending: str
19    silent_ending_except_after: str
20    shortest_with_a_silent_ending: int
21    lightest_word: float
22    lifted_marks_from: str
23    lifted_marks_to: str
24    letter_joiners: list[str]
25    folded_letters: dict[str, str]
26    frame_seconds: float
27    room_quantile: float
28    speech_above_room: float
29    quietest_room: float
30    hold_limit: float
31    speech_from_loudest_share: float
32    quietest_speech: float
33    piece_seconds: float
34    shortest_piece_share: float
35    pause_seconds: float
36    edge_overlap: float
37    ask_again_trims: list[float]
38    shortest_worth_asking_again: float
39    uncovered_head_seconds: float
40    partial_answer_overlap: float
41
42    def joins(self, character: str) -> bool:
43        """Whether this mark writes one consonant joined to the next, making them one letter."""
44        return any(int(code, 16) == ord(character) for code in self.letter_joiners)
45
46    def lifts(self, character: str) -> bool:
47        return int(self.lifted_marks_from, 16) <= ord(character) <= int(self.lifted_marks_to, 16)
48
49
50@lru_cache(maxsize=1)
51def rules() -> Rules:
52    text = (Path(__file__).parent / "rules.yaml").read_text(encoding="utf-8")
53    return read(yaml.safe_load(text))
54
55
56class RulesIncompleteError(ValueError):
57    def __init__(self, names: list[str]) -> None:
58        super().__init__("rules.yaml is missing " + ", ".join(names))
59
60
61class RuleNotANumberError(ValueError):
62    def __init__(self, name: str, value: object) -> None:
63        super().__init__(f"rules.yaml: {name} is not a number: {value!r}")
64
65
66def read(written: dict) -> Rules:
67    wanted = {field.name: field.type for field in fields(Rules)}
68    missing = sorted(set(wanted) - set(written))
69    if missing:
70        raise RulesIncompleteError(missing)
71    return Rules(**{name: as_declared(written[name], kind, name) for name, kind in wanted.items()})
72
73
74def as_declared(value: object, kind: object, name: str) -> object:
75    if kind is not float:
76        return value
77    try:
78        return float(value)  # type: ignore[arg-type]
79    except (TypeError, ValueError) as wrong:
80        raise RuleNotANumberError(name, value) from wrong
@dataclass(frozen=True)
class Rules:
 9@dataclass(frozen=True)
10class Rules:
11    match_threshold: float
12    join_floor: float
13    gap_penalty: float
14    mismatch_penalty: float
15    room_enough: float
16    join_span: int
17    vouched_join_span: int
18    english_vowels: str
19    silent_ending: str
20    silent_ending_except_after: str
21    shortest_with_a_silent_ending: int
22    lightest_word: float
23    lifted_marks_from: str
24    lifted_marks_to: str
25    letter_joiners: list[str]
26    folded_letters: dict[str, str]
27    frame_seconds: float
28    room_quantile: float
29    speech_above_room: float
30    quietest_room: float
31    hold_limit: float
32    speech_from_loudest_share: float
33    quietest_speech: float
34    piece_seconds: float
35    shortest_piece_share: float
36    pause_seconds: float
37    edge_overlap: float
38    ask_again_trims: list[float]
39    shortest_worth_asking_again: float
40    uncovered_head_seconds: float
41    partial_answer_overlap: float
42
43    def joins(self, character: str) -> bool:
44        """Whether this mark writes one consonant joined to the next, making them one letter."""
45        return any(int(code, 16) == ord(character) for code in self.letter_joiners)
46
47    def lifts(self, character: str) -> bool:
48        return int(self.lifted_marks_from, 16) <= ord(character) <= int(self.lifted_marks_to, 16)
Rules( match_threshold: float, join_floor: float, gap_penalty: float, mismatch_penalty: float, room_enough: float, join_span: int, vouched_join_span: int, english_vowels: str, silent_ending: str, silent_ending_except_after: str, shortest_with_a_silent_ending: int, lightest_word: float, lifted_marks_from: str, lifted_marks_to: str, letter_joiners: list[str], folded_letters: dict[str, str], frame_seconds: float, room_quantile: float, speech_above_room: float, quietest_room: float, hold_limit: float, speech_from_loudest_share: float, quietest_speech: float, piece_seconds: float, shortest_piece_share: float, pause_seconds: float, edge_overlap: float, ask_again_trims: list[float], shortest_worth_asking_again: float, uncovered_head_seconds: float, partial_answer_overlap: float)
match_threshold: float
join_floor: float
gap_penalty: float
mismatch_penalty: float
room_enough: float
join_span: int
vouched_join_span: int
english_vowels: str
silent_ending: str
silent_ending_except_after: str
shortest_with_a_silent_ending: int
lightest_word: float
lifted_marks_from: str
lifted_marks_to: str
letter_joiners: list[str]
folded_letters: dict[str, str]
frame_seconds: float
room_quantile: float
speech_above_room: float
quietest_room: float
hold_limit: float
speech_from_loudest_share: float
quietest_speech: float
piece_seconds: float
shortest_piece_share: float
pause_seconds: float
edge_overlap: float
ask_again_trims: list[float]
shortest_worth_asking_again: float
uncovered_head_seconds: float
partial_answer_overlap: float
def joins(self, character: str) -> bool:
43    def joins(self, character: str) -> bool:
44        """Whether this mark writes one consonant joined to the next, making them one letter."""
45        return any(int(code, 16) == ord(character) for code in self.letter_joiners)

Whether this mark writes one consonant joined to the next, making them one letter.

def lifts(self, character: str) -> bool:
47    def lifts(self, character: str) -> bool:
48        return int(self.lifted_marks_from, 16) <= ord(character) <= int(self.lifted_marks_to, 16)
@lru_cache(maxsize=1)
def rules() -> Rules:
51@lru_cache(maxsize=1)
52def rules() -> Rules:
53    text = (Path(__file__).parent / "rules.yaml").read_text(encoding="utf-8")
54    return read(yaml.safe_load(text))
class RulesIncompleteError(builtins.ValueError):
57class RulesIncompleteError(ValueError):
58    def __init__(self, names: list[str]) -> None:
59        super().__init__("rules.yaml is missing " + ", ".join(names))

Inappropriate argument value (of correct type).

RulesIncompleteError(names: list[str])
58    def __init__(self, names: list[str]) -> None:
59        super().__init__("rules.yaml is missing " + ", ".join(names))
class RuleNotANumberError(builtins.ValueError):
62class RuleNotANumberError(ValueError):
63    def __init__(self, name: str, value: object) -> None:
64        super().__init__(f"rules.yaml: {name} is not a number: {value!r}")

Inappropriate argument value (of correct type).

RuleNotANumberError(name: str, value: object)
63    def __init__(self, name: str, value: object) -> None:
64        super().__init__(f"rules.yaml: {name} is not a number: {value!r}")
def read(written: dict) -> Rules:
67def read(written: dict) -> Rules:
68    wanted = {field.name: field.type for field in fields(Rules)}
69    missing = sorted(set(wanted) - set(written))
70    if missing:
71        raise RulesIncompleteError(missing)
72    return Rules(**{name: as_declared(written[name], kind, name) for name, kind in wanted.items()})
def as_declared(value: object, kind: object, name: str) -> object:
75def as_declared(value: object, kind: object, name: str) -> object:
76    if kind is not float:
77        return value
78    try:
79        return float(value)  # type: ignore[arg-type]
80    except (TypeError, ValueError) as wrong:
81        raise RuleNotANumberError(name, value) from wrong