Skip to content

Commit c6492ab

Browse files
committed
Set methods to static objects
Improve exception handling Remove exit calls Fix docstrings Adhere to pep8 standards Remove custom six.py
1 parent ae9279a commit c6492ab

File tree

11 files changed

+64
-900
lines changed

11 files changed

+64
-900
lines changed

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ dependencies = [
55
"comtypes; platform_system == 'Windows'",
66
"pypiwin32; platform_system == 'Windows'",
77
"pywin32; platform_system == 'Windows'",
8-
"pyobjc>=2.4; platform_system == 'Darwin'"
8+
"pyobjc>=2.4; platform_system == 'Darwin'",
9+
"six"
910
]
1011
description = "Text to Speech (TTS) library for Python 3. Works without internet connection or delay. Supports multiple TTS engines, including Sapi5, nsss, and espeak."
1112
readme = "README.md"

pyttsx3/__init__.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
_activeEngines = weakref.WeakValueDictionary()
66

77

8-
def init(driverName=None, debug=False):
8+
# noinspection PyPep8Naming
9+
def init(driverName: str = None, debug: bool = False) -> Engine:
910
"""
1011
Constructs a new TTS engine instance or reuses the existing instance for
1112
the driver name.
@@ -21,12 +22,11 @@ def init(driverName=None, debug=False):
2122
try:
2223
eng = _activeEngines[driverName]
2324
except KeyError:
24-
eng = Engine(driverName, debug)
25-
_activeEngines[driverName] = eng
25+
try:
26+
eng = Engine(driverName=driverName, debug=debug)
27+
except ModuleNotFoundError:
28+
raise ValueError("\n\nDriver '%s' is unavailable" % driverName)
29+
except Exception:
30+
raise RuntimeError("\n\nUnable to load driver '%s'" % driverName)
31+
_activeEngines[driverName] = eng
2632
return eng
27-
28-
29-
def speak(text):
30-
engine = init()
31-
engine.say(text)
32-
engine.runAndWait()

pyttsx3/driver.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import weakref
55

66

7+
# noinspection PyPep8Naming
78
class DriverProxy(object):
89
"""
910
Proxy to a driver implementation.
@@ -25,7 +26,7 @@ class DriverProxy(object):
2526
@type _iterator: iterator
2627
"""
2728

28-
def __init__(self, engine, driverName, debug):
29+
def __init__(self, engine, driverName: str, debug: bool):
2930
"""
3031
Constructor.
3132
@@ -54,7 +55,7 @@ def __init__(self, engine, driverName, debug):
5455
self._engine = engine
5556
self._queue = []
5657
self._busy = True
57-
self._name = None
58+
self._name = ""
5859
self._iterator = None
5960
self._debug = debug
6061

@@ -89,10 +90,11 @@ def _pump(self):
8990
try:
9091
cmd[0](*cmd[1])
9192
except Exception as e:
92-
self.notify('error', exception=e)
93+
self.notify('error', exception=e.__dict__)
9394
if self._debug:
9495
traceback.print_exc()
9596

97+
# noinspection PyProtectedMember,PyTypeChecker
9698
def notify(self, topic, **kwargs):
9799
"""
98100
Sends a notification to the engine from the driver.
@@ -156,6 +158,8 @@ def save_to_file(self, text, filename, name):
156158
157159
@param text: Text to speak
158160
@type text: unicode
161+
@param filename: Name of the file
162+
@type filename: str
159163
@param name: Name to associate with the utterance
160164
@type name: str
161165
"""

pyttsx3/drivers/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""
22
Utility functions to help with Python 2/3 compatibility
33
"""
4-
from .. import six
4+
import six
55

66

7+
# noinspection PyPep8Naming
78
def toUtf8(value):
89
"""
910
Takes in a value and converts it to a text (unicode) type. Then decodes that
@@ -15,6 +16,7 @@ def toUtf8(value):
1516
return six.text_type(value).encode('utf-8')
1617

1718

