Coverage for afasi/tabel.py: 98.18%
58 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-21 13:35:37 +00:00
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-21 13:35:37 +00:00
1# -*- coding: utf-8 -*-
2# pylint: disable=expression-not-assigned,line-too-long
3"""Tabel (Danish for Table). Class API."""
4import json
5import pathlib
6import typing
8import yaml
10ENCODING = 'utf-8'
13class Table:
14 """Translation table."""
16 __slots__ = ['description', 'contra', 'count', 'flip_is_stop', 'flip_flop', 'pro', 'ff_state', 'translations']
18 @typing.no_type_check
19 def __init__(self, **kwargs):
20 """Instance created from dictionary usually stored in a JSON file."""
21 self.ff_state = True
22 for key, value in kwargs.items():
23 if key == 'table':
24 for me, ta in value.items():
25 setattr(self, me, ta)
26 elif key == 'translations':
27 setattr(self, key, [])
28 for entry in value:
29 self.translations.append(Translation(**entry))
30 else:
31 print(f'table ignored ({key=} -> {value=})')
33 @typing.no_type_check
34 def translate(self, text: str) -> str:
35 """Sequenced replacer (WIP)."""
36 if self.flip_flop:
37 for pos, token in enumerate(self.flip_flop, start=1):
38 if token not in text:
39 continue
40 else:
41 if self.flip_is_stop:
42 self.ff_state = False if pos % 2 else True
43 else:
44 self.ff_state = True if pos % 2 else False
46 if self.ff_state:
47 if self.contra and any(stop in text for stop in self.contra):
48 return text
50 if not self.pro or any(start in text for start in self.pro):
51 for rule in self.translations:
52 text = rule.apply(text)
54 return text
56 @typing.no_type_check
57 def __str__(self):
58 """Human readable rendition esp. for debugging."""
59 ff = "'" + "'\n '".join(switch for switch in self.flip_flop) + "'"
60 return (
61 f'table:\n {self.description=}\n'
62 f' {self.contra=}\n'
63 f' {self.count=}\n'
64 f' {self.flip_is_stop=}\n'
65 f' flip_flop:\n {ff}\n'
66 f' {self.count=}\n'
67 f' {self.pro=}\n'
68 f' translations:\n {" ".join(str(translation) for translation in self.translations)}\n'
69 )
72class Translation:
73 """Translation task."""
75 __slots__ = ['repl', 'ace']
77 @typing.no_type_check
78 def __init__(self, **kwargs):
79 """Instance created from dictionary."""
80 for key, value in kwargs.items():
81 setattr(self, key, value)
83 @typing.no_type_check
84 def apply(self, text: str) -> str:
85 """Elementary replacer (WIP)."""
86 return text.replace(self.repl, self.ace)
88 @typing.no_type_check
89 def __str__(self):
90 """Human readable rendition esp. for debugging."""
91 return f'{self.repl=} -> {self.ace=}\n'
94def load_table(path: pathlib.Path) -> typing.Any:
95 """Generate Table instance from YAML or JSON file."""
96 suffix = path.suffix.lower()
97 if suffix == '.json':
98 with open(path, 'rt', encoding=ENCODING) as dump:
99 return Table(**json.load(dump))
100 with open(path, 'rt', encoding=ENCODING) as dump: 100 ↛ 101line 100 didn't jump to line 101 because
101 return Table(**yaml.safe_load(dump))