Skip to content

Commit

Permalink
Merge pull request #389 from cclauss/still-more-ruff
Browse files Browse the repository at this point in the history
Still more ruff rules
  • Loading branch information
willwade authored Nov 18, 2024
2 parents 33e1a3a + 6a51a4c commit fbfae25
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 144 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python3 # noqa: EXE001
#
# pyttsx3 documentation build configuration file, created by
# sphinx-quickstart on Sun Jun 25 11:19:31 2017.
Expand Down
Empty file added example/__init__.py
Empty file.
8 changes: 2 additions & 6 deletions example/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,14 @@


# VOLUME
volume = engine.getProperty(
"volume"
) # getting to know current volume level (min=0 and max=1)
volume = engine.getProperty("volume") # getting to know current volume level (min=0 and max=1)
print(volume) # printing current volume level
engine.setProperty("volume", 1.0) # setting up volume level between 0 and 1

# VOICE
voices = engine.getProperty("voices") # getting details of current voice
# engine.setProperty('voice', voices[0].id) #changing index, changes voices. 0 for male
engine.setProperty(
"voice", voices[1].id
) # changing index, changes voices. 1 for female
engine.setProperty("voice", voices[1].id) # changing index, changes voices. 1 for female

# PITCH
pitch = engine.getProperty("pitch") # Get current pitch value
Expand Down
78 changes: 77 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,81 @@ packages = [ "pyttsx3", "pyttsx3.drivers" ]
include-package-data = false

[tool.ruff]
line-length = 100
target-version = "py39"
line-length = 88

[tool.ruff.lint]
select = [
"AIR", # Airflow
"ASYNC", # flake8-async
"B", # flake8-bugbear
"BLE", # flake8-blind-except
"C4", # flake8-comprehensions
"C90", # McCabe cyclomatic complexity
"DJ", # flake8-django
"DTZ", # flake8-datetimez
"E", # pycodestyle
"EM", # flake8-errmsg
"EXE", # flake8-executable
"F", # Pyflakes
"FA", # flake8-future-annotations
"FLY", # flynt
"FURB", # refurb
"G", # flake8-logging-format
"I", # isort
"ICN", # flake8-import-conventions
"INP", # flake8-no-pep420
"INT", # flake8-gettext
"ISC", # flake8-implicit-str-concat
"LOG", # flake8-logging
"NPY", # NumPy-specific rules
"PD", # pandas-vet
"PERF", # Perflint
"PGH", # pygrep-hooks
"PIE", # flake8-pie
"PL", # Pylint
"PT", # flake8-pytest-style
"PTH", # flake8-use-pathlib
"PYI", # flake8-pyi
"RET", # flake8-return
"RSE", # flake8-raise
"RUF", # Ruff-specific rules
"SIM", # flake8-simplify
"SLOT", # flake8-slots
"T10", # flake8-debugger
"TCH", # flake8-type-checking
"TID", # flake8-tidy-imports
"UP", # pyupgrade
"W", # pycodestyle
"YTT", # flake8-2020
# "A", # flake8-builtins
# "ANN", # flake8-annotations
# "ARG", # flake8-unused-arguments
# "COM", # flake8-commas
# "CPY", # flake8-copyright
# "D", # pydocstyle
# "DOC", # pydoclint
# "ERA", # eradicate
# "FAST", # FastAPI
# "FBT", # flake8-boolean-trap
# "FIX", # flake8-fixme
# "N", # pep8-naming
# "Q", # flake8-quotes
# "S", # flake8-bandit
# "SLF", # flake8-self
# "T20", # flake8-print
# "TD", # flake8-todos
# "TRY", # tryceratops
]
ignore = [
"B904", # raise-without-from-inside-except
"BLE001", # blind-except
"D212", # Multi-line docstring summary should start at the first line
"ISC001", # implicit-str-concat conflicts with ruff format
"S101", # assert
]
[tool.ruff.lint.per-file-ignores]
"pyttsx3/drivers/_espeak.py" = ["E101", "E501"]

[tool.ruff.lint.pylint]
allow-magic-value-types = ["int", "str"]
38 changes: 16 additions & 22 deletions pyttsx3/drivers/_espeak.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ def cfunc(name, dll, result, *args):


def load_library() -> bool:
global dll
paths = [
global dll # noqa: PLW0603
paths = (
# macOS paths
"/opt/homebrew/lib/libespeak-ng.1.dylib",
"/usr/local/lib/libespeak-ng.1.dylib",
Expand All @@ -44,13 +44,13 @@ def load_library() -> bool:
# Windows paths
r"C:\Program Files\eSpeak NG\libespeak-ng.dll",
r"C:\Program Files (x86)\eSpeak NG\libespeak-ng.dll",
]
)

for path in paths:
try:
dll = cdll.LoadLibrary(path)
return True
except Exception:
except Exception: # noqa: PERF203
continue # Try the next path
return False