19+
# noinspection PyPep8Naming
1820
def fromUtf8(value):
1921
"""
2022
Takes in a byte array encoded as utf-8 and returns a text (unicode) type. In

pyttsx3/drivers/_espeak.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,7 @@ def load_windows_epng3():
8282
load_linux_ep() or load_linux_epng() or load_linux_epng2() or load_windows_epng1() or load_windows_epng2() or load_windows_epng3()
8383
except Exception as exp:
8484
print("Exception: " + str(exp) + "\n")
85-
print("This means you probably do not have eSpeak or eSpeak-ng installed!")
86-
import sys
87-
88-
sys.exit()
85+
raise RuntimeError("This means you probably do not have eSpeak or eSpeak-ng installed!")
8986

9087
# constants and such from speak_lib.h
9188

pyttsx3/drivers/dummy.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from ..voice import Voice
44

55

6+
# noinspection PyPep8Naming
67
def buildDriver(proxy):
78
"""
89
Builds a new instance of a driver and returns it for use by the driver
@@ -14,6 +15,7 @@ def buildDriver(proxy):
1415
return DummyDriver(proxy)
1516

1617

18+
# noinspection PyPep8Naming,PyBroadException
1719
class DummyDriver(object):
1820
"""
1921
Dummy speech engine implementation. Documents the interface, notifications,
@@ -53,7 +55,7 @@ def __init__(self, proxy):
5355
def destroy(self):
5456
"""
5557
Optional method that will be called when the driver proxy is being
56-
destroyed. Can cleanup any resources to make sure the engine terminates
58+
destroyed. Can clean up any resources to make sure the engine terminates
5759
properly.
5860
"""
5961
pass
@@ -157,7 +159,7 @@ def getProperty(self, name):
157159
def setProperty(self, name, value):
158160
"""
159161
Sets one of the supported property values of the speech engine listed
160-
above. If a value is invalid, attempts to clip it / coerce so it is
162+
above. If a value is invalid, attempts to clip it / coerce, so it is
161163
valid before giving up and firing an exception.
162164
163165
@param name: Property name

pyttsx3/drivers/espeak.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
from ..voice import Voice
99

1010

11+
# noinspection PyPep8Naming
1112
def buildDriver(proxy):
1213
return EspeakDriver(proxy)
1314

1415

16+
# noinspection PyPep8Naming
1517
class EspeakDriver(object):
1618
_moduleInitialized = False
1719
_defaultVoice = ''
@@ -44,26 +46,25 @@ def numerise(self, data):
4446
def decode_numeric(self, data):
4547
return self._numerise_buffer[int(data) - 1]
4648

47-
def destroy(self):
49+
@staticmethod
50+
def destroy():
4851
_espeak.SetSynthCallback(None)
4952

5053
def say(self, text):
5154
self._proxy.setBusy(True)
5255
self._proxy.notify('started-utterance')
53-
_espeak.Synth(toUtf8(text), flags=_espeak.ENDPAUSE |
54-
_espeak.CHARS_UTF8)
56+
_espeak.Synth(toUtf8(text), flags=_espeak.ENDPAUSE | _espeak.CHARS_UTF8)
5557

5658
def stop(self):
5759
if _espeak.IsPlaying():
5860
self._stopping = True
5961

60-
def getProperty(self, name):
62+
@staticmethod
63+
def getProperty(name: str):
6164
if name == 'voices':
6265
voices = []
6366
for v in _espeak.ListVoices(None):
64-
kwargs = {}
65-
kwargs['id'] = fromUtf8(v.name)
66-
kwargs['name'] = fromUtf8(v.name)
67+
kwargs = {'id': fromUtf8(v.name), 'name': fromUtf8(v.name)}
6768
if v.languages:
6869
kwargs['languages'] = [v.languages]
6970
genders = [None, 'male', 'female']
@@ -83,7 +84,8 @@ def getProperty(self, name):
8384
else:
8485
raise KeyError('unknown property %s' % name)
8586

86-
def setProperty(self, name, value):
87+
@staticmethod
88+
def setProperty(name: str, value):
8789
if name == 'voice':
8890
if value is None:
8991
return
@@ -133,8 +135,7 @@ def startLoop(self):
133135

134136
def save_to_file(self, text, filename):
135137
code = self.numerise(filename)
136-
_espeak.Synth(toUtf8(text), flags=_espeak.ENDPAUSE |
137-
_espeak.CHARS_UTF8, user_data=code)
138+
_espeak.Synth(toUtf8(text), flags=_espeak.ENDPAUSE | _espeak.CHARS_UTF8, user_data=code)
138139

139140
def endLoop(self):
140141
self._looping = False

pyttsx3/drivers/nsss.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1+
# noinspection PyUnresolvedReferences
12
from AppKit import NSSpeechSynthesizer
23
from Foundation import *
34
from PyObjCTools import AppHelper
45

56
from ..voice import Voice
67

78

