scriptconv 0.0.3a14__tar.gz → 0.0.3a16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/PKG-INFO +1 -1
  2. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/base.py +7 -4
  3. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/registry.py +36 -5
  4. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/version.py +1 -1
  5. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv.egg-info/PKG-INFO +1 -1
  6. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_phonemizers_base.py +42 -5
  7. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/LICENSE +0 -0
  8. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/README.md +0 -0
  9. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/pyproject.toml +0 -0
  10. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/requirements.txt +0 -0
  11. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/__init__.py +0 -0
  12. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/__main__.py +0 -0
  13. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/cangjie.py +0 -0
  14. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/conventions.py +0 -0
  15. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/data/__init__.py +0 -0
  16. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/data/cangjie5_tc.tsv.gz +0 -0
  17. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/graph.py +0 -0
  18. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/notation.py +0 -0
  19. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/__init__.py +0 -0
  20. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/__init__.py +0 -0
  21. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/bw2ipa.py +0 -0
  22. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/hangul2ipa.py +0 -0
  23. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/aspiration.csv +0 -0
  24. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/assimilation.csv +0 -0
  25. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/double_coda.csv +0 -0
  26. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv +0 -0
  27. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv +0 -0
  28. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/neutralization.csv +0 -0
  29. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/tensification.csv +0 -0
  30. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv +0 -0
  31. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/__init__.py +0 -0
  32. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py +0 -0
  33. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/diacritize.py +0 -0
  34. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/dialectal.py +0 -0
  35. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py +0 -0
  36. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/espeak.py +0 -0
  37. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/frontend.py +0 -0
  38. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py +0 -0
  39. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/normalize.py +0 -0
  40. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/shami/phoneme_inventory.py +0 -0
  41. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/_thirdparty/zh_num.py +0 -0
  42. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/ar.py +0 -0
  43. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/en.py +0 -0
  44. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/enums.py +0 -0
  45. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/eu.py +0 -0
  46. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/fa.py +0 -0
  47. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/gl.py +0 -0
  48. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/he.py +0 -0
  49. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/ja.py +0 -0
  50. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/ko.py +0 -0
  51. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/mul.py +0 -0
  52. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/mwl.py +0 -0
  53. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/o2ipa.py +0 -0
  54. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/pt.py +0 -0
  55. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/shami.py +0 -0
  56. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/vi.py +0 -0
  57. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/phonemizers/zh.py +0 -0
  58. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/py.typed +0 -0
  59. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/readings.py +0 -0
  60. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/scripts.py +0 -0
  61. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv/translit.py +0 -0
  62. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv.egg-info/SOURCES.txt +0 -0
  63. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv.egg-info/dependency_links.txt +0 -0
  64. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv.egg-info/requires.txt +0 -0
  65. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/scriptconv.egg-info/top_level.txt +0 -0
  66. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/setup.cfg +0 -0
  67. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_arpa_stress.py +0 -0
  68. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_cangjie.py +0 -0
  69. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_cli.py +0 -0
  70. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_conventions.py +0 -0
  71. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_errors_policy.py +0 -0
  72. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_examples.py +0 -0
  73. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_graph.py +0 -0
  74. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_notation.py +0 -0
  75. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_phonemizers_cjk_ar.py +0 -0
  76. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_readings.py +0 -0
  77. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_readings_zh.py +0 -0
  78. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_scripts.py +0 -0
  79. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_scripts_stressonnx_compat.py +0 -0
  80. {scriptconv-0.0.3a14 → scriptconv-0.0.3a16}/tests/test_translit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptconv
3
- Version: 0.0.3a14
3
+ Version: 0.0.3a16
4
4
  Summary: Zero-dependency script & phoneme-notation core — ISO-15924 detection & metadata, IPA↔ARPABET/X-SAMPA/Lexique/Kirshenbaum/Cotovía/RFE, Buckwalter↔Arabic, Hangul→jamo, kana
5
5
  License: Apache-2.0
6
6
  Project-URL: Homepage, https://github.com/TigreGotico/scriptconv
@@ -45,8 +45,9 @@ class BasePhonemizer(metaclass=abc.ABCMeta):
45
45
  # optional (text, lang) -> str hook run before chunking; see module
46
46
  # docstring — scriptconv performs no normalization of its own
47
47
  self.normalizer = normalizer
48
- # local path to a phonikud ONNX model (Hebrew diacritization);
49
- # scriptconv never downloads the consumer resolves the file
48
+ # local path to a phonikud ONNX model (Hebrew diacritization), or a
49
+ # zero-arg callable resolving one lazily; scriptconv never downloads
50
+ # — the consumer resolves the file
50
51
  self.phonikud_model = phonikud_model
51
52
 
