scriptconv 0.0.4a6__tar.gz → 0.0.4a8__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.
- {scriptconv-0.0.4a6/scriptconv.egg-info → scriptconv-0.0.4a8}/PKG-INFO +8 -4
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/README.md +7 -3
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/diacritics.py +80 -24
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/registry.py +7 -3
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/version.py +1 -1
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8/scriptconv.egg-info}/PKG-INFO +8 -4
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_diacritics.py +106 -11
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_diacritics_graph.py +46 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/LICENSE +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/pyproject.toml +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/requirements.txt +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/__main__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/cangjie.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/conventions.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/data/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/data/cangjie5_tc.tsv.gz +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/graph.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/notation.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/bw2ipa.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/hangul2ipa.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/aspiration.csv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/assimilation.csv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/double_coda.csv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/neutralization.csv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/tensification.csv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/diacritize.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/dialectal.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/espeak.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/frontend.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/normalize.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/phoneme_inventory.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/zh_num.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/kog2p/LICENSE.md +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/kog2p/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/kog2p/rulebook.txt +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/LICENSE.md +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/buck/__init__.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/buck/phonetise_buckwalter.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/buck/symbols.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/buck/tokenization.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/num2words.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/unicode_symbol2label.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/ar.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/base.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/en.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/enums.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/eu.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/fa.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/gl.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/he.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/ja.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/ko.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/mul.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/mwl.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/o2ipa.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/pt.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/shami.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/vi.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/zh.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/py.typed +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/readings.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/scripts.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/translit.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv.egg-info/SOURCES.txt +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv.egg-info/dependency_links.txt +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv.egg-info/requires.txt +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv.egg-info/top_level.txt +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/setup.cfg +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_arpa_stress.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_cangjie.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_cli.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_conventions.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_errors_policy.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_examples.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_graph.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_notation.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_phonemizers_base.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_phonemizers_cjk_ar.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_readings.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_readings_zh.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_scripts.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_scripts_stressonnx_compat.py +0 -0
- {scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/tests/test_translit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4a8
|
|
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
|
|
@@ -373,9 +373,13 @@ Two design points worth knowing:
|
|
|
373
373
|
phonemizing; that needs language resources scriptconv doesn't ship. Pass
|
|
374
374
|
`normalizer=` (a `(text, lang) -> str` callable) to run yours inside the
|
|
375
375
|
pipeline; without it, text is phonemized as-is.
|
|
376
|
-
- **
|
|
377
|
-
local `model=` path; resolving and caching model
|
|
378
|
-
concern.
|
|
376
|
+
- **Large/licensed model-backed engines never download.** ByT5/Charsiu
|
|
377
|
+
require an explicit local `model=` path; resolving and caching those model
|
|
378
|
+
files is the caller's concern. Small, known-good, unencumbered models are
|
|
379
|
+
the exception: the Hebrew phonikud diacritizer auto-provisions its ONNX
|
|
380
|
+
model to a cache dir on first use (`phonikud_model=` still overrides it
|
|
381
|
+
with a path or callable; cache location via `SCRIPTCONV_CACHE`/
|
|
382
|
+
`XDG_CACHE_HOME`).
|
|
379
383
|
|
|
380
384
|
**Pre-G2P disambiguation.** `add_diacritics(text, lang, model=None)` restores
|
|
381
385
|
information ordinary orthography omits but a G2P needs, before phonemization:
|
|
@@ -246,9 +246,13 @@ Two design points worth knowing:
|
|
|
246
246
|
phonemizing; that needs language resources scriptconv doesn't ship. Pass
|
|
247
247
|
`normalizer=` (a `(text, lang) -> str` callable) to run yours inside the
|
|
248
248
|
pipeline; without it, text is phonemized as-is.
|
|
249
|
-
- **
|
|
250
|
-
local `model=` path; resolving and caching model
|
|
251
|
-
concern.
|
|
249
|
+
- **Large/licensed model-backed engines never download.** ByT5/Charsiu
|
|
250
|
+
require an explicit local `model=` path; resolving and caching those model
|
|
251
|
+
files is the caller's concern. Small, known-good, unencumbered models are
|
|
252
|
+
the exception: the Hebrew phonikud diacritizer auto-provisions its ONNX
|
|
253
|
+
model to a cache dir on first use (`phonikud_model=` still overrides it
|
|
254
|
+
with a path or callable; cache location via `SCRIPTCONV_CACHE`/
|
|
255
|
+
`XDG_CACHE_HOME`).
|
|
252
256
|
|
|
253
257
|
**Pre-G2P disambiguation.** `add_diacritics(text, lang, model=None)` restores
|
|
254
258
|
information ordinary orthography omits but a G2P needs, before phonemization:
|
|
@@ -51,6 +51,10 @@ tracks which backend/model produced the marks: overlay backends (phonikud,
|
|
|
51
51
|
tashkeel, stressonnx) are strippable; spelling-integral backends (bifonia) are
|
|
52
52
|
not.
|
|
53
53
|
"""
|
|
54
|
+
import os
|
|
55
|
+
import tempfile
|
|
56
|
+
import urllib.request
|
|
57
|
+
from pathlib import Path
|
|
54
58
|
from typing import Optional
|
|
55
59
|
|
|
56
60
|
from scriptconv.graph import Edge
|
|
@@ -119,20 +123,61 @@ _DEFAULT_DIACRITIZER_MODEL = "rawi-ensemble"
|
|
|
119
123
|
_PHONIKUD_CACHE: dict = {} # resolved model path/string -> Phonikud instance
|
|
120
124
|
_TASHKEEL_CACHE: dict = {} # model name -> text2tashkeel Diacritizer
|
|
121
125
|
|
|
126
|
+
# phonikud is a small (~100MB), public, unlicensed-restriction ONNX model, so
|
|
127
|
+
# — unlike the large/licensed models behind mul.py's ByT5/Charsiu backends —
|
|
128
|
+
# scriptconv auto-provisions it: no local path is required unless the caller
|
|
129
|
+
# wants to override the cache (e.g. an air-gapped host).
|
|
130
|
+
_PHONIKUD_URL = "https://huggingface.co/thewh1teagle/phonikud-onnx/resolve/main/phonikud-1.0.int8.onnx"
|
|
122
131
|
|
|
123
|
-
|
|
132
|
+
|
|
133
|
+
def _default_phonikud_model() -> str:
|
|
134
|
+
"""Resolve (downloading and caching on first use if needed) the path to
|
|
135
|
+
the default phonikud ONNX model.
|
|
136
|
+
|
|
137
|
+
Cache directory: ``<base>/scriptconv/phonikud`` where ``<base>`` is
|
|
138
|
+
``$SCRIPTCONV_CACHE`` if set, else ``$XDG_CACHE_HOME`` (default
|
|
139
|
+
``~/.cache``) — i.e. ``~/.cache/scriptconv/phonikud`` by default. The
|
|
140
|
+
download is written to a temp file
|
|
141
|
+
in the same directory and atomically moved into place via
|
|
142
|
+
:func:`os.replace`, so a failed or interrupted download never leaves a
|
|
143
|
+
partial file at the destination path.
|
|
144
|
+
"""
|
|
145
|
+
base = os.environ.get("SCRIPTCONV_CACHE") or os.environ.get(
|
|
146
|
+
"XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
|
|
147
|
+
cache_dir = Path(base) / "scriptconv" / "phonikud"
|
|
148
|
+
dest = cache_dir / "phonikud-1.0.int8.onnx"
|
|
149
|
+
if not dest.is_file():
|
|
150
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
151
|
+
fd, tmp_path = tempfile.mkstemp(dir=str(cache_dir), prefix=".phonikud-", suffix=".tmp")
|
|
152
|
+
try:
|
|
153
|
+
with os.fdopen(fd, "wb") as tmp_f, urllib.request.urlopen(_PHONIKUD_URL) as resp:
|
|
154
|
+
while True:
|
|
155
|
+
chunk = resp.read(1024 * 1024)
|
|
156
|
+
if not chunk:
|
|
157
|
+
break
|
|
158
|
+
tmp_f.write(chunk)
|
|
159
|
+
os.replace(tmp_path, dest)
|
|
160
|
+
except BaseException:
|
|
161
|
+
if os.path.exists(tmp_path):
|
|
162
|
+
os.remove(tmp_path)
|
|
163
|
+
raise
|
|
164
|
+
return str(dest)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _phonikud(phonikud_model=None):
|
|
124
168
|
"""Lazily build (and cache) the phonikud Phonikud instance used for Hebrew.
|
|
125
169
|
|
|
126
|
-
``phonikud_model`` is a local path to a phonikud ONNX model, or
|
|
127
|
-
zero-arg callable resolving one lazily
|
|
128
|
-
the
|
|
129
|
-
|
|
170
|
+
``phonikud_model`` is optional: a local path to a phonikud ONNX model, or
|
|
171
|
+
a zero-arg callable resolving one lazily. When omitted, scriptconv
|
|
172
|
+
auto-provisions the small public phonikud model, downloading it once into
|
|
173
|
+
a cache dir (see :func:`_default_phonikud_model`) and reusing it on
|
|
174
|
+
subsequent calls. Pass an explicit path/callable to override — e.g. to
|
|
175
|
+
point at a model already on disk, or on an air-gapped host. Install
|
|
176
|
+
phonikud-onnx with ``pip install scriptconv[he]`` (or ``pip install
|
|
177
|
+
phonikud-onnx``)."""
|
|
130
178
|
model = phonikud_model() if callable(phonikud_model) else phonikud_model
|
|
131
179
|
if not model:
|
|
132
|
-
|
|
133
|
-
"Hebrew diacritization needs a local phonikud ONNX model: "
|
|
134
|
-
"pass phonikud_model=<path> (scriptconv never downloads "
|
|
135
|
-
"models; obtain one from the phonikud-onnx release)")
|
|
180
|
+
model = _default_phonikud_model()
|
|
136
181
|
if model not in _PHONIKUD_CACHE:
|
|
137
182
|
try:
|
|
138
183
|
from phonikud_onnx import Phonikud
|
|
@@ -200,21 +245,27 @@ def _sense_diacritics_pt(text: str) -> str:
|
|
|
200
245
|
return add_extra_diacritics(text)
|
|
201
246
|
|
|
202
247
|
|
|
203
|
-
def diacritize(text: str, lang: str = "und",
|
|
204
|
-
|
|
248
|
+
def diacritize(text: str, lang: str = "und", diacritizer_model=None,
|
|
249
|
+
**kwargs) -> str:
|
|
205
250
|
"""Add pronunciation-disambiguating diacritics to *text* for *lang*.
|
|
206
251
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
-
|
|
212
|
-
|
|
252
|
+
``diacritizer_model=`` is the one model knob for the diacritizer edge — the
|
|
253
|
+
model for whichever backend the language routes to (there is one such knob,
|
|
254
|
+
parallel to ``phonemizer_model=`` on the ``text -> ipa`` phonemizer edge):
|
|
255
|
+
|
|
256
|
+
- Hebrew (``he``) — niqqud via phonikud; ``diacritizer_model`` is the
|
|
257
|
+
phonikud ONNX path (or a zero-arg callable). Optional: when omitted the
|
|
258
|
+
small public phonikud model is auto-downloaded and cached
|
|
259
|
+
(``$SCRIPTCONV_CACHE``/``$XDG_CACHE_HOME``).
|
|
260
|
+
- Arabic (``ar``) — tashkeel via text2tashkeel (``[tashkeel]``);
|
|
261
|
+
``diacritizer_model`` is the text2tashkeel model name (defaults to
|
|
262
|
+
``rawi-ensemble``).
|
|
213
263
|
- East Slavic, Bulgarian/Macedonian/Slovene, Latvian, Armenian, Georgian,
|
|
214
264
|
and Turkic/Caucasian languages (``STRESS_LANGS``, 26 stressonnx tags) —
|
|
215
|
-
word stress via stressonnx (``[stress]``);
|
|
216
|
-
under-marked in these languages,
|
|
217
|
-
also reduce, so a missing mark can
|
|
265
|
+
word stress via stressonnx (``[stress]``); ``diacritizer_model`` is the
|
|
266
|
+
stressonnx model. Stress is unwritten or under-marked in these languages,
|
|
267
|
+
and in East Slavic unstressed vowels also reduce, so a missing mark can
|
|
268
|
+
corrupt more than prosody.
|
|
218
269
|
- European Portuguese (``pt``/``pt-PT``, never ``pt-BR``) —
|
|
219
270
|
heterophonic-homograph sense diacritics via bifonia (``[pt]``);
|
|
220
271
|
ordinary Portuguese orthographic marks that any downstream G2P reads
|
|
@@ -224,13 +275,17 @@ def diacritize(text: str, lang: str = "und", model=None,
|
|
|
224
275
|
``ImportError`` naming its extra when the optional dependency is missing
|
|
225
276
|
— scriptconv never installs anything on the caller's behalf.
|
|
226
277
|
"""
|
|
278
|
+
# one knob for the diacritizer edge; fold the pre-unification kwargs so
|
|
279
|
+
# older callers keep working (phonikud_model was the Hebrew-only name).
|
|
280
|
+
diacritizer_model = (diacritizer_model or kwargs.get("phonikud_model")
|
|
281
|
+
or kwargs.get("model"))
|
|
227
282
|
family = _diacritizer_family(lang)
|
|
228
283
|
if family == "he":
|
|
229
|
-
return _phonikud(
|
|
284
|
+
return _phonikud(diacritizer_model).add_diacritics(text)
|
|
230
285
|
if family == "ar":
|
|
231
|
-
return _tashkeel(
|
|
286
|
+
return _tashkeel(diacritizer_model).diacritize(text)
|
|
232
287
|
if family == "stress":
|
|
233
|
-
return _stress(text, lang,
|
|
288
|
+
return _stress(text, lang, diacritizer_model)
|
|
234
289
|
if family == "pt":
|
|
235
290
|
return _sense_diacritics_pt(text)
|
|
236
291
|
return text
|
|
@@ -311,7 +366,8 @@ def register(graph) -> None:
|
|
|
311
366
|
"""
|
|
312
367
|
graph.register(
|
|
313
368
|
Edge("text", DIACRITIZED,
|
|
314
|
-
lambda text, lang="und",
|
|
369
|
+
lambda text, lang="und", diacritizer_model=None, **c:
|
|
370
|
+
diacritize(text, lang, diacritizer_model=diacritizer_model, **c),
|
|
315
371
|
lossless=False))
|
|
316
372
|
graph.register(
|
|
317
373
|
Edge(DIACRITIZED, "text",
|
|
@@ -231,9 +231,13 @@ def register(graph) -> None:
|
|
|
231
231
|
from scriptconv.graph import Edge
|
|
232
232
|
|
|
233
233
|
def _text_to_ipa(text: str, lang: str = "und",
|
|
234
|
-
override: Optional[Phonemizer] = None,
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
override: Optional[Phonemizer] = None,
|
|
235
|
+
phonemizer_model: Optional[str] = None, **_):
|
|
236
|
+
# phonemizer_model is the model knob for this (phonemizer) edge —
|
|
237
|
+
# forwarded to model-backed phonemizers (ByT5/Charsiu/…); parallel to
|
|
238
|
+
# diacritizer_model on the text -> text-diacritized edge.
|
|
239
|
+
return phonemizer_for_lang(lang, Alphabet.IPA, override,
|
|
240
|
+
phonemizer_model).phonemize_string(text, lang)
|
|
237
241
|
|
|
238
242
|
graph.register(Edge("text", "ipa", _text_to_ipa, lossless=False))
|
|
239
243
|
graph.register(Edge("text-diacritized", "ipa", _text_to_ipa, lossless=False))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4a8
|
|
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
|
|
@@ -373,9 +373,13 @@ Two design points worth knowing:
|
|
|
373
373
|
phonemizing; that needs language resources scriptconv doesn't ship. Pass
|
|
374
374
|
`normalizer=` (a `(text, lang) -> str` callable) to run yours inside the
|
|
375
375
|
pipeline; without it, text is phonemized as-is.
|
|
376
|
-
- **
|
|
377
|
-
local `model=` path; resolving and caching model
|
|
378
|
-
concern.
|
|
376
|
+
- **Large/licensed model-backed engines never download.** ByT5/Charsiu
|
|
377
|
+
require an explicit local `model=` path; resolving and caching those model
|
|
378
|
+
files is the caller's concern. Small, known-good, unencumbered models are
|
|
379
|
+
the exception: the Hebrew phonikud diacritizer auto-provisions its ONNX
|
|
380
|
+
model to a cache dir on first use (`phonikud_model=` still overrides it
|
|
381
|
+
with a path or callable; cache location via `SCRIPTCONV_CACHE`/
|
|
382
|
+
`XDG_CACHE_HOME`).
|
|
379
383
|
|
|
380
384
|
**Pre-G2P disambiguation.** `add_diacritics(text, lang, model=None)` restores
|
|
381
385
|
information ordinary orthography omits but a G2P needs, before phonemization:
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
No optional backend is required — strip_diacritics/_overlay_marks/_supports_strip
|
|
4
4
|
never load a phonemizer model.
|
|
5
5
|
"""
|
|
6
|
+
import os
|
|
7
|
+
|
|
6
8
|
import pytest
|
|
7
9
|
|
|
8
10
|
from scriptconv.diacritics import (
|
|
@@ -90,27 +92,120 @@ def test_overlay_marks_none_for_native_orthography():
|
|
|
90
92
|
assert _overlay_marks("en") is None
|
|
91
93
|
|
|
92
94
|
|
|
93
|
-
class
|
|
94
|
-
def
|
|
95
|
-
from scriptconv
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
class TestHebrewDiacritizerAutoProvisions:
|
|
96
|
+
def test_no_model_given_auto_provisions(self, monkeypatch):
|
|
97
|
+
from scriptconv import diacritics
|
|
98
|
+
|
|
99
|
+
monkeypatch.setitem(diacritics._PHONIKUD_CACHE, "/sentinel/path.onnx", object())
|
|
100
|
+
monkeypatch.setattr(diacritics, "_default_phonikud_model",
|
|
101
|
+
lambda: "/sentinel/path.onnx")
|
|
102
|
+
result = diacritics._phonikud(None)
|
|
103
|
+
assert result is diacritics._PHONIKUD_CACHE["/sentinel/path.onnx"]
|
|
104
|
+
|
|
105
|
+
def test_explicit_path_bypasses_provisioner(self, monkeypatch):
|
|
106
|
+
from scriptconv import diacritics
|
|
107
|
+
|
|
108
|
+
calls = []
|
|
109
|
+
monkeypatch.setattr(diacritics, "_default_phonikud_model",
|
|
110
|
+
lambda: calls.append(1) or "/should/not/be/used.onnx")
|
|
111
|
+
|
|
112
|
+
class FakePhonikud:
|
|
113
|
+
def __init__(self, model):
|
|
114
|
+
self.model = model
|
|
115
|
+
|
|
116
|
+
monkeypatch.setattr(diacritics, "_PHONIKUD_CACHE", {})
|
|
117
|
+
import sys
|
|
118
|
+
import types
|
|
119
|
+
fake_mod = types.ModuleType("phonikud_onnx")
|
|
120
|
+
fake_mod.Phonikud = FakePhonikud
|
|
121
|
+
monkeypatch.setitem(sys.modules, "phonikud_onnx", fake_mod)
|
|
122
|
+
|
|
123
|
+
result = diacritics._phonikud("/explicit/path.onnx")
|
|
124
|
+
assert result.model == "/explicit/path.onnx"
|
|
125
|
+
assert calls == [] # provisioner never invoked
|
|
99
126
|
|
|
100
127
|
|
|
101
128
|
class TestPhonikudModelResolver:
|
|
102
|
-
def test_callable_resolver_invoked_lazily(self):
|
|
103
|
-
from scriptconv
|
|
129
|
+
def test_callable_resolver_invoked_lazily(self, monkeypatch):
|
|
130
|
+
from scriptconv import diacritics
|
|
131
|
+
|
|
104
132
|
calls = []
|
|
105
133
|
|
|
106
134
|
def resolver():
|
|
107
135
|
calls.append(1)
|
|
108
|
-
return ""
|
|
136
|
+
return "/from/callable.onnx"
|
|
137
|
+
|
|
138
|
+
class FakePhonikud:
|
|
139
|
+
def __init__(self, model):
|
|
140
|
+
self.model = model
|
|
141
|
+
|
|
142
|
+
monkeypatch.setattr(diacritics, "_PHONIKUD_CACHE", {})
|
|
143
|
+
import sys
|
|
144
|
+
import types
|
|
145
|
+
fake_mod = types.ModuleType("phonikud_onnx")
|
|
146
|
+
fake_mod.Phonikud = FakePhonikud
|
|
147
|
+
monkeypatch.setitem(sys.modules, "phonikud_onnx", fake_mod)
|
|
109
148
|
|
|
110
149
|
assert calls == [] # not resolved at construction
|
|
111
|
-
|
|
112
|
-
diacritize("שלום", "he", phonikud_model=resolver)
|
|
150
|
+
result = diacritics._phonikud(resolver)
|
|
113
151
|
assert calls == [1]
|
|
152
|
+
assert result.model == "/from/callable.onnx"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class TestDefaultPhonikudModelProvisioning:
|
|
156
|
+
def test_downloads_to_cache_dir_and_returns_path(self, monkeypatch, tmp_path):
|
|
157
|
+
from scriptconv import diacritics
|
|
158
|
+
|
|
159
|
+
monkeypatch.setenv("SCRIPTCONV_CACHE", str(tmp_path))
|
|
160
|
+
calls = []
|
|
161
|
+
|
|
162
|
+
class FakeResponse:
|
|
163
|
+
def __enter__(self):
|
|
164
|
+
return self
|
|
165
|
+
|
|
166
|
+
def __exit__(self, *a):
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
def read(self, n):
|
|
170
|
+
if calls and calls[-1] == "read-done":
|
|
171
|
+
return b""
|
|
172
|
+
calls.append("read-done")
|
|
173
|
+
return b"dummy-onnx-bytes"
|
|
174
|
+
|
|
175
|
+
def fake_urlopen(url):
|
|
176
|
+
calls.append(("urlopen", url))
|
|
177
|
+
return FakeResponse()
|
|
178
|
+
|
|
179
|
+
monkeypatch.setattr(diacritics.urllib.request, "urlopen", fake_urlopen)
|
|
180
|
+
|
|
181
|
+
path = diacritics._default_phonikud_model()
|
|
182
|
+
assert path == str(tmp_path / "scriptconv" / "phonikud" / "phonikud-1.0.int8.onnx")
|
|
183
|
+
assert os.path.isfile(path)
|
|
184
|
+
|
|
185
|
+
# idempotent: second call must not re-download
|
|
186
|
+
urlopen_calls_before = sum(1 for c in calls if isinstance(c, tuple))
|
|
187
|
+
diacritics._default_phonikud_model()
|
|
188
|
+
urlopen_calls_after = sum(1 for c in calls if isinstance(c, tuple))
|
|
189
|
+
assert urlopen_calls_before == urlopen_calls_after == 1
|
|
190
|
+
|
|
191
|
+
def test_failed_download_leaves_no_partial_file(self, monkeypatch, tmp_path):
|
|
192
|
+
from scriptconv import diacritics
|
|
193
|
+
|
|
194
|
+
monkeypatch.setenv("SCRIPTCONV_CACHE", str(tmp_path))
|
|
195
|
+
|
|
196
|
+
def failing_urlopen(url):
|
|
197
|
+
raise OSError("network unavailable")
|
|
198
|
+
|
|
199
|
+
monkeypatch.setattr(diacritics.urllib.request, "urlopen", failing_urlopen)
|
|
200
|
+
|
|
201
|
+
with pytest.raises(OSError):
|
|
202
|
+
diacritics._default_phonikud_model()
|
|
203
|
+
|
|
204
|
+
dest = tmp_path / "scriptconv" / "phonikud" / "phonikud-1.0.int8.onnx"
|
|
205
|
+
assert not dest.exists()
|
|
206
|
+
cache_dir = tmp_path / "scriptconv" / "phonikud"
|
|
207
|
+
if cache_dir.exists():
|
|
208
|
+
assert list(cache_dir.iterdir()) == []
|
|
114
209
|
|
|
115
210
|
|
|
116
211
|
class TestEuropeanPortugueseSenseDiacritics:
|
|
@@ -101,3 +101,49 @@ class TestDiacriticsGraphExtension(unittest.TestCase):
|
|
|
101
101
|
|
|
102
102
|
if __name__ == "__main__":
|
|
103
103
|
unittest.main()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class TestEdgeModelKeys(unittest.TestCase):
|
|
107
|
+
"""The two model-bearing edges each read their own context key: the
|
|
108
|
+
diacritizer edge (text -> text-diacritized) reads ``diacritizer_model``,
|
|
109
|
+
the phonemizer edge (text -> ipa) reads ``phonemizer_model``. Neither picks
|
|
110
|
+
up the other's key."""
|
|
111
|
+
|
|
112
|
+
def setUp(self):
|
|
113
|
+
self.graph = DEFAULT_GRAPH.extend(diacritics.register).extend(phonemizers.register)
|
|
114
|
+
|
|
115
|
+
def test_diacritizer_edge_reads_diacritizer_model(self):
|
|
116
|
+
seen = {}
|
|
117
|
+
backend = types.SimpleNamespace(diacritize=lambda t: t)
|
|
118
|
+
|
|
119
|
+
def _spy(m=None):
|
|
120
|
+
seen["m"] = m
|
|
121
|
+
return backend
|
|
122
|
+
|
|
123
|
+
with mock.patch.object(diacritics, "_tashkeel", _spy):
|
|
124
|
+
self.graph.convert("نص", "text", "text-diacritized", lang="ar",
|
|
125
|
+
diacritizer_model="RAWI-X",
|
|
126
|
+
phonemizer_model="IGNORED")
|
|
127
|
+
self.assertEqual(seen["m"], "RAWI-X")
|
|
128
|
+
|
|
129
|
+
def test_phonemizer_edge_reads_phonemizer_model(self):
|
|
130
|
+
from scriptconv.phonemizers import registry
|
|
131
|
+
cap = {}
|
|
132
|
+
|
|
133
|
+
class _P:
|
|
134
|
+
def phonemize_string(self, t, l):
|
|
135
|
+
return "IPA"
|
|
136
|
+
|
|
137
|
+
def _spy(lang, alphabet, override=None, model=None, **k):
|
|
138
|
+
cap["model"] = model
|
|
139
|
+
return _P()
|
|
140
|
+
|
|
141
|
+
with mock.patch.object(registry, "phonemizer_for_lang", _spy):
|
|
142
|
+
self.graph.convert("hello", "text", "ipa", lang="en",
|
|
143
|
+
phonemizer_model="BYT5-CKPT",
|
|
144
|
+
diacritizer_model="IGNORED")
|
|
145
|
+
self.assertEqual(cap["model"], "BYT5-CKPT")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/__init__.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/diacritize.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/dialectal.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/espeak.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/frontend.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_thirdparty/shami/normalize.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/kog2p/__init__.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/kog2p/rulebook.txt
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/LICENSE.md
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/__init__.py
RENAMED
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/buck/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/buck/symbols.py
RENAMED
|
File without changes
|
|
File without changes
|
{scriptconv-0.0.4a6 → scriptconv-0.0.4a8}/scriptconv/phonemizers/_vendored/mantoq/num2words.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|