9+
# noinspection PyPep8Naming
810
def buildDriver(proxy):
911
return NSSpeechDriver.alloc().initWithProxy(proxy)
1012

1113

14+
# noinspection PyUnresolvedReferences,PyPep8Naming,PyUnusedLocal
1215
class NSSpeechDriver(NSObject):
16+
17+
def __init__(self):
18+
self._proxy = None
19+
self._tts = None
20+
self._completed = False
21+
1322
@objc.python_method
1423
def initWithProxy(self, proxy):
1524
try:
@@ -37,7 +46,8 @@ def startLoop(self):
3746
0.0, self, 'onPumpFirst:', None, False)
3847
AppHelper.runConsoleEventLoop()
3948

40-
def endLoop(self):
49+
@staticmethod
50+
def endLoop():
4151
AppHelper.stopEventLoop()
4252

4353
def iterate(self):

pyttsx3/drivers/sapi5.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# noinspection PyUnresolvedReferences
12
import comtypes.client # Importing comtypes.client will make the gen subpackage
23

34
try:
@@ -6,8 +7,10 @@
67
# Generate the SpeechLib lib and any associated files
78
engine = comtypes.client.CreateObject("SAPI.SpVoice")
89
stream = comtypes.client.CreateObject("SAPI.SpFileStream")
10+
# noinspection PyUnresolvedReferences
911
from comtypes.gen import SpeechLib
1012

13+
# noinspection PyUnresolvedReferences
1114
import pythoncom
1215
import time
1316
import math
@@ -27,10 +30,12 @@
2730
MSMIKE: (154.37, 1.11)}
2831

2932

33+
# noinspection PyPep8Naming
3034
def buildDriver(proxy):
3135
return SAPI5Driver(proxy)
3236

3337

38+
# noinspection PyPep8Naming,PyShadowingNames
3439
class SAPI5Driver(object):
3540
def __init__(self, proxy):
3641
self._tts = comtypes.client.CreateObject('SAPI.SPVoice')
@@ -74,7 +79,8 @@ def save_to_file(self, text, filename):
7479
stream.close()
7580
os.chdir(cwd)
7681

77-
def _toVoice(self, attr):
82+
@staticmethod
83+
def _toVoice(attr):
7884
return Voice(attr.Id, attr.GetDescription())
7985

8086
def _tokenFromId(self, id_):
@@ -142,6 +148,7 @@ def iterate(self):
142148
yield
143149

144150

151+
# noinspection PyPep8Naming,PyProtectedMember,PyUnusedLocal,PyShadowingNames
145152
class SAPI5DriverEventSink(object):
146153
def __init__(self):
147154
self._driver = None

pyttsx3/engine.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
from . import driver
55

66

7+
# noinspection PyPep8Naming
78
class Engine(object):
89
"""
910
@ivar proxy: Proxy to a driver implementation
1011
@type proxy: L{DriverProxy}
11-
@ivar _connects: Array of subscriptions
12-
@type _connects: list
12+
@ivar _connects: Dictionary of list of subscriptions
13+
@type _connects: dict
1314
@ivar _inLoop: Running an event loop or not
1415
@type _inLoop: bool
1516
@ivar _driverLoop: Using a driver event loop or not
@@ -18,7 +19,7 @@ class Engine(object):
1819
@type _debug: bool
1920
"""
2021

21-
def __init__(self, driverName=None, debug=False):
22+
def __init__(self, driverName: str = None, debug: bool = False):
2223
"""
2324
Constructs a new TTS engine instance.
2425
@@ -35,6 +36,7 @@ def __init__(self, driverName=None, debug=False):
3536
self._driverLoop = True
3637
self._debug = debug
3738

39+
# noinspection PyBroadException
3840
def _notify(self, topic, **kwargs):
3941
"""
4042
Invokes callbacks for an event topic.
@@ -143,8 +145,8 @@ def getProperty(self, name):
143145
144146
@param name: Name of the property to fetch
145147
@type name: str
146-
@return: Value associated with the property
147-
@rtype: object
148+
@return: List of values associated with the property
149+
@rtype: list
148150
@raise KeyError: When the property name is unknown
149151
"""
150152
return self.proxy.getProperty(name)
@@ -163,7 +165,7 @@ def setProperty(self, name, value):
163165
164166
@param name: Name of the property to fetch
165167
@type name: str
166-
@param: Value to set for the property
168+
@param value: Value to set for the property
167169
@rtype: object
168170
@raise KeyError: When the property name is unknown
169171
"""

0 commit comments

Comments
 (0)