scriptconv 0.0.4a8__tar.gz → 0.0.4a9__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 (96) hide show
  1. {scriptconv-0.0.4a8/scriptconv.egg-info → scriptconv-0.0.4a9}/PKG-INFO +1 -1
  2. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/en.py +32 -11
  3. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/ja.py +16 -2
  4. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/ko.py +8 -1
  5. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/mul.py +69 -34
  6. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/pt.py +8 -1
  7. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/vi.py +8 -1
  8. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/zh.py +8 -1
  9. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/version.py +1 -1
  10. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9/scriptconv.egg-info}/PKG-INFO +1 -1
  11. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv.egg-info/SOURCES.txt +1 -0
  12. scriptconv-0.0.4a9/tests/test_phonemizers_friendly_import_errors.py +129 -0
  13. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/LICENSE +0 -0
  14. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/README.md +0 -0
  15. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/pyproject.toml +0 -0
  16. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/requirements.txt +0 -0
  17. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/__init__.py +0 -0
  18. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/__main__.py +0 -0
  19. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/cangjie.py +0 -0
  20. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/conventions.py +0 -0
  21. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/data/__init__.py +0 -0
  22. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/data/cangjie5_tc.tsv.gz +0 -0
  23. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/diacritics.py +0 -0
  24. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/graph.py +0 -0
  25. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/notation.py +0 -0
  26. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/__init__.py +0 -0
  27. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/__init__.py +0 -0
  28. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/bw2ipa.py +0 -0
  29. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/hangul2ipa.py +0 -0
  30. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/aspiration.csv +0 -0
  31. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/assimilation.csv +0 -0
  32. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/double_coda.csv +0 -0
  33. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/hanja.tsv +0 -0
  34. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/ipa.csv +0 -0
  35. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/neutralization.csv +0 -0
  36. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/tensification.csv +0 -0
  37. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/ko_tables/yale.csv +0 -0
  38. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/__init__.py +0 -0
  39. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/codeswitch.py +0 -0
  40. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/diacritize.py +0 -0
  41. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/dialectal.py +0 -0
  42. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/english_g2p.py +0 -0
  43. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/espeak.py +0 -0
  44. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/frontend.py +0 -0
  45. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/levantine_g2p.py +0 -0
  46. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/normalize.py +0 -0
  47. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/shami/phoneme_inventory.py +0 -0
  48. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_thirdparty/zh_num.py +0 -0
  49. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/__init__.py +0 -0
  50. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/kog2p/LICENSE.md +0 -0
  51. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/kog2p/__init__.py +0 -0
  52. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/kog2p/rulebook.txt +0 -0
  53. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/LICENSE.md +0 -0
  54. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/__init__.py +0 -0
  55. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/buck/__init__.py +0 -0
  56. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/buck/phonetise_buckwalter.py +0 -0
  57. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/buck/symbols.py +0 -0
  58. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/buck/tokenization.py +0 -0
  59. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/num2words.py +0 -0
  60. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/_vendored/mantoq/unicode_symbol2label.py +0 -0
  61. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/ar.py +0 -0
  62. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/base.py +0 -0
  63. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/enums.py +0 -0
  64. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/eu.py +0 -0
  65. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/fa.py +0 -0
  66. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/gl.py +0 -0
  67. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/he.py +0 -0
  68. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/mwl.py +0 -0
  69. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/o2ipa.py +0 -0
  70. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/registry.py +0 -0
  71. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/phonemizers/shami.py +0 -0
  72. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/py.typed +0 -0
  73. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/readings.py +0 -0
  74. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/scripts.py +0 -0
  75. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv/translit.py +0 -0
  76. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv.egg-info/dependency_links.txt +0 -0
  77. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv.egg-info/requires.txt +0 -0
  78. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/scriptconv.egg-info/top_level.txt +0 -0
  79. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/setup.cfg +0 -0
  80. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_arpa_stress.py +0 -0
  81. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_cangjie.py +0 -0
  82. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_cli.py +0 -0
  83. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_conventions.py +0 -0
  84. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_diacritics.py +0 -0
  85. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_diacritics_graph.py +0 -0
  86. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_errors_policy.py +0 -0
  87. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_examples.py +0 -0
  88. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_graph.py +0 -0
  89. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_notation.py +0 -0
  90. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_phonemizers_base.py +0 -0
  91. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_phonemizers_cjk_ar.py +0 -0
  92. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_readings.py +0 -0
  93. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_readings_zh.py +0 -0
  94. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_scripts.py +0 -0
  95. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_scripts_stressonnx_compat.py +0 -0
  96. {scriptconv-0.0.4a8 → scriptconv-0.0.4a9}/tests/test_translit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptconv
3
- Version: 0.0.4a8
3
+ Version: 0.0.4a9
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
@@ -16,9 +16,16 @@ class DeepPhonemizer(BasePhonemizer):
16
16
  def __init__(self, model="latin_ipa_forward.pt"):
17
17
  import os
18
18
 
19
- import dp
20
- from dp.phonemizer import Phonemizer
21
- import torch
19
+ try:
20
+ import dp
21
+ from dp.phonemizer import Phonemizer
22
+ import torch
23
+ except ImportError as e:
24
+ raise ImportError(
25
+ "deep-phonemizer and torch are required for the DeepPhonemizer "
26
+ "phonemizer. Install them with 'pip install deep-phonemizer torch' "
27
+ "(or 'pip install scriptconv[en-phonemizers]')."
28
+ ) from e
22
29
  # needed for latest torch version
23
30
  torch.serialization.add_safe_globals([dp.preprocessing.text.Preprocessor])
24
31
  torch.serialization.add_safe_globals([dp.preprocessing.text.LanguageTokenizer])
@@ -90,10 +97,17 @@ class OpenPhonemizer(BasePhonemizer):
90
97
  """
91
98
 
92
99
  def __init__(self):
93
- from openphonemizer import OpenPhonemizer
94
- import torch
95
- # needed for latest torch version
96
- import dp
100
+ try:
101
+ from openphonemizer import OpenPhonemizer
102
+ import torch
103
+ # needed for latest torch version
104
+ import dp
105
+ except ImportError as e:
106
+ raise ImportError(
107
+ "openphonemizer and torch are required for the OpenPhonemizer "
108
+ "phonemizer. Install them with 'pip install openphonemizer torch' "
109
+ "(or 'pip install scriptconv[en-phonemizers]')."
110
+ ) from e
97
111
  torch.serialization.add_safe_globals([dp.preprocessing.text.Preprocessor])
98
112
  torch.serialization.add_safe_globals([dp.preprocessing.text.LanguageTokenizer])
99
113
  torch.serialization.add_safe_globals([dp.preprocessing.text.SequenceTokenizer])
@@ -142,10 +156,17 @@ class G2PEnPhonemizer(BasePhonemizer):
142
156
 
143
157
  def __init__(self, alphabet=Alphabet.IPA):
144
158
  assert alphabet in [Alphabet.IPA, Alphabet.ARPA]
145
- import nltk
146
- nltk.download('averaged_perceptron_tagger_eng')
147
- nltk.download('cmudict')
148
- from g2p_en import G2p
159
+ try:
160
+ import nltk
161
+ nltk.download('averaged_perceptron_tagger_eng')
162
+ nltk.download('cmudict')
163
+ from g2p_en import G2p
164
+ except ImportError as e:
165
+ raise ImportError(
166
+ "g2p_en is required for the G2PEn phonemizer. "
167
+ "Install it with 'pip install g2p_en' "
168
+ "(or 'pip install scriptconv[en-phonemizers]')."
169
+ ) from e
149
170
  self.g2p = G2p()
150
171
  super().__init__(alphabet)
151
172
 
@@ -40,7 +40,14 @@ class CutletPhonemizer(BasePhonemizer):
40
40
  # If `use_foreign_spelling` is true, output will use the foreign spelling
41
41
  # provided in a UniDic lemma when available. For example, "カツ" will
42
42
  # become "cutlet" instead of "katsu".
43
- import cutlet
43
+ try:
44
+ import cutlet
45
+ except ImportError as e:
46
+ raise ImportError(
47
+ "cutlet is required for the Cutlet phonemizer. "
48
+ "Install it with 'pip install cutlet' "
49
+ "(or 'pip install scriptconv[ja-phonemizers]')."
50
+ ) from e
44
51
  self.g2p = cutlet.Cutlet(alphabet)
45
52
  self.g2p.use_foreign_spelling = use_foreign_spelling
46
53
  super().__init__(alphabet)
@@ -73,7 +80,14 @@ class PyKakasiPhonemizer(BasePhonemizer):
73
80
  def __init__(self, alphabet=Alphabet.HEPBURN):
74
81
  assert alphabet in [Alphabet.HEPBURN, Alphabet.KANA, Alphabet.HIRA]
75
82
  # kana, hira, hepburn
76
- import pykakasi
83
+ try:
84
+ import pykakasi
85
+ except ImportError as e:
86
+ raise ImportError(
87
+ "pykakasi is required for the PyKakasi phonemizer. "
88
+ "Install it with 'pip install pykakasi' "
89
+ "(or 'pip install scriptconv[ja-phonemizers]')."
90
+ ) from e
77
91
  self.g2p = pykakasi.kakasi()
78
92
  super().__init__(alphabet)
79
93
 
@@ -10,7 +10,14 @@ class G2PKPhonemizer(BasePhonemizer):
10
10
  def __init__(self, descriptive=True, group_vowels=True, to_syl=True,
11
11
  alphabet=Alphabet.IPA):
12
12
  assert alphabet in [Alphabet.IPA, Alphabet.HANGUL]
13
- from g2pk import G2p
13
+ try:
14
+ from g2pk import G2p
15
+ except ImportError as e:
16
+ raise ImportError(
17
+ "g2pk is required for the G2PK phonemizer. "
18
+ "Install it with 'pip install g2pk' "
19
+ "(or 'pip install scriptconv[ko]')."
20
+ ) from e
14
21
  self.g2p = G2p()
15
22
  self.descriptive = descriptive
16
23
  self.group_vowels = group_vowels
@@ -435,7 +435,14 @@ class GruutPhonemizer(BasePhonemizer):
435
435
  Yields lists of word phonemes for each sentence.
436
436
  """