Expand All @@ -74,11 +74,11 @@ def load_library() -> bool:


class numberORname(Union):
_fields_ = [("number", c_int), ("name", c_char_p)]
_fields_ = (("number", c_int), ("name", c_char_p))


class EVENT(Structure):
_fields_ = [
_fields_ = (
("type", c_int),
("unique_identifier", c_uint),
("text_position", c_int),
Expand All @@ -87,7 +87,7 @@ class EVENT(Structure):
("sample", c_int),
("user_data", c_void_p),
("id", numberORname),
]
)


AUDIO_OUTPUT_PLAYBACK = 0
Expand Down Expand Up @@ -126,7 +126,7 @@ class EVENT(Structure):


def SetSynthCallback(cb) -> None:
global SynthCallback
global SynthCallback # noqa: PLW0603
SynthCallback = t_espeak_callback(cb)
cSetSynthCallback(SynthCallback)

Expand Down Expand Up @@ -155,14 +155,12 @@ def SetSynthCallback(cb) -> None:

t_UriCallback = CFUNCTYPE(c_int, c_int, c_char_p, c_char_p)

cSetUriCallback = cfunc(
"espeak_SetUriCallback", dll, None, ("UriCallback", t_UriCallback, 1)
)
cSetUriCallback = cfunc("espeak_SetUriCallback", dll, None, ("UriCallback", t_UriCallback, 1))
UriCallback = None


def SetUriCallback(cb) -> None:
global UriCallback
global UriCallback # noqa: PLW0603
UriCallback = t_UriCallback(UriCallback)
cSetUriCallback(UriCallback)

Expand Down Expand Up @@ -202,7 +200,7 @@ def SetUriCallback(cb) -> None:
POS_SENTENCE = 3


