scriptconv 0.0.3a10__tar.gz → 0.0.3a11__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.3a10 → scriptconv-0.0.3a11}/PKG-INFO +2 -2
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/README.md +1 -1
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/notation.py +45 -12
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/version.py +1 -1
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv.egg-info/PKG-INFO +2 -2
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv.egg-info/SOURCES.txt +1 -0
- scriptconv-0.0.3a11/tests/test_arpa_stress.py +88 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/LICENSE +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/pyproject.toml +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/requirements.txt +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/__init__.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/__main__.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/cangjie.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/conventions.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/data/__init__.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/data/cangjie5_tc.tsv.gz +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/graph.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/py.typed +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/readings.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/scripts.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv/translit.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv.egg-info/dependency_links.txt +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv.egg-info/requires.txt +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/scriptconv.egg-info/top_level.txt +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/setup.cfg +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_cangjie.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_cli.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_conventions.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_errors_policy.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_examples.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_graph.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_notation.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_readings.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_readings_zh.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_scripts.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/tests/test_scripts_stressonnx_compat.py +0 -0
- {scriptconv-0.0.3a10 → scriptconv-0.0.3a11}/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.3a11
|
|
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
|
|
@@ -210,7 +210,7 @@ below describes the per-converter default.
|
|
|
210
210
|
|
|
211
211
|
| Notation | `to_ipa` → `from_ipa` round-trip | `from_ipa` → `to_ipa` round-trip | Unknown-token behaviour |
|
|
212
212
|
|----------|----------------------------------|----------------------------------|-------------------------|
|
|
213
|
-
| **ARPABET** |
|
|
213
|
+
| **ARPABET** | **Lossless with `stress=True`** (digits ↔ IPA `ˈ`/`ˌ` before the stressed vowel; residues: extended-ARPABET `AX` normalises to CMUdict's `AH0` spelling, and `AH0 R` fuses to the r-colored `AXR0` — stable from the IPA side). Default `stress=False` drops digits and merges `AH0`↔`AX` | **Lossy** — ARPABET is an English-only inventory, so any IPA symbol outside it becomes the *unknown* placeholder | `arpa_to_ipa`: passed through unchanged. `ipa_to_arpa`: diacritics and suprasegmentals are dropped; other out-of-inventory symbols follow `errors=` (default `"replace"` → `?`) |
|
|
214
214
|
| **X-SAMPA** | Exact for all canonical symbols | Exact except aliases (`f\`→ɸ, `&`→æ) normalise to their canonical spelling | Passed through unchanged |
|
|
215
215
|
| **Buckwalter ↔ Arabic** | Exact (the `^` shadda alias normalises to canonical `~`; precomposed lam-alef ligatures decompose to two chars, visually identical) | Exact | Passed through unchanged |
|
|
216
216
|
| **Lexique ↔ IPA** | Exact except the `°`/`3` schwa pair (both → `ə`; reverse always → `°`) | Exact | Passed through unchanged |
|
|
@@ -177,7 +177,7 @@ below describes the per-converter default.
|
|
|
177
177
|
|
|
178
178
|
| Notation | `to_ipa` → `from_ipa` round-trip | `from_ipa` → `to_ipa` round-trip | Unknown-token behaviour |
|
|
179
179
|
|----------|----------------------------------|----------------------------------|-------------------------|
|
|
180
|
-
| **ARPABET** |
|
|
180
|
+
| **ARPABET** | **Lossless with `stress=True`** (digits ↔ IPA `ˈ`/`ˌ` before the stressed vowel; residues: extended-ARPABET `AX` normalises to CMUdict's `AH0` spelling, and `AH0 R` fuses to the r-colored `AXR0` — stable from the IPA side). Default `stress=False` drops digits and merges `AH0`↔`AX` | **Lossy** — ARPABET is an English-only inventory, so any IPA symbol outside it becomes the *unknown* placeholder | `arpa_to_ipa`: passed through unchanged. `ipa_to_arpa`: diacritics and suprasegmentals are dropped; other out-of-inventory symbols follow `errors=` (default `"replace"` → `?`) |
|
|
181
181
|
| **X-SAMPA** | Exact for all canonical symbols | Exact except aliases (`f\`→ɸ, `&`→æ) normalise to their canonical spelling | Passed through unchanged |
|
|
182
182
|
| **Buckwalter ↔ Arabic** | Exact (the `^` shadda alias normalises to canonical `~`; precomposed lam-alef ligatures decompose to two chars, visually identical) | Exact | Passed through unchanged |
|
|
183
183
|
| **Lexique ↔ IPA** | Exact except the `°`/`3` schwa pair (both → `ə`; reverse always → `°`) | Exact | Passed through unchanged |
|
|
@@ -157,6 +157,11 @@ _IPA_TO_ARPA.setdefault("g", "G")
|
|
|
157
157
|
_IPA_ARPA_KEYS_SORTED = sorted(_IPA_TO_ARPA.keys(), key=len, reverse=True)
|
|
158
158
|
_IPA_ARPA_RE = re.compile("|".join(re.escape(s) for s in _IPA_ARPA_KEYS_SORTED))
|
|
159
159
|
|
|
160
|
+
# ARPABET vowel class — the symbols that carry stress digits.
|
|
161
|
+
_ARPA_VOWELS = {"AO", "AA", "IY", "UW", "EH", "IH", "UH", "AH", "AE", "AX",
|
|
162
|
+
"EY", "AY", "OW", "AW", "OY", "ER", "AXR"}
|
|
163
|
+
_STRESS_MARKS = {"ˈ": "1", "ˌ": "2"} # IPA primary/secondary stress
|
|
164
|
+
|
|
160
165
|
|
|
161
166
|
|
|
162
167
|
class UnknownSymbolError(ValueError):
|
|
@@ -187,12 +192,18 @@ def _unknown(symbol: str, position: int, notation: str, errors: str,
|
|
|
187
192
|
raise ValueError(f"errors must be one of {_ERROR_POLICIES}, not {errors!r}")
|
|
188
193
|
|
|
189
194
|
|
|
190
|
-
def arpa_to_ipa(arpa_sequence: str, errors: str = "pass"
|
|
195
|
+
def arpa_to_ipa(arpa_sequence: str, errors: str = "pass",
|
|
196
|
+
stress: bool = False) -> str:
|
|
191
197
|
"""Convert a space-separated ARPABET sequence to an IPA string.
|
|
192
198
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
``
|
|
199
|
+
With ``stress=True``, ARPABET stress digits are preserved as IPA stress
|
|
200
|
+
marks placed immediately before the stressed vowel's symbol: ``AH1`` →
|
|
201
|
+
``ˈʌ``, ``AH2`` → ``ˌʌ``, ``AH0`` → unmarked (a purely notational,
|
|
202
|
+
reversible convention — no syllabification is performed). The default
|
|
203
|
+
``stress=False`` keeps the historical behavior of stripping the digits.
|
|
204
|
+
``errors`` selects the unknown-token policy: ``"pass"`` (default) keeps
|
|
205
|
+
the token, ``"replace"`` substitutes ``?``, ``"ignore"`` drops it,
|
|
206
|
+
``"strict"`` raises :class:`UnknownSymbolError`.
|
|
196
207
|
|
|
197
208
|
Examples
|
|
198
209
|
--------
|
|
@@ -202,6 +213,9 @@ def arpa_to_ipa(arpa_sequence: str, errors: str = "pass") -> str:
|
|
|
202
213
|
tokens = arpa_sequence.strip().split()
|
|
203
214
|
result = []
|
|
204
215
|
for i, tok in enumerate(tokens):
|
|
216
|
+
mark = ""
|
|
217
|
+
if stress and tok[-1:] in ("1", "2") and tok[:-1] in _ARPA_VOWELS:
|
|
218
|
+
mark = "ˈ" if tok[-1] == "1" else "ˌ"
|
|
205
219
|
ipa = _ARPA_TO_IPA.get(tok)
|
|
206
220
|
if ipa is None:
|
|
207
221
|
# strip trailing digit and retry
|
|
@@ -209,19 +223,24 @@ def arpa_to_ipa(arpa_sequence: str, errors: str = "pass") -> str:
|
|
|
209
223
|
ipa = _ARPA_TO_IPA.get(stripped)
|
|
210
224
|
if ipa is None:
|
|
211
225
|
ipa = _unknown(tok, i, "arpa", errors)
|
|
212
|
-
|
|
226
|
+
mark = ""
|
|
227
|
+
result.append(mark + ipa)
|
|
213
228
|
return "".join(result)
|
|
214
229
|
|
|
215
230
|
|
|
216
231
|
def ipa_to_arpa(ipa_string: str, unknown: str = "?",
|
|
217
|
-
errors: str = "replace") -> str:
|
|
232
|
+
errors: str = "replace", stress: bool = False) -> str:
|
|
218
233
|
"""Convert an IPA string to a space-separated ARPABET sequence.
|
|
219
234
|
|
|
220
|
-
Matches longest IPA symbol first. ``
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
Matches longest IPA symbol first. With ``stress=True``, IPA stress
|
|
236
|
+
marks (``ˈ``/``ˌ``) preceding a vowel-class symbol are emitted as that
|
|
237
|
+
vowel's stress digit (``1``/``2``) and unmarked vowels receive ``0`` —
|
|
238
|
+
the reverse of :func:`arpa_to_ipa`'s ``stress=True`` convention.
|
|
239
|
+
``errors`` selects the unknown-symbol policy (default ``"replace"``,
|
|
240
|
+
preserving the historical behavior of substituting *unknown*, ``"?"``);
|
|
241
|
+
``unknown=""`` still drops silently. Combining marks and modifier
|
|
242
|
+
letters qualify the preceding phoneme and are always dropped rather
|
|
243
|
+
than treated as unknowns.
|
|
225
244
|
|
|
226
245
|
Examples
|
|
227
246
|
--------
|
|
@@ -231,11 +250,25 @@ def ipa_to_arpa(ipa_string: str, unknown: str = "?",
|
|
|
231
250
|
'?'
|
|
232
251
|
"""
|
|
233
252
|
tokens = []
|
|
253
|
+
pending_stress = ""
|
|
234
254
|
pos = 0
|
|
235
255
|
while pos < len(ipa_string):
|
|
256
|
+
if stress and ipa_string[pos] in _STRESS_MARKS:
|
|
257
|
+
pending_stress = _STRESS_MARKS[ipa_string[pos]]
|
|
258
|
+
pos += 1
|
|
259
|
+
continue
|
|
236
260
|
m = _IPA_ARPA_RE.match(ipa_string, pos)
|
|
237
261
|
if m:
|
|
238
|
-
|
|
262
|
+
tok = _IPA_TO_ARPA[m.group(0)]
|
|
263
|
+
if stress and tok in _ARPA_VOWELS:
|
|
264
|
+
# CMUdict spells schwa as AH0 (it has no AX), so stress-aware
|
|
265
|
+
# output uses AH for exact round-trips; extended-ARPABET AX
|
|
266
|
+
# input still normalizes to AH0 (documented residue).
|
|
267
|
+
if m.group(0) == "ə":
|
|
268
|
+
tok = "AH"
|
|
269
|
+
tok += pending_stress or "0"
|
|
270
|
+
pending_stress = ""
|
|
271
|
+
tokens.append(tok)
|
|
239
272
|
pos = m.end()
|
|
240
273
|
else:
|
|
241
274
|
ch = ipa_string[pos]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scriptconv
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3a11
|
|
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
|
|
@@ -210,7 +210,7 @@ below describes the per-converter default.
|
|
|
210
210
|
|
|
211
211
|
| Notation | `to_ipa` → `from_ipa` round-trip | `from_ipa` → `to_ipa` round-trip | Unknown-token behaviour |
|
|
212
212
|
|----------|----------------------------------|----------------------------------|-------------------------|
|
|
213
|
-
| **ARPABET** |
|
|
213
|
+
| **ARPABET** | **Lossless with `stress=True`** (digits ↔ IPA `ˈ`/`ˌ` before the stressed vowel; residues: extended-ARPABET `AX` normalises to CMUdict's `AH0` spelling, and `AH0 R` fuses to the r-colored `AXR0` — stable from the IPA side). Default `stress=False` drops digits and merges `AH0`↔`AX` | **Lossy** — ARPABET is an English-only inventory, so any IPA symbol outside it becomes the *unknown* placeholder | `arpa_to_ipa`: passed through unchanged. `ipa_to_arpa`: diacritics and suprasegmentals are dropped; other out-of-inventory symbols follow `errors=` (default `"replace"` → `?`) |
|
|
214
214
|
| **X-SAMPA** | Exact for all canonical symbols | Exact except aliases (`f\`→ɸ, `&`→æ) normalise to their canonical spelling | Passed through unchanged |
|
|
215
215
|
| **Buckwalter ↔ Arabic** | Exact (the `^` shadda alias normalises to canonical `~`; precomposed lam-alef ligatures decompose to two chars, visually identical) | Exact | Passed through unchanged |
|
|
216
216
|
| **Lexique ↔ IPA** | Exact except the `°`/`3` schwa pair (both → `ə`; reverse always → `°`) | Exact | Passed through unchanged |
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""ARPABET stress preservation (stress=True) — reversible by construction."""
|
|
2
|
+
import random
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from scriptconv.notation import _ARPA_BASE, _ARPA_VOWELS, arpa_to_ipa, ipa_to_arpa
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestStressForward(unittest.TestCase):
|
|
9
|
+
def test_marks_placed_before_stressed_vowel(self):
|
|
10
|
+
self.assertEqual(arpa_to_ipa("HH AH0 L OW1", stress=True), "həlˈoʊ")
|
|
11
|
+
self.assertEqual(arpa_to_ipa("K AE1 T", stress=True), "kˈæt")
|
|
12
|
+
self.assertEqual(arpa_to_ipa("AH2", stress=True), "ˌʌ")
|
|
13
|
+
|
|
14
|
+
def test_unstressed_and_consonants_unmarked(self):
|
|
15
|
+
self.assertEqual(arpa_to_ipa("AH0", stress=True), "ə")
|
|
16
|
+
self.assertEqual(arpa_to_ipa("K T", stress=True), "kt")
|
|
17
|
+
|
|
18
|
+
def test_default_still_strips(self):
|
|
19
|
+
self.assertEqual(arpa_to_ipa("HH AH0 L OW1"), "həloʊ")
|
|
20
|
+
|
|
21
|
+
def test_unknown_token_never_gets_a_mark(self):
|
|
22
|
+
self.assertEqual(arpa_to_ipa("ZZ1", stress=True), "ZZ1")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestStressReverse(unittest.TestCase):
|
|
26
|
+
def test_marks_become_digits_unmarked_vowels_get_zero(self):
|
|
27
|
+
self.assertEqual(ipa_to_arpa("həlˈoʊ", stress=True), "HH AH0 L OW1")
|
|
28
|
+
self.assertEqual(ipa_to_arpa("kˈæt", stress=True), "K AE1 T")
|
|
29
|
+
self.assertEqual(ipa_to_arpa("ˌʌ", stress=True), "AH2")
|
|
30
|
+
|
|
31
|
+
def test_schwa_spelled_ah0_like_cmudict(self):
|
|
32
|
+
self.assertEqual(ipa_to_arpa("ə", stress=True), "AH0")
|
|
33
|
+
|
|
34
|
+
def test_default_still_drops_marks(self):
|
|
35
|
+
self.assertEqual(ipa_to_arpa("həlˈoʊ"), "HH AX L OW")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestRoundTrip(unittest.TestCase):
|
|
39
|
+
def test_readme_example_exact(self):
|
|
40
|
+
x = "HH AH0 L OW1"
|
|
41
|
+
self.assertEqual(ipa_to_arpa(arpa_to_ipa(x, stress=True), stress=True), x)
|
|
42
|
+
|
|
43
|
+
def test_property_cmudict_legal_sequences(self):
|
|
44
|
+
"""Random CMUdict-legal sequences round-trip exactly up to
|
|
45
|
+
IPA-equivalence: token sequences whose IPA concatenation is identical
|
|
46
|
+
(r-colored schwa, affricates, diphthong fusion — contiguous IPA has
|
|
47
|
+
no token boundary) may normalize to the fused spelling, and that
|
|
48
|
+
spelling must itself be IPA-stable. The overwhelming majority
|
|
49
|
+
round-trip byte-exactly."""
|
|
50
|
+
random.seed(20260721)
|
|
51
|
+
cons = [c for c in _ARPA_BASE
|
|
52
|
+
if c not in _ARPA_VOWELS and c not in ("AX", "AXR")]
|
|
53
|
+
vows = [v for v in _ARPA_VOWELS if v not in ("AX", "AXR")]
|
|
54
|
+
exact = fused = 0
|
|
55
|
+
for _ in range(400):
|
|
56
|
+
seq = []
|
|
57
|
+
for _ in range(random.randint(1, 7)):
|
|
58
|
+
if random.random() < 0.5:
|
|
59
|
+
seq.append(random.choice(cons))
|
|
60
|
+
else:
|
|
61
|
+
seq.append(random.choice(vows) + random.choice("012"))
|
|
62
|
+
s = " ".join(seq)
|
|
63
|
+
ipa = arpa_to_ipa(s, stress=True)
|
|
64
|
+
rt = ipa_to_arpa(ipa, stress=True)
|
|
65
|
+
if rt == s:
|
|
66
|
+
exact += 1
|
|
67
|
+
else:
|
|
68
|
+
fused += 1
|
|
69
|
+
# fusion residue must be IPA-stable
|
|
70
|
+
self.assertEqual(arpa_to_ipa(rt, stress=True), ipa, s)
|
|
71
|
+
self.assertGreater(exact, 350)
|
|
72
|
+
self.assertLess(fused, 50)
|
|
73
|
+
|
|
74
|
+
def test_documented_residues(self):
|
|
75
|
+
# extended-ARPABET AX normalizes to CMUdict's AH0 spelling
|
|
76
|
+
self.assertEqual(
|
|
77
|
+
ipa_to_arpa(arpa_to_ipa("AX", stress=True), stress=True), "AH0")
|
|
78
|
+
# schwa+R fuses to the r-colored vowel; stable from the IPA side
|
|
79
|
+
fused = ipa_to_arpa(arpa_to_ipa("AH0 R", stress=True), stress=True)
|
|
80
|
+
self.assertEqual(fused, "AXR0")
|
|
81
|
+
# affricate fusion exists in default mode too (contiguous IPA)
|
|
82
|
+
self.assertEqual(ipa_to_arpa(arpa_to_ipa("T SH")), "CH")
|
|
83
|
+
self.assertEqual(arpa_to_ipa(fused, stress=True),
|
|
84
|
+
arpa_to_ipa("AH0 R", stress=True))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
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
|
|
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
|