437
437
  lang = self.get_lang(lang)
438
- import gruut
438
+ try:
439
+ import gruut
440
+ except ImportError as e:
441
+ raise ImportError(
442
+ "gruut is required for the Gruut phonemizer. "
443
+ "Install it with 'pip install gruut' "
444
+ "(or 'pip install scriptconv[gruut]')."
445
+ ) from e
439
446
  for sentence in gruut.sentences(text, lang=lang):
440
447
  sent_phonemes = [w.phonemes for w in sentence if w.phonemes]
441
448
  if sentence and not sent_phonemes:
@@ -601,8 +608,15 @@ class GoruutPhonemizer(BasePhonemizer):
601
608
 
602
609
  def __init__(self, remote_url=None):
603
610
  super().__init__(Alphabet.IPA)
604
- from pygoruut.pygoruut import Pygoruut
605
- from pygoruut.pygoruut_languages import PygoruutLanguages
611
+ try:
612
+ from pygoruut.pygoruut import Pygoruut
613
+ from pygoruut.pygoruut_languages import PygoruutLanguages
614
+ except ImportError as e:
615
+ raise ImportError(
616
+ "pygoruut is required for the Goruut phonemizer. "
617
+ "Install it with 'pip install pygoruut' "
618
+ "(or 'pip install scriptconv[goruut]')."
619
+ ) from e
606
620
 
607
621
  self.pygoruut_langs = PygoruutLanguages()
608
622
  if remote_url is not None:
@@ -665,7 +679,14 @@ class EpitranPhonemizer(BasePhonemizer):
665
679
 
666
680
  def __init__(self):
667
681
  super().__init__(Alphabet.IPA)
668
- import epitran
682
+ try:
683
+ import epitran
684
+ except ImportError as e:
685
+ raise ImportError(
686
+ "epitran is required for the Epitran phonemizer. "
687
+ "Install it with 'pip install epitran' "
688
+ "(or 'pip install scriptconv[epitran]')."
689
+ ) from e
669
690
  self.epitran = epitran
670
691
  self._epis: Dict[str, epitran.Epitran] = {}
671
692
 
@@ -735,35 +756,42 @@ class MisakiPhonemizer(BasePhonemizer):
735
756
  """
736
757
  lang = self.get_lang(lang)
737
758
 
738
- if lang == "zh":
739
- if self.g2p_zh is None:
740
- from misaki.zh import ZHG2P
741
- self.g2p_zh = ZHG2P(version=self.zh_version)
742
- return self.g2p_zh
743
- elif lang == "ko":
744
- if self.g2p_ko is None:
745
- from misaki.ko import KOG2P
746
- self.g2p_ko = KOG2P()
747
- return self.g2p_ko
748
- elif lang == "vi":
749
- if self.g2p_vi is None:
750
- from misaki.vi import VIG2P
751
- self.g2p_vi = VIG2P()
752
- return self.g2p_vi
753
- elif lang == "ja":
754
- if self.g2p_ja is None:
755
- from misaki.ja import JAG2P
756
- self.g2p_ja = JAG2P()
757
- return self.g2p_ja
758
- else:
759
- if self.g2p_en is None:
760
- from misaki import en
761
- self.g2p_en = en.G2P()
762
- if lang == "en-GB":
763
- self.g2p_en.british = True
764
- elif lang == "en-US":
765
- self.g2p_en.british = False
766
- return self.g2p_en
759
+ try:
760
+ if lang == "zh":
761
+ if self.g2p_zh is None:
762
+ from misaki.zh import ZHG2P
763
+ self.g2p_zh = ZHG2P(version=self.zh_version)
764
+ return self.g2p_zh
765
+ elif lang == "ko":
766
+ if self.g2p_ko is None:
767
+ from misaki.ko import KOG2P
768
+ self.g2p_ko = KOG2P()
769
+ return self.g2p_ko
770
+ elif lang == "vi":
771
+ if self.g2p_vi is None:
772
+ from misaki.vi import VIG2P
773
+ self.g2p_vi = VIG2P()
774
+ return self.g2p_vi
775
+ elif lang == "ja":
776
+ if self.g2p_ja is None:
777
+ from misaki.ja import JAG2P
778
+ self.g2p_ja = JAG2P()
779
+ return self.g2p_ja
780
+ else:
781
+ if self.g2p_en is None:
782
+ from misaki import en
783
+ self.g2p_en = en.G2P()
784
+ if lang == "en-GB":
785
+ self.g2p_en.british = True
786
+ elif lang == "en-US":
787
+ self.g2p_en.british = False
788
+ return self.g2p_en
789
+ except ImportError as e:
790
+ raise ImportError(
791
+ "misaki is required for the Misaki phonemizer. "
792
+ "Install it with 'pip install misaki' "
793
+ "(or 'pip install scriptconv[misaki]')."
794
+ ) from e
767
795
 
768
796
  def phonemize_string(self, text: str, lang: str) -> str:
769
797
  pho = self._get_phonemizer(lang)
@@ -1386,7 +1414,14 @@ class TransphonePhonemizer(BasePhonemizer):
1386
1414
 
1387
1415
  def __init__(self):
1388
1416
  super().__init__(Alphabet.IPA)
1389
- from transphone import read_tokenizer
1417
+ try:
1418
+ from transphone import read_tokenizer
1419
+ except ImportError as e:
1420
+ raise ImportError(
1421
+ "transphone is required for the Transphone phonemizer. "
1422
+ "Install it with 'pip install transphone' "
1423
+ "(or 'pip install scriptconv[transphone]')."
1424
+ ) from e
1390
1425
  self.read_tokenizer = read_tokenizer
1391
1426
  self._models = {}
1392
1427
 
@@ -7,7 +7,14 @@ from scriptconv.phonemizers.enums import Alphabet
7
7
  class TugaphonePhonemizer(BasePhonemizer):
8
8
 
9
9
  def __init__(self):
10
- from tugaphone import TugaPhonemizer
10
+ try:
11
+ from tugaphone import TugaPhonemizer
12
+ except ImportError as e:
13
+ raise ImportError(
14
+ "tugaphone is required for the Tugaphone phonemizer. "
15
+ "Install it with 'pip install tugaphone' "
16
+ "(or 'pip install scriptconv[pt-phonemizers]')."
17
+ ) from e
11
18
  self.tuga = TugaPhonemizer()
12
19
  super().__init__(Alphabet.IPA)
13
20
 
@@ -5,7 +5,14 @@ from scriptconv.phonemizers.enums import Alphabet
5
5
  class VIPhonemePhonemizer(BasePhonemizer):
6
6
  """https://github.com/v-nhandt21/Viphoneme"""
7
7
  def __init__(self):
8
- from viphoneme import vi2IPA
8
+ try:
9
+ from viphoneme import vi2IPA
10
+ except ImportError as e:
11
+ raise ImportError(
12
+ "viphoneme is required for the VIPhoneme phonemizer. "
13
+ "Install it with 'pip install viphoneme' "
14
+ "(or 'pip install scriptconv[vi]')."
15
+ ) from e
9
16
  self.g2p = vi2IPA
10
17
  super().__init__(Alphabet.IPA)
11
18
 
@@ -44,7 +44,14 @@ class JiebaPhonemizer(BasePhonemizer):
44
44
  Returns:
45
45
  str: Tokenized text with words separated by spaces.
46
46
  """
47
- import jieba
47
+ try:
48
+ import jieba
49
+ except ImportError as e:
50
+ raise ImportError(
51
+ "jieba is required for the Jieba phonemizer. "
52
+ "Install it with 'pip install jieba' "
53
+ "(or 'pip install scriptconv[zh-phonemizers]')."
54
+ ) from e
48
55
  lang = self.get_lang(lang)
49
56
  seg_list = jieba.cut(text, cut_all=False)
50
57
  # NFKC folds full-width digits (e.g. "3") to ASCII so num2str's
@@ -2,7 +2,7 @@
2
2
  VERSION_MAJOR = 0
3
3
  VERSION_MINOR = 0
4
4
  VERSION_BUILD = 4
5
- VERSION_ALPHA = 8
5
+ VERSION_ALPHA = 9
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.4a8
3
+ Version: 0.0.4a9
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
@@ -86,6 +86,7 @@ tests/test_graph.py
86
86
  tests/test_notation.py
87
87
  tests/test_phonemizers_base.py
88
88
  tests/test_phonemizers_cjk_ar.py
89
+ tests/test_phonemizers_friendly_import_errors.py
89
90
  tests/test_readings.py
90
91
  tests/test_readings_zh.py
91
92
  tests/test_scripts.py
@@ -0,0 +1,129 @@
1
+ """Pins the friendly-ImportError convention for the lazy backend imports
2
+ inside phonemizer wrappers.
3
+
4
+ ``get_phonemizer_class`` (see ``registry.py``) already raises a friendly,
5
+ extra-naming ``ImportError`` when the wrapper *module itself* fails to
6
+ import. That does not cover the far more common case: the wrapper module
7
+ imports fine (the backend package is only imported lazily, inside
8
+ ``__init__``/a method), and it is that inner, lazy import that fails when
9
+ the backend is missing. Every wrapper must catch that inner failure and
10
+ re-raise a friendly ``ImportError`` naming the pip extra — never let a bare
11
+ ``ModuleNotFoundError``/``ImportError`` escape.
12
+
13
+ This test forces each wrapper's lazy import to fail (by shadowing the
14
+ backend module in ``sys.modules`` with ``None``, which makes the ``import``
15
+ statement raise ``ImportError`` regardless of whether the package is
16
+ actually installed) and asserts the resulting exception is an
17
+ ``ImportError`` whose message contains an install instruction. No optional
18
+ backend needs to be installed to run this, and nothing is skipped.
19
+ """
20
+ import sys
21
+ import unittest
22
+ from unittest.mock import patch
23
+
24
+ from scriptconv.phonemizers import Phonemizer, get_phonemizer_class
25
+
26
+
27
+ def _trigger_construct(cls):
28
+ cls()
29
+
30
+
31
+ def _trigger_gruut(cls):
32
+ p = cls()
33
+ list(p._text_to_phonemes("hi", "en"))
34
+
35
+
36
+ def _trigger_misaki_en(cls):
37
+ p = cls()
38
+ p._get_phonemizer("en-US")
39
+
40
+
41
+ def _trigger_jieba(cls):
42
+ p = cls()
43
+ p.phonemize_string("你好", "zh")
44
+
45
+
46
+ # member -> (module name to blank out in sys.modules, how to trigger the
47
+ # lazy import that consumes it)
48
+ CASES = {
49
+ Phonemizer.MISAKI: ("misaki", _trigger_misaki_en),
50
+ Phonemizer.MISAKI_EN: ("misaki", _trigger_misaki_en),
51
+ Phonemizer.GRUUT: ("gruut", _trigger_gruut),
52
+ Phonemizer.GORUUT: ("pygoruut", _trigger_construct),
53
+ Phonemizer.EPITRAN: ("epitran", _trigger_construct),
54
+ Phonemizer.TRANSPHONE: ("transphone", _trigger_construct),
55
+ Phonemizer.DEEPPHONEMIZER: ("dp", _trigger_construct),
56
+ Phonemizer.OPENPHONEMIZER: ("openphonemizer", _trigger_construct),
57
+ Phonemizer.G2PEN: ("nltk", _trigger_construct),
58
+ Phonemizer.TUGAPHONE: ("tugaphone", _trigger_construct),
59
+ Phonemizer.CUTLET: ("cutlet", _trigger_construct),
60
+ Phonemizer.PYKAKASI: ("pykakasi", _trigger_construct),
61
+ Phonemizer.VIPHONEME: ("viphoneme", _trigger_construct),
62
+ Phonemizer.G2PK: ("g2pk", _trigger_construct),
63
+ Phonemizer.JIEBA: ("jieba", _trigger_jieba),
64
+ }
65
+
66
+
67
+ class TestLazyBackendImportsAreFriendly(unittest.TestCase):
68
+ def test_all_documented_backends_covered(self):
69
+ # keeps this test list honest if a new Phonemizer member is added
70
+ # backed by one of the wrapper modules touched by this convention
71
+ self.assertEqual(len(CASES), 15)
72
+
73
+ def _assert_friendly(self, member):
74
+ module_name, trigger = CASES[member]
75
+ cls = get_phonemizer_class(member)
76
+ with patch.dict(sys.modules, {module_name: None}):
77
+ with self.assertRaises(ImportError, msg=member) as ctx:
78
+ trigger(cls)
79
+ msg = str(ctx.exception)
80
+ self.assertIn("pip install", msg, member)
81
+
82
+ def test_misaki(self):
83
+ self._assert_friendly(Phonemizer.MISAKI)
84
+
85
+ def test_misaki_en(self):
86
+ self._assert_friendly(Phonemizer.MISAKI_EN)
87
+
88
+ def test_gruut(self):
89
+ self._assert_friendly(Phonemizer.GRUUT)
90
+
91
+ def test_goruut(self):
92
+ self._assert_friendly(Phonemizer.GORUUT)
93
+
94
+ def test_epitran(self):
95
+ self._assert_friendly(Phonemizer.EPITRAN)
96
+
97
+ def test_transphone(self):
98
+ self._assert_friendly(Phonemizer.TRANSPHONE)
99
+
100
+ def test_deepphonemizer(self):
101
+ self._assert_friendly(Phonemizer.DEEPPHONEMIZER)
102
+
103
+ def test_openphonemizer(self):
104
+ self._assert_friendly(Phonemizer.OPENPHONEMIZER)
105
+
106
+ def test_g2pen(self):
107
+ self._assert_friendly(Phonemizer.G2PEN)
108
+
109
+ def test_tugaphone(self):
110
+ self._assert_friendly(Phonemizer.TUGAPHONE)
111
+
112
+ def test_cutlet(self):
113
+ self._assert_friendly(Phonemizer.CUTLET)
114
+
115
+ def test_pykakasi(self):
116
+ self._assert_friendly(Phonemizer.PYKAKASI)
117
+
118
+ def test_viphoneme(self):
119
+ self._assert_friendly(Phonemizer.VIPHONEME)
120
+
121
+ def test_g2pk(self):
122
+ self._assert_friendly(Phonemizer.G2PK)
123
+
124
+ def test_jieba(self):
125
+ self._assert_friendly(Phonemizer.JIEBA)
126
+
127
+
128
+ if __name__ == "__main__":
129
+ unittest.main()
File without changes
File without changes
File without changes