def Synth(
def Synth( # noqa: PLR0913
text,
position=0,
position_type=POS_CHARACTER,
Expand Down Expand Up @@ -384,9 +382,7 @@ def Synth_Mark(text, index_mark, end_position=0, flags=CHARS_AUTO) -> None:
GetParameter.__doc__ = """current=0 Returns the default value of the specified parameter.
current=1 Returns the current value of the specified parameter, as set by SetParameter()"""

SetPunctuationList = cfunc(
"espeak_SetPunctuationList", dll, c_int, ("punctlist", c_wchar, 1)
)
SetPunctuationList = cfunc("espeak_SetPunctuationList", dll, c_int, ("punctlist", c_wchar, 1))
SetPunctuationList.__doc__ = """Specified a list of punctuation characters whose names are
to be spoken when the value of the Punctuation parameter is set to "some".
Expand Down Expand Up @@ -419,7 +415,7 @@ def Synth_Mark(text, index_mark, end_position=0, flags=CHARS_AUTO) -> None:


class VOICE(Structure):
_fields_ = [
_fields_ = (
("name", c_char_p),
("languages", c_char_p),
("identifier", c_char_p),
Expand All @@ -429,14 +425,12 @@ class VOICE(Structure):
("xx1", c_ubyte),
("score", c_int),
("spare", c_void_p),
]
)

def __repr__(self) -> str:
"""Print the fields."""
res = []
for field in self._fields_:
res.append(f"{field[0]}={getattr(self, field[0])!r}")
return self.__class__.__name__ + "(" + ",".join(res) + ")"
res = ",".join(f"{field[0]}={getattr(self, field[0])!r}" for field in self._fields_)
return f"{self.__class__.__name__}({res})"


cListVoices = cfunc(
Expand Down
2 changes: 1 addition & 1 deletion pyttsx3/drivers/avspeech.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from collections.abc import Iterator


def buildDriver(proxy): # noqa: N802, ANN001, ANN201
def buildDriver(proxy):
"""Build an AVSpeech driver instance."""
driver = AVSpeechDriver.alloc().init()
driver.setProxy(proxy)
Expand Down
65 changes: 33 additions & 32 deletions pyttsx3/drivers/espeak.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import ctypes
import logging
import os
import platform
import subprocess
import time
import wave
from tempfile import NamedTemporaryFile
import logging

if platform.system() == "Windows":
import winsound

from ..voice import Voice
from pyttsx3.voice import Voice

from . import _espeak


Expand All @@ -31,7 +32,8 @@ def __init__(self, proxy):
# so just keep it alive and init once
rate = _espeak.Initialize(_espeak.AUDIO_OUTPUT_RETRIEVAL, 1000)
if rate == -1:
raise RuntimeError("could not initialize espeak")
msg = "could not initialize espeak"
raise RuntimeError(msg)
current_voice = _espeak.GetCurrentVoice()
if current_voice and current_voice.contents.name:
EspeakDriver._defaultVoice = current_voice.contents.name.decode("utf-8")
Expand Down Expand Up @@ -85,9 +87,7 @@ def getProperty(name: str):
if v.languages:
try:
language_code_bytes = v.languages[1:]
language_code = language_code_bytes.decode(
"utf-8", errors="ignore"
)
language_code = language_code_bytes.decode("utf-8", errors="ignore")
kwargs["languages"] = [language_code]
except UnicodeDecodeError:
kwargs["languages"] = ["Unknown"]
Expand All @@ -107,33 +107,35 @@ def getProperty(name: str):
return _espeak.GetParameter(_espeak.VOLUME) / 100.0
if name == "pitch":
return _espeak.GetParameter(_espeak.PITCH)
raise KeyError("unknown property %s" % name)
msg = f"unknown property {name}"
raise KeyError(msg)

@staticmethod
def setProperty(name: str, value):
def setProperty(name: str, value): # noqa: C901,PLR0912
if name == "voice":
if value is None:
return
try:
utf8Value = str(value).encode("utf-8")
logging.debug(f"Attempting to set voice to: {value}")
logging.debug(f"Attempting to set voice to: {value}") # noqa: G004
result = _espeak.SetVoiceByName(utf8Value)
if result == 0: # EE_OK is 0
logging.debug(f"Successfully set voice to: {value}")
logging.debug(f"Successfully set voice to: {value}") # noqa: G004
elif result == 1: # EE_BUFFER_FULL
raise ValueError(
f"SetVoiceByName failed: EE_BUFFER_FULL while setting voice to {value}"
)
msg = f"SetVoiceByName failed: EE_BUFFER_FULL while setting voice to {value}"
raise ValueError(msg)
elif result == 2: # EE_INTERNAL_ERROR
raise ValueError(
f"SetVoiceByName failed: EE_INTERNAL_ERROR while setting voice to {value}"
)
msg = f"SetVoiceByName failed: EE_INTERNAL_ERROR while setting voice to {value}"
raise ValueError(msg)
else:
raise ValueError(
f"SetVoiceByName failed with unknown return code {result} for voice: {value}"
msg = (
"SetVoiceByName "
f"ailed with unknown return code {result} for voice: {value}"
)
raise ValueError(msg)
except ctypes.ArgumentError as e:
raise ValueError(f"Invalid voice name: {value}, error: {e}")
msg = f"Invalid voice name: {value}, error: {e}"
raise ValueError(msg)
elif name == "rate":
try:
_espeak.SetParameter(_espeak.RATE, value, 0)
Expand All @@ -150,7 +152,8 @@ def setProperty(name: str, value):
except TypeError as e:
raise ValueError(str(e))
else:
raise KeyError("unknown property %s" % name)
msg = f"unknown property {name}"
raise KeyError(msg)

def save_to_file(self, text, filename):
"""
Expand All @@ -165,15 +168,16 @@ def _start_synthesis(self, text):
self._speaking = True
self._data_buffer = b"" # Ensure buffer is cleared before starting
try:
_espeak.Synth(
str(text).encode("utf-8"), flags=_espeak.ENDPAUSE | _espeak.CHARS_UTF8
)
_espeak.Synth(str(text).encode("utf-8"), flags=_espeak.ENDPAUSE | _espeak.CHARS_UTF8)
except Exception as e:
self._proxy.setBusy(False)
self._proxy.notify("error", exception=e)
raise

def _onSynth(self, wav, numsamples, events):
def _onSynth(self, wav, numsamples, events): # noqa: C901,PLR0912,PLR0915
"""
TODO: Refactor this function because it is too complex by several measures.
"""
if not self._speaking:
return 0

Expand Down Expand Up @@ -208,12 +212,11 @@ def _onSynth(self, wav, numsamples, events):
f.writeframes(self._data_buffer)
print(f"Audio saved to {self._save_file}")
except Exception as e:
raise RuntimeError(f"Error saving WAV file: {e}")
msg = f"Error saving WAV file: {e}"
raise RuntimeError(msg)
else:
try:
with NamedTemporaryFile(
suffix=".wav", delete=False
) as temp_wav:
with NamedTemporaryFile(suffix=".wav", delete=False) as temp_wav:
with wave.open(temp_wav, "wb") as f:
f.setnchannels(1) # Mono
f.setsampwidth(2) # 16-bit samples
Expand All @@ -232,7 +235,7 @@ def _onSynth(self, wav, numsamples, events):
winsound.PlaySound(temp_wav_name, winsound.SND_FILENAME)

# Remove the file after playback
os.remove(temp_wav_name)
os.remove(temp_wav_name) # noqa: PTH107
except Exception as e:
print(f"Playback error: {e}")

Expand All @@ -248,9 +251,7 @@ def _onSynth(self, wav, numsamples, events):

# Accumulate audio data if available
if numsamples > 0:
self._data_buffer += ctypes.string_at(
wav, numsamples * ctypes.sizeof(ctypes.c_short)
)
self._data_buffer += ctypes.string_at(wav, numsamples * ctypes.sizeof(ctypes.c_short))

return 0

Expand Down
Loading

0 comments on commit fbfae25

Please sign in to comment.