Skip to content

Commit

Permalink
Use packaging.version for version comparisons (#2310)
Browse files Browse the repository at this point in the history
* Use packaging.version for version comparisons

The distutils package is deprecated¹ and relies on PEP 386² version
comparisons, which have been superseded by PEP 440³ which is implemented
through the packaging module.

With more recent distutils versions, provided through setuptools
vendoring, we are seeing the following exception during version
comparisons:

> TypeError: '<' not supported between instances of 'str' and 'int'

This is fixed by this migration.

[1] https://docs.python.org/3/library/distutils.html
[2] https://peps.python.org/pep-0386/
[3] https://peps.python.org/pep-0440/

* Improve espeak version detection robustness

On many modern systems espeak is just a symlink to espeak-ng. In that
case looking for the 3rd word in the version output will break the
version comparison, when it finds `text-to-speech:`, instead of a proper
version.

This will not break during runtime, where espeak-ng would be
prioritized, but the phonemizer and tokenizer tests force the backend
to `espeak`, which exhibits this breakage.

This improves the version detection by simply looking for the version
after the "text-to-speech:" token.

* Replace distuils.copy_tree with shutil.copytree

The distutils module is deprecated and slated for removal in Python
3.12. Its usage should be replaced, in this case by a compatible method
from shutil.
  • Loading branch information
mweinelt committed Jan 29, 2023
1 parent c59b3f7 commit 994be16
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 17 deletions.
4 changes: 2 additions & 2 deletions TTS/bin/resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import glob
import os
from argparse import RawTextHelpFormatter
from distutils.dir_util import copy_tree
from multiprocessing import Pool
from shutil import copytree

import librosa
import soundfile as sf
Expand All @@ -19,7 +19,7 @@ def resample_file(func_args):
def resample_files(input_dir, output_sr, output_dir=None, file_ext="wav", n_jobs=10):
if output_dir:
print("Recursively copying the input folder...")
copy_tree(input_dir, output_dir)
copytree(input_dir, output_dir)
input_dir = output_dir

print("Resampling the audio files...")
Expand Down
5 changes: 2 additions & 3 deletions TTS/tts/layers/glow_tts/glow.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from distutils.version import LooseVersion

import torch
from packaging.version import Version
from torch import nn
from torch.nn import functional as F

Expand Down Expand Up @@ -91,7 +90,7 @@ def __init__(self, channels, num_splits=4, no_jacobian=False, **kwargs): # pyli
self.no_jacobian = no_jacobian
self.weight_inv = None

if LooseVersion(torch.__version__) < LooseVersion("1.9"):
if Version(torch.__version__) < Version("1.9"):
w_init = torch.qr(torch.FloatTensor(self.num_splits, self.num_splits).normal_())[0]
else:
w_init = torch.linalg.qr(torch.FloatTensor(self.num_splits, self.num_splits).normal_(), "complete")[0]
Expand Down
14 changes: 11 additions & 3 deletions TTS/tts/utils/text/phonemizers/espeak_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging
import re
import subprocess
from distutils.version import LooseVersion
from typing import Dict, List

from packaging.version import Version

from TTS.tts.utils.text.phonemizers.base import BasePhonemizer
from TTS.tts.utils.text.punctuation import Punctuation

Expand All @@ -14,9 +15,16 @@ def is_tool(name):
return which(name) is not None


# Use a regex pattern to match the espeak version, because it may be
# symlinked to espeak-ng, which moves the version bits to another spot.
espeak_version_pattern = re.compile(r"text-to-speech:\s(?P<version>\d+\.\d+(\.\d+)?)")


def get_espeak_version():
output = subprocess.getoutput("espeak --version")
return output.split()[2]
match = espeak_version_pattern.search(output)

return match.group("version")


def get_espeakng_version():
Expand Down Expand Up @@ -168,7 +176,7 @@ def phonemize_espeak(self, text: str, separator: str = "|", tie=False) -> str:
else:
# split with '_'
if self.backend == "espeak":
if LooseVersion(self.backend_version) >= LooseVersion("1.48.15"):
if Version(self.backend_version) >= Version("1.48.15"):
args.append("--ipa=1")
else:
args.append("--ipa=3")
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools", "wheel", "cython==0.29.28", "numpy==1.21.6"]
requires = ["setuptools", "wheel", "cython==0.29.28", "numpy==1.21.6", "packaging"]

[flake8]
max-line-length=120
Expand Down Expand Up @@ -30,4 +30,4 @@ exclude = '''
[tool.isort]
line_length = 120
profile = "black"
multi_line_output = 3
multi_line_output = 3
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ tqdm
anyascii
pyyaml
fsspec>=2021.04.0
packaging
# deps for examples
flask
# deps for inference
Expand Down
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
import os
import subprocess
import sys
from distutils.version import LooseVersion
from packaging.version import Version

import numpy
import setuptools.command.build_py
import setuptools.command.develop
from Cython.Build import cythonize
from setuptools import Extension, find_packages, setup

if LooseVersion(sys.version) < LooseVersion("3.7") or LooseVersion(sys.version) >= LooseVersion("3.11"):
python_version = sys.version.split()[0]
if Version(python_version) < Version("3.7") or Version(python_version) >= Version("3.11"):
raise RuntimeError("TTS requires python >= 3.7 and < 3.11 " "but your Python version is {}".format(sys.version))


Expand Down
11 changes: 6 additions & 5 deletions tests/text_tests/test_phonemizer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest
from distutils.version import LooseVersion

from packaging.version import Version

from TTS.tts.utils.text.phonemizers import ESpeak, Gruut, JA_JP_Phonemizer, ZH_CN_Phonemizer
from TTS.tts.utils.text.phonemizers.multi_phonemizer import MultiPhonemizer
Expand Down Expand Up @@ -40,7 +41,7 @@ class TestEspeakPhonemizer(unittest.TestCase):
def setUp(self):
self.phonemizer = ESpeak(language="en-us", backend="espeak")

if LooseVersion(self.phonemizer.backend_version) >= LooseVersion("1.48.15"):
if Version(self.phonemizer.backend_version) >= Version("1.48.15"):
target_phonemes = EXPECTED_ESPEAK_v1_48_15_PHONEMES
else:
target_phonemes = EXPECTED_ESPEAK_PHONEMES
Expand All @@ -52,7 +53,7 @@ def setUp(self):
# multiple punctuations
text = "Be a voice, not an! echo?"
gt = "biː ɐ vˈɔɪs, nˈɑːt ɐn! ˈɛkoʊ?"
if LooseVersion(self.phonemizer.backend_version) >= LooseVersion("1.48.15"):
if Version(self.phonemizer.backend_version) >= Version("1.48.15"):
gt = "biː ɐ vˈɔɪs, nˈɑːt æn! ˈɛkoʊ?"
output = self.phonemizer.phonemize(text, separator="|")
output = output.replace("|", "")
Expand All @@ -61,15 +62,15 @@ def setUp(self):
# not ending with punctuation
text = "Be a voice, not an! echo"
gt = "biː ɐ vˈɔɪs, nˈɑːt ɐn! ˈɛkoʊ"
if LooseVersion(self.phonemizer.backend_version) >= LooseVersion("1.48.15"):
if Version(self.phonemizer.backend_version) >= Version("1.48.15"):
gt = "biː ɐ vˈɔɪs, nˈɑːt æn! ˈɛkoʊ"
output = self.phonemizer.phonemize(text, separator="")
self.assertEqual(output, gt)

# extra space after the sentence
text = "Be a voice, not an! echo. "
gt = "biː ɐ vˈɔɪs, nˈɑːt ɐn! ˈɛkoʊ."
if LooseVersion(self.phonemizer.backend_version) >= LooseVersion("1.48.15"):
if Version(self.phonemizer.backend_version) >= Version("1.48.15"):
gt = "biː ɐ vˈɔɪs, nˈɑːt æn! ˈɛkoʊ."
output = self.phonemizer.phonemize(text, separator="")
self.assertEqual(output, gt)
Expand Down

0 comments on commit 994be16

Please sign in to comment.