refactor(espeak_wrapper): fix ruff lint suggestions

This commit is contained in:
Enno Hermann 2024-05-01 13:31:39 +02:00
parent 7b2289a454
commit 962f9bbbcf
2 changed files with 44 additions and 41 deletions

View File

@ -1,7 +1,9 @@
"""Wrapper to call the espeak/espeak-ng phonemizer."""
import logging import logging
import re import re
import subprocess import subprocess
from typing import Dict, List from typing import Optional
from packaging.version import Version from packaging.version import Version
@ -11,7 +13,7 @@ from TTS.tts.utils.text.punctuation import Punctuation
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def is_tool(name): def _is_tool(name) -> bool:
from shutil import which from shutil import which
return which(name) is not None return which(name) is not None
@ -22,23 +24,25 @@ def is_tool(name):
espeak_version_pattern = re.compile(r"text-to-speech:\s(?P<version>\d+\.\d+(\.\d+)?)") espeak_version_pattern = re.compile(r"text-to-speech:\s(?P<version>\d+\.\d+(\.\d+)?)")
def get_espeak_version(): def get_espeak_version() -> str:
"""Return version of the `espeak` binary."""
output = subprocess.getoutput("espeak --version") output = subprocess.getoutput("espeak --version")
match = espeak_version_pattern.search(output) match = espeak_version_pattern.search(output)
return match.group("version") return match.group("version")
def get_espeakng_version(): def get_espeakng_version() -> str:
"""Return version of the `espeak-ng` binary."""
output = subprocess.getoutput("espeak-ng --version") output = subprocess.getoutput("espeak-ng --version")
return output.split()[3] return output.split()[3]
# priority: espeakng > espeak # priority: espeakng > espeak
if is_tool("espeak-ng"): if _is_tool("espeak-ng"):
_DEF_ESPEAK_LIB = "espeak-ng" _DEF_ESPEAK_LIB = "espeak-ng"
_DEF_ESPEAK_VER = get_espeakng_version() _DEF_ESPEAK_VER = get_espeakng_version()
elif is_tool("espeak"): elif _is_tool("espeak"):
_DEF_ESPEAK_LIB = "espeak" _DEF_ESPEAK_LIB = "espeak"
_DEF_ESPEAK_VER = get_espeak_version() _DEF_ESPEAK_VER = get_espeak_version()
else: else:
@ -46,7 +50,7 @@ else:
_DEF_ESPEAK_VER = None _DEF_ESPEAK_VER = None
def _espeak_exe(espeak_lib: str, args: List, sync=False) -> List[str]: def _espeak_exe(espeak_lib: str, args: list, *, sync: bool = False) -> list[bytes]:
"""Run espeak with the given arguments.""" """Run espeak with the given arguments."""
cmd = [ cmd = [
espeak_lib, espeak_lib,
@ -73,9 +77,7 @@ def _espeak_exe(espeak_lib: str, args: List, sync=False) -> List[str]:
if p.stdin: if p.stdin:
p.stdin.close() p.stdin.close()
return res return res
res2 = [] res2 = list(res)
for line in res:
res2.append(line)
p.stdout.close() p.stdout.close()
if p.stderr: if p.stderr:
p.stderr.close() p.stderr.close()
@ -86,7 +88,7 @@ def _espeak_exe(espeak_lib: str, args: List, sync=False) -> List[str]:
class ESpeak(BasePhonemizer): class ESpeak(BasePhonemizer):
"""ESpeak wrapper calling `espeak` or `espeak-ng` from the command-line the perform G2P """Wrapper calling `espeak` or `espeak-ng` from the command-line to perform G2P.
Args: Args:
language (str): language (str):
@ -111,13 +113,17 @@ class ESpeak(BasePhonemizer):
""" """
_ESPEAK_LIB = _DEF_ESPEAK_LIB def __init__(
_ESPEAK_VER = _DEF_ESPEAK_VER self,
language: str,
def __init__(self, language: str, backend=None, punctuations=Punctuation.default_puncs(), keep_puncs=True): backend: Optional[str] = None,
if self._ESPEAK_LIB is None: punctuations: str = Punctuation.default_puncs(),
raise Exception(" [!] No espeak backend found. Install espeak-ng or espeak to your system.") keep_puncs: bool = True,
self.backend = self._ESPEAK_LIB ):
if _DEF_ESPEAK_LIB is None:
msg = "[!] No espeak backend found. Install espeak-ng or espeak to your system."
raise FileNotFoundError(msg)
self.backend = _DEF_ESPEAK_LIB
# band-aid for backwards compatibility # band-aid for backwards compatibility
if language == "en": if language == "en":
@ -130,35 +136,37 @@ class ESpeak(BasePhonemizer):
self.backend = backend self.backend = backend
@property @property
def backend(self): def backend(self) -> str:
return self._ESPEAK_LIB return self._ESPEAK_LIB
@property @property
def backend_version(self): def backend_version(self) -> str:
return self._ESPEAK_VER return self._ESPEAK_VER
@backend.setter @backend.setter
def backend(self, backend): def backend(self, backend: str) -> None:
if backend not in ["espeak", "espeak-ng"]: if backend not in ["espeak", "espeak-ng"]:
raise Exception("Unknown backend: %s" % backend) msg = f"Unknown backend: {backend}"
raise ValueError(msg)
self._ESPEAK_LIB = backend self._ESPEAK_LIB = backend
self._ESPEAK_VER = get_espeakng_version() if backend == "espeak-ng" else get_espeak_version() self._ESPEAK_VER = get_espeakng_version() if backend == "espeak-ng" else get_espeak_version()
def auto_set_espeak_lib(self) -> None: def auto_set_espeak_lib(self) -> None:
if is_tool("espeak-ng"): if _is_tool("espeak-ng"):
self._ESPEAK_LIB = "espeak-ng" self._ESPEAK_LIB = "espeak-ng"
self._ESPEAK_VER = get_espeakng_version() self._ESPEAK_VER = get_espeakng_version()
elif is_tool("espeak"): elif _is_tool("espeak"):
self._ESPEAK_LIB = "espeak" self._ESPEAK_LIB = "espeak"
self._ESPEAK_VER = get_espeak_version() self._ESPEAK_VER = get_espeak_version()
else: else:
raise Exception("Cannot set backend automatically. espeak-ng or espeak not found") msg = "Cannot set backend automatically. espeak-ng or espeak not found"
raise FileNotFoundError(msg)
@staticmethod @staticmethod
def name(): def name() -> str:
return "espeak" return "espeak"
def phonemize_espeak(self, text: str, separator: str = "|", tie=False) -> str: def phonemize_espeak(self, text: str, separator: str = "|", *, tie: bool = False) -> str:
"""Convert input text to phonemes. """Convert input text to phonemes.
Args: Args:
@ -193,7 +201,7 @@ class ESpeak(BasePhonemizer):
args.append(text) args.append(text)
# compute phonemes # compute phonemes
phonemes = "" phonemes = ""
for line in _espeak_exe(self._ESPEAK_LIB, args, sync=True): for line in _espeak_exe(self.backend, args, sync=True):
logger.debug("line: %s", repr(line)) logger.debug("line: %s", repr(line))
ph_decoded = line.decode("utf8").strip() ph_decoded = line.decode("utf8").strip()
# espeak: # espeak:
@ -210,11 +218,11 @@ class ESpeak(BasePhonemizer):
phonemes += ph_decoded.strip() phonemes += ph_decoded.strip()
return phonemes.replace("_", separator) return phonemes.replace("_", separator)
def _phonemize(self, text, separator=None): def _phonemize(self, text: str, separator: str = "") -> str:
return self.phonemize_espeak(text, separator, tie=False) return self.phonemize_espeak(text, separator, tie=False)
@staticmethod @staticmethod
def supported_languages() -> Dict: def supported_languages() -> dict[str, str]:
"""Get a dictionary of supported languages. """Get a dictionary of supported languages.
Returns: Returns:
@ -224,8 +232,7 @@ class ESpeak(BasePhonemizer):
return {} return {}
args = ["--voices"] args = ["--voices"]
langs = {} langs = {}
count = 0 for count, line in enumerate(_espeak_exe(_DEF_ESPEAK_LIB, args, sync=True)):
for line in _espeak_exe(_DEF_ESPEAK_LIB, args, sync=True):
line = line.decode("utf8").strip() line = line.decode("utf8").strip()
if count > 0: if count > 0:
cols = line.split() cols = line.split()
@ -233,7 +240,6 @@ class ESpeak(BasePhonemizer):
lang_name = cols[3] lang_name = cols[3]
langs[lang_code] = lang_name langs[lang_code] = lang_name
logger.debug("line: %s", repr(line)) logger.debug("line: %s", repr(line))
count += 1
return langs return langs
def version(self) -> str: def version(self) -> str:
@ -242,16 +248,12 @@ class ESpeak(BasePhonemizer):
Returns: Returns:
str: Version of the used backend. str: Version of the used backend.
""" """
args = ["--version"] return self.backend_version
for line in _espeak_exe(self.backend, args, sync=True):
version = line.decode("utf8").strip().split()[2]
logger.debug("line: %s", repr(line))
return version
@classmethod @classmethod
def is_available(cls): def is_available(cls) -> bool:
"""Return true if ESpeak is available else false""" """Return true if ESpeak is available else false."""
return is_tool("espeak") or is_tool("espeak-ng") return _is_tool("espeak") or _is_tool("espeak-ng")
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -8,6 +8,7 @@ requires = [
] ]
[tool.ruff] [tool.ruff]
target-version = "py39"
line-length = 120 line-length = 120
lint.extend-select = [ lint.extend-select = [
"B033", # duplicate-value "B033", # duplicate-value