52
53
  # diacritizer model name, for languages that need one. Arabic uses
@@ -59,7 +60,9 @@ class BasePhonemizer(metaclass=abc.ABCMeta):
59
60
  @property
60
61
  def phonikud(self):
61
62
  if self._phonikud is None:
62
- if not self.phonikud_model:
63
+ model = self.phonikud_model() if callable(self.phonikud_model) \
64
+ else self.phonikud_model
65
+ if not model:
63
66
  raise ValueError(
64
67
  "Hebrew diacritization needs a local phonikud ONNX model: "
65
68
  "pass phonikud_model=<path> (scriptconv never downloads "
@@ -70,7 +73,7 @@ class BasePhonemizer(metaclass=abc.ABCMeta):
70
73
  raise ImportError(
71
74
  "Hebrew diacritization needs phonikud-onnx — install "
72
75
  "with `pip install scriptconv[he]`") from None
73
- self._phonikud = Phonikud(self.phonikud_model)
76
+ self._phonikud = Phonikud(model)
74
77
  return self._phonikud
75
78
 
76
79
  def tashkeel(self, model: Optional[str] = None):
@@ -133,30 +133,61 @@ _EMITS: Dict[Phonemizer, Tuple[Alphabet, ...]] = {
133
133
  }
134
134
 
135
135
  LANG_DEFAULTS: Dict[str, Tuple[Phonemizer, ...]] = {
136
- # Arabic IPA always goes through arbtok (hard org rule)
136
+ # Arabic IPA always goes through arbtok (hard org rule — never falls
137
+ # through to orthography2ipa or espeak)
137
138
  "ar": (_P.ARBTOK,),
138
139
  "eu": (_P.EUSKAPHONE,),
139
140
  "mwl": (_P.MIRANDESE,),
140
141
  "pt": (_P.TUGAPHONE,),
141
142
  "he": (_P.PHONIKUD,),
142
- "gl": (_P.COTOVIA, _P.ESPEAK),
143
+ "gl": (_P.COTOVIA, _P.ORTHOGRAPHY2IPA, _P.ESPEAK),
143
144
  }
144
145
 
146
+ # Languages whose explicit entry is exhaustive: no generic fallback beyond it.
147
+ _NO_GENERIC_FALLBACK = {"ar"}
148
+
149
+
150
+ def _o2i_supports(lang: str) -> bool:
151
+ """True when orthography2ipa is installed and has a spec for *lang*."""
152
+ try:
153
+ from scriptconv.phonemizers.o2ipa import Orthography2IPAPhonemizer
154
+ Orthography2IPAPhonemizer.get_lang(lang)
155
+ return True
156
+ except (ImportError, ValueError):
157
+ return False
158
+
145
159
 
146
160
  def phonemizer_for_lang(lang: str, alphabet: Alphabet = Alphabet.IPA,
147
161
  override: Optional[Phonemizer] = None,
148
162
  model: Optional[str] = None, **kwargs):
149
163
  """Construct the default phonemizer for *lang* (or the override).
150
164
 
151
- Candidates from :data:`LANG_DEFAULTS` are filtered by the alphabet they
152
- can emit; espeak is the universal fallback.
165
+ Resolution order in-house engines first:
166
+
167
+ 1. ``override=`` when given;
168
+ 2. the language's explicit :data:`LANG_DEFAULTS` chain, filtered by the
169
+ alphabet each candidate can emit;
170
+ 3. :data:`~Phonemizer.ORTHOGRAPHY2IPA` when orthography2ipa supports the
171
+ language (the usual default);
172
+ 4. espeak as the last resort.
173
+
174
+ Languages in the no-fallback set (Arabic) never leave their explicit
175
+ chain — a missing arbtok raises rather than silently degrading.
153
176
  """
154
177
  if override is not None:
155
178
  return get_phonemizer(override, alphabet, model, **kwargs)
156
179
  key = lang.replace("_", "-").split("-")[0].lower()
157
- for candidate in LANG_DEFAULTS.get(key, ()):
180
+ explicit = LANG_DEFAULTS.get(key, ())
181
+ for candidate in explicit:
158
182
  if alphabet in _EMITS.get(candidate, (Alphabet.IPA,)):
159
183
  return get_phonemizer(candidate, alphabet, model, **kwargs)
184
+ if key in _NO_GENERIC_FALLBACK:
185
+ raise ValueError(
186
+ f"no eligible phonemizer for {lang!r} with alphabet "
187
+ f"{alphabet.value!r}: the explicit chain "
188
+ f"{[c.value for c in explicit]} is exhaustive for this language")
189
+ if alphabet == Alphabet.IPA and _o2i_supports(lang):
190
+ return get_phonemizer(_P.ORTHOGRAPHY2IPA, alphabet, model, **kwargs)
160
191
  return get_phonemizer(_P.ESPEAK, alphabet, model, **kwargs)
161
192
 
162
193
 
@@ -2,7 +2,7 @@
2
2
  VERSION_MAJOR = 0
3
3
  VERSION_MINOR = 0
4
4
  VERSION_BUILD = 3
5
- VERSION_ALPHA = 14
5
+ VERSION_ALPHA = 16
6
6
  # END_VERSION_BLOCK
7
7
 
8
8
  VERSION_STR = f"{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_BUILD}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptconv
3
- Version: 0.0.3a14
3
+ Version: 0.0.3a16
4
4
  Summary: Zero-dependency script & phoneme-notation core — ISO-15924 detection & metadata, IPA↔ARPABET/X-SAMPA/Lexique/Kirshenbaum/Cotovía/RFE, Buckwalter↔Arabic, Hangul→jamo, kana
5
5
  License: Apache-2.0
6
6
  Project-URL: Homepage, https://github.com/TigreGotico/scriptconv
@@ -96,12 +96,34 @@ class TestLangDefaults(unittest.TestCase):
96
96
  self.assertEqual(LANG_DEFAULTS["ar"], (Phonemizer.ARBTOK,))
97
97
 
98
98
  def test_cotovia_only_for_cotovia_alphabet(self):
99
+ # requesting IPA must never yield Cotovía (it emits its own notation);
100
+ # the in-house chain falls through to orthography2ipa (or espeak)
99
101
  gl_ipa = phonemizer_for_lang("gl", alphabet=Alphabet.IPA)
100
- self.assertEqual(type(gl_ipa).__name__, "EspeakPhonemizer")
101
-
102
- def test_fallback_is_espeak(self):
103
- de = phonemizer_for_lang("de-DE")
104
- self.assertEqual(type(de).__name__, "EspeakPhonemizer")
102
+ self.assertIn(type(gl_ipa).__name__,
103
+ ("Orthography2IPAPhonemizer", "EspeakPhonemizer"))
104
+ self.assertNotEqual(type(gl_ipa).__name__, "CotoviaPhonemizer")
105
+ gl_ctv = phonemizer_for_lang("gl", alphabet=Alphabet.COTOVIA)
106
+ self.assertEqual(type(gl_ctv).__name__, "CotoviaPhonemizer")
107
+
108
+ def test_fallback_prefers_o2i_then_espeak(self):
109
+ # in-house first: orthography2ipa is the usual default when it has a
110
+ # spec for the language; espeak only as last resort
111
+ from unittest import mock
112
+ import scriptconv.phonemizers.registry as reg
113
+ with mock.patch.object(reg, "_o2i_supports", return_value=True):
114
+ with mock.patch.object(reg, "get_phonemizer") as gp:
115
+ reg.phonemizer_for_lang("de-DE")
116
+ self.assertEqual(gp.call_args[0][0], Phonemizer.ORTHOGRAPHY2IPA)
117
+ with mock.patch.object(reg, "_o2i_supports", return_value=False):
118
+ de = reg.phonemizer_for_lang("de-DE")
119
+ self.assertEqual(type(de).__name__, "EspeakPhonemizer")
120
+
121
+ def test_arabic_never_falls_through(self):
122
+ # hard org rule: Arabic IPA only via arbtok — with arbtok ineligible
123
+ # (wrong alphabet), the resolver raises instead of degrading to
124
+ # o2i/espeak
125
+ with self.assertRaises((ValueError, ImportError)):
126
+ phonemizer_for_lang("ar", alphabet=Alphabet.ARPA)
105
127
 
106
128
  def test_override_wins(self):
107
129
  g = phonemizer_for_lang("de", override=Phonemizer.GRAPHEMES)
@@ -159,3 +181,18 @@ class TestFacadeAndGraph(unittest.TestCase):
159
181
  from scriptconv import phonemizers
160
182
  g = DEFAULT_GRAPH.extend(phonemizers.register)
161
183
  self.assertTrue(g.can_convert("text", "arpa"))
184
+
185
+
186
+ class TestPhonikudModelResolver(unittest.TestCase):
187
+ def test_callable_resolver_invoked_lazily(self):
188
+ calls = []
189
+
190
+ def resolver():
191
+ calls.append(1)
192
+ return "" # resolves to nothing -> still the explicit ValueError
193
+
194
+ g = GraphemePhonemizer(phonikud_model=resolver)
195
+ self.assertEqual(calls, []) # not resolved at construction
196
+ with self.assertRaises(ValueError):
197
+ g.add_diacritics("שלום", "he")
198
+ self.assertEqual(calls, [1])
File without changes
File without changes
File without changes