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)
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.
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).
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).
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: