first commit
This commit is contained in:
32
venv/lib/python3.11/site-packages/pyttsx3/__init__.py
Normal file
32
venv/lib/python3.11/site-packages/pyttsx3/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from .engine import Engine
|
||||
import weakref
|
||||
|
||||
_activeEngines = weakref.WeakValueDictionary()
|
||||
|
||||
def init(driverName=None, debug=False):
|
||||
'''
|
||||
Constructs a new TTS engine instance or reuses the existing instance for
|
||||
the driver name.
|
||||
|
||||
@param driverName: Name of the platform specific driver to use. If
|
||||
None, selects the default driver for the operating system.
|
||||
@type: str
|
||||
@param debug: Debugging output enabled or not
|
||||
@type debug: bool
|
||||
@return: Engine instance
|
||||
@rtype: L{engine.Engine}
|
||||
'''
|
||||
try:
|
||||
eng = _activeEngines[driverName]
|
||||
except KeyError:
|
||||
eng = Engine(driverName, debug)
|
||||
_activeEngines[driverName] = eng
|
||||
return eng
|
||||
|
||||
|
||||
def speak(text):
|
||||
engine = init()
|
||||
engine.say(text)
|
||||
engine.runAndWait()
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
226
venv/lib/python3.11/site-packages/pyttsx3/driver.py
Normal file
226
venv/lib/python3.11/site-packages/pyttsx3/driver.py
Normal file
@@ -0,0 +1,226 @@
|
||||
import sys
|
||||
import traceback
|
||||
import weakref
|
||||
import importlib
|
||||
|
||||
|
||||
class DriverProxy(object):
|
||||
'''
|
||||
Proxy to a driver implementation.
|
||||
|
||||
@ivar _module: Module containing the driver implementation
|
||||
@type _module: module
|
||||
@ivar _engine: Reference to the engine that owns the driver
|
||||
@type _engine: L{engine.Engine}
|
||||
@ivar _queue: Queue of commands outstanding for the driver
|
||||
@type _queue: list
|
||||
@ivar _busy: True when the driver is busy processing a command, False when
|
||||
not
|
||||
@type _busy: bool
|
||||
@ivar _name: Name associated with the current utterance
|
||||
@type _name: str
|
||||
@ivar _debug: Debugging output enabled or not
|
||||
@type _debug: bool
|
||||
@ivar _iterator: Driver iterator to invoke when in an external run loop
|
||||
@type _iterator: iterator
|
||||
'''
|
||||
|
||||
def __init__(self, engine, driverName, debug):
|
||||
'''
|
||||
Constructor.
|
||||
|
||||
@param engine: Reference to the engine that owns the driver
|
||||
@type engine: L{engine.Engine}
|
||||
@param driverName: Name of the driver module to use under drivers/ or
|
||||
None to select the default for the platform
|
||||
@type driverName: str
|
||||
@param debug: Debugging output enabled or not
|
||||
@type debug: bool
|
||||
'''
|
||||
if driverName is None:
|
||||
# pick default driver for common platforms
|
||||
if sys.platform == 'darwin':
|
||||
driverName = 'nsss'
|
||||
elif sys.platform == 'win32':
|
||||
driverName = 'sapi5'
|
||||
else:
|
||||
driverName = 'espeak'
|
||||
# import driver module
|
||||
name = 'pyttsx3.drivers.%s' % driverName
|
||||
self._module = importlib.import_module(name)
|
||||
# build driver instance
|
||||
self._driver = self._module.buildDriver(weakref.proxy(self))
|
||||
# initialize refs
|
||||
self._engine = engine
|
||||
self._queue = []
|
||||
self._busy = True
|
||||
self._name = None
|
||||
self._iterator = None
|
||||
self._debug = debug
|
||||
self._current_text = ''
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
self._driver.destroy()
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
|
||||
def _push(self, mtd, args, name=None):
|
||||
'''
|
||||
Adds a command to the queue.
|
||||
|
||||
@param mtd: Method to invoke to process the command
|
||||
@type mtd: method
|
||||
@param args: Arguments to apply when invoking the method
|
||||
@type args: tuple
|
||||
@param name: Name associated with the command
|
||||
@type name: str
|
||||
'''
|
||||
self._queue.append((mtd, args, name))
|
||||
self._pump()
|
||||
|
||||
def _pump(self):
|
||||
'''
|
||||
Attempts to process the next command in the queue if one exists and the
|
||||
driver is not currently busy.
|
||||
'''
|
||||
while (not self._busy) and len(self._queue):
|
||||
cmd = self._queue.pop(0)
|
||||
self._name = cmd[2]
|
||||
try:
|
||||
cmd[0](*cmd[1])
|
||||
except Exception as e:
|
||||
self.notify('error', exception=e)
|
||||
if self._debug:
|
||||
traceback.print_exc()
|
||||
|
||||
def notify(self, topic, **kwargs):
|
||||
'''
|
||||
Sends a notification to the engine from the driver.
|
||||
|
||||
@param topic: Notification topic
|
||||
@type topic: str
|
||||
@param kwargs: Arbitrary keyword arguments
|
||||
@type kwargs: dict
|
||||
'''
|
||||
if 'name' not in kwargs or kwargs['name'] is None: # Avoid overwriting
|
||||
kwargs['name'] = self._name
|
||||
self._engine._notify(topic, **kwargs)
|
||||
|
||||
def setBusy(self, busy):
|
||||
'''
|
||||
Called by the driver to indicate it is busy.
|
||||
|
||||
@param busy: True when busy, false when idle
|
||||
@type busy: bool
|
||||
'''
|
||||
self._busy = busy
|
||||
if not self._busy:
|
||||
self._pump()
|
||||
|
||||
def isBusy(self):
|
||||
'''
|
||||
@return: True if the driver is busy, false if not
|
||||
@rtype: bool
|
||||
'''
|
||||
return self._busy
|
||||
|
||||
def say(self, text, name):
|
||||
'''
|
||||
Called by the engine to push a say command onto the queue.
|
||||
|
||||
@param text: Text to speak
|
||||
@type text: unicode
|
||||
@param name: Name to associate with the utterance
|
||||
@type name: str
|
||||
'''
|
||||
self._current_text = text
|
||||
self._push(self._driver.say, (text,), name)
|
||||
|
||||
def stop(self):
|
||||
'''
|
||||
Called by the engine to stop the current utterance and clear the queue
|
||||
of commands.
|
||||
'''
|
||||
# clear queue up to first end loop command
|
||||
while(True):
|
||||
try:
|
||||
mtd, args, name = self._queue[0]
|
||||
except IndexError:
|
||||
break
|
||||
if(mtd == self._engine.endLoop):
|
||||
break
|
||||
self._queue.pop(0)
|
||||
self._driver.stop()
|
||||
|
||||
def save_to_file(self, text, filename, name):
|
||||
'''
|
||||
Called by the engine to push a say command onto the queue.
|
||||
|
||||
@param text: Text to speak
|
||||
@type text: unicode
|
||||
@param name: Name to associate with the utterance
|
||||
@type name: str
|
||||
'''
|
||||
self._push(self._driver.save_to_file, (text, filename), name)
|
||||
|
||||
def getProperty(self, name):
|
||||
'''
|
||||
Called by the engine to get a driver property value.
|
||||
|
||||
@param name: Name of the property
|
||||
@type name: str
|
||||
@return: Property value
|
||||
@rtype: object
|
||||
'''
|
||||
return self._driver.getProperty(name)
|
||||
|
||||
def setProperty(self, name, value):
|
||||
'''
|
||||
Called by the engine to set a driver property value.
|
||||
|
||||
@param name: Name of the property
|
||||
@type name: str
|
||||
@param value: Property value
|
||||
@type value: object
|
||||
'''
|
||||
self._push(self._driver.setProperty, (name, value))
|
||||
|
||||
def runAndWait(self):
|
||||
'''
|
||||
Called by the engine to start an event loop, process all commands in
|
||||
the queue at the start of the loop, and then exit the loop.
|
||||
'''
|
||||
self._push(self._engine.endLoop, tuple())
|
||||
self._driver.startLoop()
|
||||
|
||||
def startLoop(self, useDriverLoop):
|
||||
'''
|
||||
Called by the engine to start an event loop.
|
||||
'''
|
||||
if useDriverLoop:
|
||||
self._driver.startLoop()
|
||||
else:
|
||||
self._iterator = self._driver.iterate()
|
||||
|
||||
def endLoop(self, useDriverLoop):
|
||||
'''
|
||||
Called by the engine to stop an event loop.
|
||||
'''
|
||||
self._queue = []
|
||||
self._driver.stop()
|
||||
if useDriverLoop:
|
||||
self._driver.endLoop()
|
||||
else:
|
||||
self._iterator = None
|
||||
self.setBusy(True)
|
||||
|
||||
def iterate(self):
|
||||
'''
|
||||
Called by the engine to iterate driver commands and notifications from
|
||||
within an external event loop.
|
||||
'''
|
||||
try:
|
||||
next(self._iterator)
|
||||
except StopIteration:
|
||||
pass
|
@@ -0,0 +1,23 @@
|
||||
|
||||
'''
|
||||
Utility functions to help with Python 2/3 compatibility
|
||||
'''
|
||||
from .. import six
|
||||
|
||||
def toUtf8(value):
|
||||
'''
|
||||
Takes in a value and converts it to a text (unicode) type. Then decodes that
|
||||
type to a byte array encoded in utf-8. In 2.X the resulting object will be a
|
||||
str and in 3.X the resulting object will be bytes. In both 2.X and 3.X any
|
||||
object can be passed in and the object's __str__ will be used (or __repr__ if
|
||||
__str__ is not defined) if the object is not already a text type.
|
||||
'''
|
||||
return six.text_type(value).encode('utf-8')
|
||||
|
||||
def fromUtf8(value):
|
||||
'''
|
||||
Takes in a byte array encoded as utf-8 and returns a text (unicode) type. In
|
||||
2.X we expect a str type and return a unicde type. In 3.X we expect a bytes
|
||||
type and return a str type.
|
||||
'''
|
||||
return value.decode('utf-8')
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
500
venv/lib/python3.11/site-packages/pyttsx3/drivers/_espeak.py
Normal file
500
venv/lib/python3.11/site-packages/pyttsx3/drivers/_espeak.py
Normal file
@@ -0,0 +1,500 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
from ctypes import (CFUNCTYPE, POINTER, Structure, Union, c_char_p, c_int,
|
||||
c_long, c_short, c_ubyte, c_uint, c_ulong, c_void_p,
|
||||
c_wchar, cdll)
|
||||
|
||||
|
||||
def cfunc(name, dll, result, *args):
|
||||
"""build and apply a ctypes prototype complete with parameter flags"""
|
||||
atypes = []
|
||||
aflags = []
|
||||
for arg in args:
|
||||
atypes.append(arg[1])
|
||||
aflags.append((arg[2], arg[0]) + arg[3:])
|
||||
return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags))
|
||||
|
||||
|
||||
dll = None
|
||||
|
||||
def load_library():
|
||||
global dll
|
||||
paths = [
|
||||
# macOS paths
|
||||
'/usr/local/lib/libespeak-ng.1.dylib',
|
||||
'/usr/local/lib/libespeak.dylib',
|
||||
|
||||
# Linux paths
|
||||
'libespeak-ng.so.1',
|
||||
'/usr/local/lib/libespeak-ng.so.1',
|
||||
'libespeak.so.1',
|
||||
|
||||
# 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:
|
||||
continue # Try the next path
|
||||
return False
|
||||
|
||||
try:
|
||||
if not load_library():
|
||||
raise RuntimeError("This means you probably do not have eSpeak or eSpeak-ng installed!")
|
||||
except Exception as exp:
|
||||
raise
|
||||
|
||||
# constants and such from speak_lib.h
|
||||
|
||||
EVENT_LIST_TERMINATED = 0
|
||||
EVENT_WORD = 1
|
||||
EVENT_SENTENCE = 2
|
||||
EVENT_MARK = 3
|
||||
EVENT_PLAY = 4
|
||||
EVENT_END = 5
|
||||
EVENT_MSG_TERMINATED = 6
|
||||
|
||||
|
||||
class numberORname(Union):
|
||||
_fields_ = [
|
||||
('number', c_int),
|
||||
('name', c_char_p)
|
||||
]
|
||||
|
||||
|
||||
class EVENT(Structure):
|
||||
_fields_ = [
|
||||
('type', c_int),
|
||||
('unique_identifier', c_uint),
|
||||
('text_position', c_int),
|
||||
('length', c_int),
|
||||
('audio_position', c_int),
|
||||
('sample', c_int),
|
||||
('user_data', c_void_p),
|
||||
('id', numberORname)
|
||||
]
|
||||
|
||||
|
||||
AUDIO_OUTPUT_PLAYBACK = 0
|
||||
AUDIO_OUTPUT_RETRIEVAL = 1
|
||||
AUDIO_OUTPUT_SYNCHRONOUS = 2
|
||||
AUDIO_OUTPUT_SYNCH_PLAYBACK = 3
|
||||
|
||||
EE_OK = 0
|
||||
EE_INTERNAL_ERROR = -1
|
||||
EE_BUFFER_FULL = 1
|
||||
EE_NOT_FOUND = 2
|
||||
|
||||
Initialize = cfunc('espeak_Initialize', dll, c_int,
|
||||
('output', c_int, 1, AUDIO_OUTPUT_PLAYBACK),
|
||||
('bufflength', c_int, 1, 100),
|
||||
('path', c_char_p, 1, None),
|
||||
('option', c_int, 1, 0))
|
||||
Initialize.__doc__ = """Must be called before any synthesis functions are called.
|
||||
output: the audio data can either be played by eSpeak or passed back by the SynthCallback function.
|
||||
buflength: The length in mS of sound buffers passed to the SynthCallback function.
|
||||
path: The directory which contains the espeak-data directory, or NULL for the default location.
|
||||
options: bit 0: 1=allow espeakEVENT_PHONEME events.
|
||||
|
||||
Returns: sample rate in Hz, or -1 (EE_INTERNAL_ERROR)."""
|
||||
|
||||
t_espeak_callback = CFUNCTYPE(c_int, POINTER(c_short), c_int, POINTER(EVENT))
|
||||
|
||||
cSetSynthCallback = cfunc('espeak_SetSynthCallback', dll, None,
|
||||
('SynthCallback', t_espeak_callback, 1))
|
||||
SynthCallback = None
|
||||
|
||||
|
||||
def SetSynthCallback(cb):
|
||||
global SynthCallback
|
||||
SynthCallback = t_espeak_callback(cb)
|
||||
cSetSynthCallback(SynthCallback)
|
||||
|
||||
|
||||
SetSynthCallback.__doc__ = """Must be called before any synthesis functions are called.
|
||||
This specifies a function in the calling program which is called when a buffer of
|
||||
speech sound data has been produced.
|
||||
|
||||
|
||||
The callback function is of the form:
|
||||
|
||||
int SynthCallback(short *wav, int numsamples, espeak_EVENT *events);
|
||||
|
||||
wav: is the speech sound data which has been produced.
|
||||
NULL indicates that the synthesis has been completed.
|
||||
|
||||
numsamples: is the number of entries in wav. This number may vary, may be less than
|
||||
the value implied by the buflength parameter given in espeak_Initialize, and may
|
||||
sometimes be zero (which does NOT indicate end of synthesis).
|
||||
|
||||
events: an array of espeak_EVENT items which indicate word and sentence events, and
|
||||
also the occurance if <mark> and <audio> elements within the text.
|
||||
|
||||
|
||||
Callback returns: 0=continue synthesis, 1=abort synthesis."""
|
||||
|
||||
t_UriCallback = CFUNCTYPE(c_int, c_int, c_char_p, c_char_p)
|
||||
|
||||
cSetUriCallback = cfunc('espeak_SetUriCallback', dll, None,
|
||||
('UriCallback', t_UriCallback, 1))
|
||||
UriCallback = None
|
||||
|
||||
|
||||
def SetUriCallback(cb):
|
||||
global UriCallback
|
||||
UriCallback = t_UriCallback(UriCallback)
|
||||
cSetUriCallback(UriCallback)
|
||||
|
||||
|
||||
SetUriCallback.__doc__ = """This function must be called before synthesis functions are used, in order to deal with
|
||||
<audio> tags. It specifies a callback function which is called when an <audio> element is
|
||||
encountered and allows the calling program to indicate whether the sound file which
|
||||
is specified in the <audio> element is available and is to be played.
|
||||
|
||||
The callback function is of the form:
|
||||
|
||||
int UriCallback(int type, const char *uri, const char *base);
|
||||
|
||||
type: type of callback event. Currently only 1= <audio> element
|
||||
|
||||
uri: the "src" attribute from the <audio> element
|
||||
|
||||
base: the "xml:base" attribute (if any) from the <speak> element
|
||||
|
||||
Return: 1=don't play the sound, but speak the text alternative.
|
||||
0=place a PLAY event in the event list at the point where the <audio> element
|
||||
occurs. The calling program can then play the sound at that point."""
|
||||
|
||||
# a few manifest constants
|
||||
CHARS_AUTO = 0
|
||||
CHARS_UTF8 = 1
|
||||
CHARS_8BIT = 2
|
||||
CHARS_WCHAR = 3
|
||||
|
||||
SSML = 0x10
|
||||
PHONEMES = 0x100
|
||||
ENDPAUSE = 0x1000
|
||||
KEEP_NAMEDATA = 0x2000
|
||||
|
||||
POS_CHARACTER = 1
|
||||
POS_WORD = 2
|
||||
POS_SENTENCE = 3
|
||||
|
||||
|
||||
def Synth(text, position=0, position_type=POS_CHARACTER, end_position=0, flags=0, user_data=None):
|
||||
return cSynth(text, len(text) * 10, position, position_type, end_position, flags, None, user_data)
|
||||
|
||||
|
||||
cSynth = cfunc('espeak_Synth', dll, c_int,
|
||||
('text', c_char_p, 1),
|
||||
('size', c_long, 1),
|
||||
('position', c_uint, 1, 0),
|
||||
('position_type', c_int, 1, POS_CHARACTER),
|
||||
('end_position', c_uint, 1, 0),
|
||||
('flags', c_uint, 1, CHARS_AUTO),
|
||||
('unique_identifier', POINTER(c_uint), 1, None),
|
||||
('user_data', c_void_p, 1, None))
|
||||
Synth.__doc__ = """Synthesize speech for the specified text. The speech sound data is passed to the calling
|
||||
program in buffers by means of the callback function specified by espeak_SetSynthCallback(). The command is asynchronous: it is internally buffered and returns as soon as possible. If espeak_Initialize was previously called with AUDIO_OUTPUT_PLAYBACK as argument, the sound data are played by eSpeak.
|
||||
|
||||
text: The text to be spoken, terminated by a zero character. It may be either 8-bit characters,
|
||||
wide characters (wchar_t), or UTF8 encoding. Which of these is determined by the "flags"
|
||||
parameter.
|
||||
|
||||
size: Equal to (or greater than) the size of the text data, in bytes. This is used in order
|
||||
to allocate internal storage space for the text. This value is not used for
|
||||
AUDIO_OUTPUT_SYNCHRONOUS mode.
|
||||
|
||||
position: The position in the text where speaking starts. Zero indicates speak from the
|
||||
start of the text.
|
||||
|
||||
position_type: Determines whether "position" is a number of characters, words, or sentences.
|
||||
Values:
|
||||
|
||||
end_position: If set, this gives a character position at which speaking will stop. A value
|
||||
of zero indicates no end position.
|
||||
|
||||
flags: These may be OR'd together:
|
||||
Type of character codes, one of:
|
||||
espeak.CHARS_UTF8 UTF8 encoding
|
||||
espeak.CHARS_8BIT The 8 bit ISO-8859 character set for the particular language.
|
||||
espeak.CHARS_AUTO 8 bit or UTF8 (this is the default)
|
||||
espeak.CHARS_WCHAR Wide characters (wchar_t)
|
||||
|
||||
espeak.SSML Elements within < > are treated as SSML elements, or if not recognised are ignored.
|
||||
|
||||
espeak.PHONEMES Text within [[ ]] is treated as phonemes codes (in espeak's Hirschenbaum encoding).
|
||||
|
||||
espeak.ENDPAUSE If set then a sentence pause is added at the end of the text. If not set then
|
||||
this pause is suppressed.
|
||||
|
||||
unique_identifier: message identifier; helpful for identifying later
|
||||
data supplied to the callback.
|
||||
|
||||
user_data: pointer which will be passed to the callback function.
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_BUFFER_FULL: the command can not be buffered;
|
||||
you may try after a while to call the function again.
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
|
||||
def Synth_Mark(text, index_mark, end_position=0, flags=CHARS_AUTO):
|
||||
cSynth_Mark(text, len(text) + 1, index_mark, end_position, flags)
|
||||
|
||||
|
||||
cSynth_Mark = cfunc('espeak_Synth_Mark', dll, c_int,
|
||||
('text', c_char_p, 1),
|
||||
('size', c_ulong, 1),
|
||||
('index_mark', c_char_p, 1),
|
||||
('end_position', c_uint, 1, 0),
|
||||
('flags', c_uint, 1, CHARS_AUTO),
|
||||
('unique_identifier', POINTER(c_uint), 1, None),
|
||||
('user_data', c_void_p, 1, None))
|
||||
Synth_Mark.__doc__ = """Synthesize speech for the specified text. Similar to espeak_Synth() but the start position is
|
||||
specified by the name of a <mark> element in the text.
|
||||
|
||||
index_mark: The "name" attribute of a <mark> element within the text which specified the
|
||||
point at which synthesis starts. UTF8 string.
|
||||
|
||||
For the other parameters, see espeak_Synth()
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_BUFFER_FULL: the command can not be buffered;
|
||||
you may try after a while to call the function again.
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
Key = cfunc('espeak_Key', dll, c_int,
|
||||
('key_name', c_char_p, 1))
|
||||
Key.__doc__ = """Speak the name of a keyboard key.
|
||||
Currently this just speaks the "key_name" as given
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_BUFFER_FULL: the command can not be buffered;
|
||||
you may try after a while to call the function again.
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
Char = cfunc('espeak_Char', dll, c_int,
|
||||
('character', c_wchar, 1))
|
||||
Char.__doc__ = """Speak the name of the given character
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_BUFFER_FULL: the command can not be buffered;
|
||||
you may try after a while to call the function again.
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
# Speech Parameters
|
||||
SILENCE = 0 # internal use
|
||||
RATE = 1
|
||||
VOLUME = 2
|
||||
PITCH = 3
|
||||
RANGE = 4
|
||||
PUNCTUATION = 5
|
||||
CAPITALS = 6
|
||||
EMPHASIS = 7 # internal use
|
||||
LINELENGTH = 8 # internal use
|
||||
|
||||
PUNCT_NONE = 0
|
||||
PUNCT_ALL = 1
|
||||
PUNCT_SOME = 2
|
||||
|
||||
SetParameter = cfunc('espeak_SetParameter', dll, c_int,
|
||||
('parameter', c_int, 1),
|
||||
('value', c_int, 1),
|
||||
('relative', c_int, 1, 0))
|
||||
SetParameter.__doc__ = """Sets the value of the specified parameter.
|
||||
relative=0 Sets the absolute value of the parameter.
|
||||
relative=1 Sets a relative value of the parameter.
|
||||
|
||||
parameter:
|
||||
espeak.RATE: speaking speed in word per minute.
|
||||
|
||||
espeak.VOLUME: volume in range 0-100 0=silence
|
||||
|
||||
espeak.PITCH: base pitch, range 0-100. 50=normal
|
||||
|
||||
espeak.RANGE: pitch range, range 0-100. 0-monotone, 50=normal
|
||||
|
||||
espeak.PUNCTUATION: which punctuation characters to announce:
|
||||
value in espeak_PUNCT_TYPE (none, all, some),
|
||||
see espeak_GetParameter() to specify which characters are announced.
|
||||
|
||||
espeak.CAPITALS: announce capital letters by:
|
||||
0=none,
|
||||
1=sound icon,
|
||||
2=spelling,
|
||||
3 or higher, by raising pitch. This values gives the amount in Hz by which the pitch
|
||||
of a word raised to indicate it has a capital letter.
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_BUFFER_FULL: the command can not be buffered;
|
||||
you may try after a while to call the function again.
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
GetParameter = cfunc('espeak_GetParameter', dll, c_int,
|
||||
('parameter', c_int, 1))
|
||||
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.__doc__ = """Specified a list of punctuation characters whose names are
|
||||
to be spoken when the value of the Punctuation parameter is set to "some".
|
||||
|
||||
punctlist: A list of character codes, terminated by a zero character.
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_BUFFER_FULL: the command can not be buffered;
|
||||
you may try after a while to call the function again.
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
SetPhonemeTrace = cfunc('espeak_SetPhonemeTrace', dll, None,
|
||||
('value', c_int, 1),
|
||||
('stream', c_void_p, 1))
|
||||
SetPhonemeTrace.__doc__ = """Controls the output of phoneme symbols for the text
|
||||
value=0 No phoneme output (default)
|
||||
value=1 Output the translated phoneme symbols for the text
|
||||
value=2 as (1), but also output a trace of how the translation was done (matching rules and list entries)
|
||||
|
||||
stream output stream for the phoneme symbols (and trace). If stream=NULL then it uses stdout."""
|
||||
|
||||
CompileDictionary = cfunc('espeak_CompileDictionary', dll, None,
|
||||
('path', c_char_p, 1),
|
||||
('log', c_void_p, 1))
|
||||
CompileDictionary.__doc__ = """Compile pronunciation dictionary for a language which corresponds to the currently
|
||||
selected voice. The required voice should be selected before calling this function.
|
||||
|
||||
path: The directory which contains the language's '_rules' and '_list' files.
|
||||
'path' should end with a path separator character ('/').
|
||||
log: Stream for error reports and statistics information. If log=NULL then stderr will be used."""
|
||||
|
||||
|
||||
class VOICE(Structure):
|
||||
_fields_ = [
|
||||
('name', c_char_p),
|
||||
('languages', c_char_p),
|
||||
('identifier', c_char_p),
|
||||
('gender', c_ubyte),
|
||||
('age', c_ubyte),
|
||||
('variant', c_ubyte),
|
||||
('xx1', c_ubyte),
|
||||
('score', c_int),
|
||||
('spare', c_void_p),
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
"""Print the fields"""
|
||||
res = []
|
||||
for field in self._fields_:
|
||||
res.append('%s=%s' % (field[0], repr(getattr(self, field[0]))))
|
||||
return self.__class__.__name__ + '(' + ','.join(res) + ')'
|
||||
|
||||
|
||||
cListVoices = cfunc('espeak_ListVoices', dll, POINTER(POINTER(VOICE)),
|
||||
('voice_spec', POINTER(VOICE), 1))
|
||||
cListVoices.__doc__ = """Reads the voice files from espeak-data/voices and creates an array of espeak_VOICE pointers.
|
||||
The list is terminated by a NULL pointer
|
||||
|
||||
If voice_spec is NULL then all voices are listed.
|
||||
If voice spec is given, then only the voices which are compatible with the voice_spec
|
||||
are listed, and they are listed in preference order."""
|
||||
|
||||
|
||||
def ListVoices(voice_spec=None):
|
||||
"""Reads the voice files from espeak-data/voices and returns a list of VOICE objects.
|
||||
|
||||
If voice_spec is None then all voices are listed.
|
||||
If voice spec is given, then only the voices which are compatible with the voice_spec
|
||||
are listed, and they are listed in preference order."""
|
||||
ppv = cListVoices(voice_spec)
|
||||
res = []
|
||||
i = 0
|
||||
while ppv[i]:
|
||||
res.append(ppv[i][0])
|
||||
i += 1
|
||||
return res
|
||||
|
||||
|
||||
SetVoiceByName = cfunc('espeak_SetVoiceByName', dll, c_int,
|
||||
('name', c_char_p, 1))
|
||||
SetVoiceByName.__doc__ = """Searches for a voice with a matching "name" field. Language is not considered.
|
||||
"name" is a UTF8 string.
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_BUFFER_FULL: the command can not be buffered;
|
||||
you may try after a while to call the function again.
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
SetVoiceByProperties = cfunc('espeak_SetVoiceByProperties', dll, c_int,
|
||||
('voice_spec', POINTER(VOICE), 1))
|
||||
SetVoiceByProperties.__doc__ = """An espeak_VOICE structure is used to pass criteria to select a voice. Any of the following
|
||||
fields may be set:
|
||||
|
||||
name NULL, or a voice name
|
||||
|
||||
languages NULL, or a single language string (with optional dialect), eg. "en-uk", or "en"
|
||||
|
||||
gender 0=not specified, 1=male, 2=female
|
||||
|
||||
age 0=not specified, or an age in years
|
||||
|
||||
variant After a list of candidates is produced, scored and sorted, "variant" is used to index
|
||||
that list and choose a voice.
|
||||
variant=0 takes the top voice (i.e. best match). variant=1 takes the next voice, etc"""
|
||||
|
||||
GetCurrentVoice = cfunc('espeak_GetCurrentVoice', dll, POINTER(VOICE),
|
||||
)
|
||||
GetCurrentVoice.__doc__ = """Returns the espeak_VOICE data for the currently selected voice.
|
||||
This is not affected by temporary voice changes caused by SSML elements such as <voice> and <s>"""
|
||||
|
||||
Cancel = cfunc('espeak_Cancel', dll, c_int)
|
||||
Cancel.__doc__ = """Stop immediately synthesis and audio output of the current text. When this
|
||||
function returns, the audio output is fully stopped and the synthesizer is ready to
|
||||
synthesize a new message.
|
||||
|
||||
Return: EE_OK: operation achieved
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
IsPlaying = cfunc('espeak_IsPlaying', dll, c_int)
|
||||
IsPlaying.__doc__ = """Returns 1 if audio is played, 0 otherwise."""
|
||||
|
||||
Synchronize = cfunc('espeak_Synchronize', dll, c_int)
|
||||
Synchronize.__doc__ = """This function returns when all data have been spoken.
|
||||
Return: EE_OK: operation achieved
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
Terminate = cfunc('espeak_Terminate', dll, c_int)
|
||||
Terminate.__doc__ = """last function to be called.
|
||||
Return: EE_OK: operation achieved
|
||||
EE_INTERNAL_ERROR."""
|
||||
|
||||
Info = cfunc('espeak_Info', dll, c_char_p, ('ptr', c_void_p, 1, 0))
|
||||
Info.__doc__ = """Returns the version number string.
|
||||
The parameter is for future use, and should be set to NULL"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
def synth_cb(wav, numsample, events):
|
||||
print(numsample, end="")
|
||||
i = 0
|
||||
while True:
|
||||
if events[i].type == EVENT_LIST_TERMINATED:
|
||||
break
|
||||
print(events[i].type, end="")
|
||||
i += 1
|
||||
return 0
|
||||
|
||||
|
||||
samplerate = Initialize(output=AUDIO_OUTPUT_PLAYBACK)
|
||||
SetSynthCallback(synth_cb)
|
||||
s = 'This is a test, only a test. '
|
||||
uid = c_uint(0)
|
||||
# print 'pitch=',GetParameter(PITCH)
|
||||
# SetParameter(PITCH, 50, 0)
|
||||
print(Synth(s))
|
||||
while IsPlaying():
|
||||
time.sleep(0.1)
|
175
venv/lib/python3.11/site-packages/pyttsx3/drivers/dummy.py
Normal file
175
venv/lib/python3.11/site-packages/pyttsx3/drivers/dummy.py
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
from ..voice import Voice
|
||||
import time
|
||||
|
||||
def buildDriver(proxy):
|
||||
'''
|
||||
Builds a new instance of a driver and returns it for use by the driver
|
||||
proxy.
|
||||
|
||||
@param proxy: Proxy creating the driver
|
||||
@type proxy: L{driver.DriverProxy}
|
||||
'''
|
||||
return DummyDriver(proxy)
|
||||
|
||||
class DummyDriver(object):
|
||||
'''
|
||||
Dummy speech engine implementation. Documents the interface, notifications,
|
||||
properties, and sequencing responsibilities of a driver implementation.
|
||||
|
||||
@ivar _proxy: Driver proxy that manages this instance
|
||||
@type _proxy: L{driver.DriverProxy}
|
||||
@ivar _config: Dummy configuration
|
||||
@type _config: dict
|
||||
@ivar _looping: True when in the dummy event loop, False when not
|
||||
@ivar _looping: bool
|
||||
'''
|
||||
def __init__(self, proxy):
|
||||
'''
|
||||
Constructs the driver.
|
||||
|
||||
@param proxy: Proxy creating the driver
|
||||
@type proxy: L{driver.DriverProxy}
|
||||
'''
|
||||
self._proxy = proxy
|
||||
self._looping = False
|
||||
# hold config values as if we had a real tts implementation that
|
||||
# supported them
|
||||
voices = [
|
||||
Voice('dummy.voice1', 'John Doe', ['en-US', 'en-GB'], 'male', 'adult'),
|
||||
Voice('dummy.voice2', 'Jane Doe', ['en-US', 'en-GB'], 'female', 'adult'),
|
||||
Voice('dummy.voice3', 'Jimmy Doe', ['en-US', 'en-GB'], 'male', 10)
|
||||
]
|
||||
self._config = {
|
||||
'rate' : 200,
|
||||
'volume' : 1.0,
|
||||
'voice' : voices[0],
|
||||
'voices' : voices
|
||||
}
|
||||
|
||||
def destroy(self):
|
||||
'''
|
||||
Optional method that will be called when the driver proxy is being
|
||||
destroyed. Can cleanup any resources to make sure the engine terminates
|
||||
properly.
|
||||
'''
|
||||
pass
|
||||
|
||||
def startLoop(self):
|
||||
'''
|
||||
Starts a blocking run loop in which driver callbacks are properly
|
||||
invoked.
|
||||
|
||||
@precondition: There was no previous successful call to L{startLoop}
|
||||
without an intervening call to L{stopLoop}.
|
||||
'''
|
||||
first = True
|
||||
self._looping = True
|
||||
while self._looping:
|
||||
if first:
|
||||
self._proxy.setBusy(False)
|
||||
first = False
|
||||
time.sleep(0.5)
|
||||
|
||||
def endLoop(self):
|
||||
'''
|
||||
Stops a previously started run loop.
|
||||
|
||||
@precondition: A previous call to L{startLoop} suceeded and there was
|
||||
no intervening call to L{endLoop}.
|
||||
'''
|
||||
self._looping = False
|
||||
|
||||
def iterate(self):
|
||||
'''
|
||||
Iterates from within an external run loop.
|
||||
'''
|
||||
self._proxy.setBusy(False)
|
||||
yield
|
||||
|
||||
def say(self, text):
|
||||
'''
|
||||
Speaks the given text. Generates the following notifications during
|
||||
output:
|
||||
|
||||
started-utterance: When speech output has started
|
||||
started-word: When a word is about to be spoken. Includes the character
|
||||
"location" of the start of the word in the original utterance text
|
||||
and the "length" of the word in characters.
|
||||
finished-utterance: When speech output has finished. Includes a flag
|
||||
indicating if the entire utterance was "completed" or not.
|
||||
|
||||
The proxy automatically adds any "name" associated with the utterance
|
||||
to the notifications on behalf of the driver.
|
||||
|
||||
When starting to output an utterance, the driver must inform its proxy
|
||||
that it is busy by invoking L{driver.DriverProxy.setBusy} with a flag
|
||||
of True. When the utterance completes or is interrupted, the driver
|
||||
inform the proxy that it is no longer busy by invoking
|
||||
L{driver.DriverProxy.setBusy} with a flag of False.
|
||||
|
||||
@param text: Unicode text to speak
|
||||
@type text: unicode
|
||||
'''
|
||||
self._proxy.setBusy(True)
|
||||
self._proxy.notify('started-utterance')
|
||||
i = 0
|
||||
for word in text.split(' '):
|
||||
self._proxy.notify('started-word', location=i, length=len(word))
|
||||
try:
|
||||
i = text.index(' ', i+1)+1
|
||||
except Exception:
|
||||
pass
|
||||
self._proxy.notify('finished-utterance', completed=True)
|
||||
self._proxy.setBusy(False)
|
||||
|
||||
def stop(self):
|
||||
'''
|
||||
Stops any current output. If an utterance was being spoken, the driver
|
||||
is still responsible for sending the closing finished-utterance
|
||||
notification documented above and resetting the busy state of the
|
||||
proxy.
|
||||
'''
|
||||
pass
|
||||
|
||||
def getProperty(self, name):
|
||||
'''
|
||||
Gets a property value of the speech engine. The suppoted properties
|
||||
and their values are:
|
||||
|
||||
voices: List of L{voice.Voice} objects supported by the driver
|
||||
voice: String ID of the current voice
|
||||
rate: Integer speech rate in words per minute
|
||||
volume: Floating point volume of speech in the range [0.0, 1.0]
|
||||
|
||||
@param name: Property name
|
||||
@type name: str
|
||||
@raise KeyError: When the property name is unknown
|
||||
'''
|
||||
try:
|
||||
return self._config[name]
|
||||
except KeyError:
|
||||
raise KeyError('unknown property %s' % name)
|
||||
|
||||
def setProperty(self, name, value):
|
||||
'''
|
||||
Sets one of the supported property values of the speech engine listed
|
||||
above. If a value is invalid, attempts to clip it / coerce so it is
|
||||
valid before giving up and firing an exception.
|
||||
|
||||
@param name: Property name
|
||||
@type name: str
|
||||
@param value: Property value
|
||||
@type value: object
|
||||
@raise KeyError: When the property name is unknown
|
||||
@raise ValueError: When the value cannot be coerced to fit the property
|
||||
'''
|
||||
if name == 'voice':
|
||||
v = filter(lambda v: v.id == value, self._config['voices'])
|
||||
self._config['voice'] = v[0]
|
||||
elif name == 'rate':
|
||||
self._config['rate'] = value
|
||||
elif name == 'volume':
|
||||
self._config['volume'] = value
|
||||
else:
|
||||
raise KeyError('unknown property %s' % name)
|
233
venv/lib/python3.11/site-packages/pyttsx3/drivers/espeak.py
Normal file
233
venv/lib/python3.11/site-packages/pyttsx3/drivers/espeak.py
Normal file
@@ -0,0 +1,233 @@
|
||||
import os
|
||||
import wave
|
||||
import platform
|
||||
import ctypes
|
||||
import time
|
||||
import subprocess
|
||||
from tempfile import NamedTemporaryFile
|
||||
if platform.system() == 'Windows':
|
||||
import winsound
|
||||
|
||||
from ..voice import Voice
|
||||
from . import _espeak, fromUtf8, toUtf8
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
def buildDriver(proxy):
|
||||
return EspeakDriver(proxy)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class EspeakDriver(object):
|
||||
_moduleInitialized = False
|
||||
_defaultVoice = ''
|
||||
|
||||
def __init__(self, proxy):
|
||||
if not EspeakDriver._moduleInitialized:
|
||||
# espeak cannot initialize more than once per process and has
|
||||
# issues when terminating from python (assert error on close)
|
||||
# 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')
|
||||
EspeakDriver._defaultVoice = 'default'
|
||||
EspeakDriver._moduleInitialized = True
|
||||
self._proxy = proxy
|
||||
self._looping = False
|
||||
self._stopping = False
|
||||
self._speaking = False
|
||||
self._text_to_say = None
|
||||
self._data_buffer = b''
|
||||
self._numerise_buffer = []
|
||||
|
||||
_espeak.SetSynthCallback(self._onSynth)
|
||||
self.setProperty('voice', EspeakDriver._defaultVoice)
|
||||
self.setProperty('rate', 200)
|
||||
self.setProperty('volume', 1.0)
|
||||
|
||||
def numerise(self, data):
|
||||
self._numerise_buffer.append(data)
|
||||
return ctypes.c_void_p(len(self._numerise_buffer))
|
||||
|
||||
def decode_numeric(self, data):
|
||||
return self._numerise_buffer[int(data) - 1]
|
||||
|
||||
@staticmethod
|
||||
def destroy():
|
||||
_espeak.SetSynthCallback(None)
|
||||
|
||||
def stop(self):
|
||||
if _espeak.IsPlaying():
|
||||
self._stopping = True
|
||||
_espeak.Cancel()
|
||||
|
||||
@staticmethod
|
||||
def getProperty(name: str):
|
||||
if name == 'voices':
|
||||
voices = []
|
||||
for v in _espeak.ListVoices(None):
|
||||
kwargs = {'id': fromUtf8(v.name), 'name': fromUtf8(v.name)}
|
||||
if v.languages:
|
||||
try:
|
||||
language_code_bytes = v.languages[1:]
|
||||
language_code = language_code_bytes.decode('utf-8', errors='ignore')
|
||||
kwargs['languages'] = [language_code]
|
||||
except UnicodeDecodeError as e:
|
||||
kwargs['languages'] = ["Unknown"]
|
||||
genders = [None, 'male', 'female']
|
||||
kwargs['gender'] = genders[v.gender]
|
||||
kwargs['age'] = v.age or None
|
||||
voices.append(Voice(**kwargs))
|
||||
return voices
|
||||
elif name == 'voice':
|
||||
voice = _espeak.GetCurrentVoice()
|
||||
return fromUtf8(voice.contents.name)
|
||||
elif name == 'rate':
|
||||
return _espeak.GetParameter(_espeak.RATE)
|
||||
elif name == 'volume':
|
||||
return _espeak.GetParameter(_espeak.VOLUME) / 100.0
|
||||
elif name == 'pitch':
|
||||
return _espeak.GetParameter(_espeak.PITCH)
|
||||
else:
|
||||
raise KeyError('unknown property %s' % name)
|
||||
|
||||
@staticmethod
|
||||
def setProperty(name: str, value):
|
||||
if name == 'voice':
|
||||
if value is None:
|
||||
return
|
||||
try:
|
||||
utf8Value = toUtf8(value)
|
||||
_espeak.SetVoiceByName(utf8Value)
|
||||
except ctypes.ArgumentError as e:
|
||||
raise ValueError(str(e))
|
||||
elif name == 'rate':
|
||||
try:
|
||||
_espeak.SetParameter(_espeak.RATE, value, 0)
|
||||
except ctypes.ArgumentError as e:
|
||||
raise ValueError(str(e))
|
||||
elif name == 'volume':
|
||||
try:
|
||||
_espeak.SetParameter(
|
||||
_espeak.VOLUME, int(round(value * 100, 2)), 0)
|
||||
except TypeError as e:
|
||||
raise ValueError(str(e))
|
||||
elif name == 'pitch':
|
||||
try:
|
||||
_espeak.SetParameter(
|
||||
_espeak.PITCH, int(value), 0
|
||||
)
|
||||
except TypeError as e:
|
||||
raise ValueError(str(e))
|
||||
else:
|
||||
raise KeyError('unknown property %s' % name)
|
||||
|
||||
def save_to_file(self, text, filename):
|
||||
code = self.numerise(filename)
|
||||
_espeak.Synth(toUtf8(text), flags=_espeak.ENDPAUSE | _espeak.CHARS_UTF8, user_data=code)
|
||||
|
||||
def _start_synthesis(self, text):
|
||||
self._proxy.setBusy(True)
|
||||
self._proxy.notify('started-utterance')
|
||||
self._speaking = True
|
||||
self._data_buffer = b'' # Ensure buffer is cleared before starting
|
||||
try:
|
||||
_espeak.Synth(toUtf8(text), 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):
|
||||
i = 0
|
||||
while True:
|
||||
event = events[i]
|
||||
if event.type == _espeak.EVENT_LIST_TERMINATED:
|
||||
break
|
||||
if event.type == _espeak.EVENT_WORD:
|
||||
|
||||
if self._text_to_say:
|
||||
start_index = event.text_position-1
|
||||
end_index = start_index + event.length
|
||||
word = self._text_to_say[start_index:end_index]
|
||||
else:
|
||||
word = "Unknown"
|
||||
|
||||
self._proxy.notify('started-word', name=word, location=event.text_position, length=event.length)
|
||||
|
||||
elif event.type == _espeak.EVENT_END:
|
||||
stream = NamedTemporaryFile(delete=False, suffix='.wav')
|
||||
|
||||
try:
|
||||
with wave.open(stream, 'wb') as f:
|
||||
f.setnchannels(1)
|
||||
f.setsampwidth(2)
|
||||
f.setframerate(22050.0)
|
||||
f.writeframes(self._data_buffer)
|
||||
self._data_buffer = b''
|
||||
|
||||
if event.user_data:
|
||||
os.system(f'ffmpeg -y -i {stream.name} {self.decode_numeric(event.user_data)} -loglevel quiet')
|
||||
else:
|
||||
if platform.system() == 'Darwin': # macOS
|
||||
try:
|
||||
result = subprocess.run(['afplay', stream.name], check=True, capture_output=True, text=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"[EspeakDriver._onSynth] Mac afplay failed with error: {e}")
|
||||
elif platform.system() == 'Linux':
|
||||
os.system(f'aplay {stream.name} -q')
|
||||
elif platform.system() == 'Windows':
|
||||
winsound.PlaySound(stream.name, winsound.SND_FILENAME) # Blocking playback
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Error during playback: {e}")
|
||||
|
||||
finally:
|
||||
try:
|
||||
stream.close() # Ensure the file is closed
|
||||
os.remove(stream.name)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Error deleting temporary WAV file: {e}")
|
||||
|
||||
self._proxy.notify('finished-utterance', completed=True)
|
||||
self._proxy.setBusy(False)
|
||||
self.endLoop() # End the loop here
|
||||
break # Exit the loop after handling the termination event
|
||||
|
||||
i += 1
|
||||
|
||||
if numsamples > 0:
|
||||
self._data_buffer += ctypes.string_at(wav, numsamples * ctypes.sizeof(ctypes.c_short))
|
||||
return 0
|
||||
|
||||
|
||||
def endLoop(self):
|
||||
self._looping = False
|
||||
|
||||
def startLoop(self):
|
||||
first = True
|
||||
self._looping = True
|
||||
while self._looping:
|
||||
if not self._looping:
|
||||
break
|
||||
if first:
|
||||
self._proxy.setBusy(False)
|
||||
first = False
|
||||
if self._text_to_say:
|
||||
self._start_synthesis(self._text_to_say)
|
||||
self.iterate()
|
||||
time.sleep(0.01)
|
||||
|
||||
def iterate(self):
|
||||
if not self._looping:
|
||||
return
|
||||
if self._stopping:
|
||||
_espeak.Cancel()
|
||||
self._stopping = False
|
||||
self._proxy.notify('finished-utterance', completed=False)
|
||||
self._proxy.setBusy(False)
|
||||
self.endLoop()
|
||||
|
||||
def say(self, text):
|
||||
self._text_to_say = text
|
165
venv/lib/python3.11/site-packages/pyttsx3/drivers/nsss.py
Normal file
165
venv/lib/python3.11/site-packages/pyttsx3/drivers/nsss.py
Normal file
@@ -0,0 +1,165 @@
|
||||
# noinspection PyUnresolvedReferences
|
||||
import objc
|
||||
from AppKit import NSSpeechSynthesizer
|
||||
from Foundation import *
|
||||
from PyObjCTools import AppHelper
|
||||
# noinspection PyProtectedMember
|
||||
from PyObjCTools.AppHelper import PyObjCAppHelperRunLoopStopper
|
||||
|
||||
from ..voice import Voice
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
class RunLoopStopper(PyObjCAppHelperRunLoopStopper):
|
||||
"""
|
||||
Overrides PyObjCAppHelperRunLoopStopper to terminate after endLoop.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.shouldStop = False
|
||||
|
||||
def init(self):
|
||||
return objc.super(RunLoopStopper, self).init()
|
||||
|
||||
def stop(self):
|
||||
self.shouldStop = True
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
def buildDriver(proxy):
|
||||
return NSSpeechDriver.alloc().initWithProxy(proxy)
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences,PyPep8Naming,PyUnusedLocal
|
||||
class NSSpeechDriver(NSObject):
|
||||
|
||||
def __init__(self):
|
||||
self._proxy = None
|
||||
self._tts = None
|
||||
self._completed = False
|
||||
self._current_text = ''
|
||||
|
||||
@objc.python_method
|
||||
def initWithProxy(self, proxy):
|
||||
try:
|
||||
proxy_attr = objc.super(NSSpeechDriver, self).init()
|
||||
except AttributeError:
|
||||
proxy_attr = self
|
||||
if proxy_attr:
|
||||
self._proxy = proxy
|
||||
self._tts = NSSpeechSynthesizer.alloc().initWithVoice_(None)
|
||||
self._tts.setDelegate_(self)
|
||||
# default rate
|
||||
self._tts.setRate_(200)
|
||||
self._completed = True
|
||||
return self
|
||||
|
||||
def destroy(self):
|
||||
self._tts.setDelegate_(None)
|
||||
del self._tts
|
||||
|
||||
def onPumpFirst_(self, timer):
|
||||
self._proxy.setBusy(False)
|
||||
|
||||
def startLoop(self):
|
||||
# https://github.com/ronaldoussoren/pyobjc/blob/mater/pyobjc-framework-Cocoa/Lib/PyObjCTools/AppHelper.py#L243C25-L243C25 # noqa
|
||||
NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
||||
0.0, self, 'onPumpFirst:', None, False
|
||||
)
|
||||
runLoop = NSRunLoop.currentRunLoop()
|
||||
stopper = RunLoopStopper.alloc().init()
|
||||
PyObjCAppHelperRunLoopStopper.addRunLoopStopper_toRunLoop_(stopper, runLoop)
|
||||
while stopper.shouldRun():
|
||||
nextfire = runLoop.limitDateForMode_(NSDefaultRunLoopMode)
|
||||
soon = NSDate.dateWithTimeIntervalSinceNow_(0) # maxTimeout in runConsoleEventLoop
|
||||
if nextfire is not None:
|
||||
nextfire = soon.earlierDate_(nextfire)
|
||||
if not runLoop.runMode_beforeDate_(NSDefaultRunLoopMode, nextfire):
|
||||
stopper.stop()
|
||||
break
|
||||
PyObjCAppHelperRunLoopStopper.removeRunLoopStopperFromRunLoop_(runLoop)
|
||||
|
||||
@staticmethod
|
||||
def endLoop():
|
||||
AppHelper.stopEventLoop()
|
||||
|
||||
def iterate(self):
|
||||
self._proxy.setBusy(False)
|
||||
yield
|
||||
|
||||
@objc.python_method
|
||||
def say(self, text):
|
||||
self._proxy.setBusy(True)
|
||||
self._completed = True
|
||||
self._proxy.notify('started-utterance')
|
||||
self._current_text = text
|
||||
self._tts.startSpeakingString_(text)
|
||||
|
||||
def stop(self):
|
||||
if self._proxy.isBusy():
|
||||
self._completed = False
|
||||
self._tts.stopSpeaking()
|
||||
|
||||
@objc.python_method
|
||||
def _toVoice(self, attr):
|
||||
return Voice(attr.get('VoiceIdentifier'), attr.get('VoiceName'),
|
||||
[attr.get('VoiceLocaleIdentifier', attr.get('VoiceLanguage'))], attr.get('VoiceGender'),
|
||||
attr.get('VoiceAge'))
|
||||
|
||||
@objc.python_method
|
||||
def getProperty(self, name):
|
||||
if name == 'voices':
|
||||
return [self._toVoice(NSSpeechSynthesizer.attributesForVoice_(v))
|
||||
for v in list(NSSpeechSynthesizer.availableVoices())]
|
||||
elif name == 'voice':
|
||||
return self._tts.voice()
|
||||
elif name == 'rate':
|
||||
return self._tts.rate()
|
||||
elif name == 'volume':
|
||||
return self._tts.volume()
|
||||
elif name == "pitch":
|
||||
print("Pitch adjustment not supported when using NSSS")
|
||||
else:
|
||||
raise KeyError('unknown property %s' % name)
|
||||
|
||||
@objc.python_method
|
||||
def setProperty(self, name, value):
|
||||
if name == 'voice':
|
||||
# vol/rate gets reset, so store and restore it
|
||||
vol = self._tts.volume()
|
||||
rate = self._tts.rate()
|
||||
self._tts.setVoice_(value)
|
||||
self._tts.setRate_(rate)
|
||||
self._tts.setVolume_(vol)
|
||||
elif name == 'rate':
|
||||
self._tts.setRate_(value)
|
||||
elif name == 'volume':
|
||||
self._tts.setVolume_(value)
|
||||
elif name == 'pitch':
|
||||
print("Pitch adjustment not supported when using NSSS")
|
||||
else:
|
||||
raise KeyError('unknown property %s' % name)
|
||||
|
||||
@objc.python_method
|
||||
def save_to_file(self, text, filename):
|
||||
self._proxy.setBusy(True)
|
||||
self._completed = True
|
||||
url = Foundation.NSURL.fileURLWithPath_(filename)
|
||||
self._tts.startSpeakingString_toURL_(text, url)
|
||||
|
||||
def speechSynthesizer_didFinishSpeaking_(self, tts, success):
|
||||
if not self._completed:
|
||||
success = False
|
||||
else:
|
||||
success = True
|
||||
self._proxy.notify('finished-utterance', completed=success)
|
||||
self._proxy.setBusy(False)
|
||||
|
||||
def speechSynthesizer_willSpeakWord_ofString_(self, tts, rng, text):
|
||||
if self._current_text:
|
||||
current_word = self._current_text[rng.location:rng.location + rng.length]
|
||||
else:
|
||||
current_word = "Unknown"
|
||||
|
||||
self._proxy.notify('started-word', name=current_word, location=rng.location,
|
||||
length=rng.length)
|
187
venv/lib/python3.11/site-packages/pyttsx3/drivers/sapi5.py
Normal file
187
venv/lib/python3.11/site-packages/pyttsx3/drivers/sapi5.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# noinspection PyUnresolvedReferences
|
||||
import comtypes.client # Importing comtypes.client will make the gen subpackage
|
||||
|
||||
try:
|
||||
from comtypes.gen import SpeechLib # comtypes
|
||||
except ImportError:
|
||||
# Generate the SpeechLib lib and any associated files
|
||||
engine = comtypes.client.CreateObject("SAPI.SpVoice")
|
||||
stream = comtypes.client.CreateObject("SAPI.SpFileStream")
|
||||
# noinspection PyUnresolvedReferences
|
||||
from comtypes.gen import SpeechLib
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
import math
|
||||
import os
|
||||
import time
|
||||
import weakref
|
||||
|
||||
import pythoncom
|
||||
|
||||
from ..voice import Voice
|
||||
from . import fromUtf8, toUtf8
|
||||
|
||||
# common voices
|
||||
MSSAM = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\MSSam'
|
||||
MSMARY = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\MSMary'
|
||||
MSMIKE = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\MSMike'
|
||||
|
||||
# coeffs for wpm conversion
|
||||
E_REG = {MSSAM: (137.89, 1.11),
|
||||
MSMARY: (156.63, 1.11),
|
||||
MSMIKE: (154.37, 1.11)}
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
def buildDriver(proxy):
|
||||
return SAPI5Driver(proxy)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyShadowingNames
|
||||
class SAPI5Driver(object):
|
||||
def __init__(self, proxy):
|
||||
self._tts = comtypes.client.CreateObject('SAPI.SPVoice')
|
||||
# all events
|
||||
self._tts.EventInterests = 33790
|
||||
self._event_sink = SAPI5DriverEventSink()
|
||||
self._event_sink.setDriver(weakref.proxy(self))
|
||||
self._advise = comtypes.client.GetEvents(self._tts, self._event_sink)
|
||||
self._proxy = proxy
|
||||
self._looping = False
|
||||
self._speaking = False
|
||||
self._stopping = False
|
||||
self._current_text = ''
|
||||
# initial rate
|
||||
self._rateWpm = 200
|
||||
self.setProperty('voice', self.getProperty('voice'))
|
||||
|
||||
def destroy(self):
|
||||
self._tts.EventInterests = 0
|
||||
|
||||
def say(self, text):
|
||||
self._proxy.setBusy(True)
|
||||
self._proxy.notify('started-utterance')
|
||||
self._speaking = True
|
||||
self._current_text = text
|
||||
# call this async otherwise this blocks the callbacks
|
||||
# see SpeechVoiceSpeakFlags: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms720892%28v%3dvs.85%29
|
||||
# and Speak : https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms723609(v=vs.85)
|
||||
self._tts.Speak(fromUtf8(toUtf8(text)), 1) # -> stream_number as described in the remarks of the documentation
|
||||
|
||||
def stop(self):
|
||||
if not self._speaking:
|
||||
return
|
||||
self._proxy.setBusy(True)
|
||||
self._stopping = True
|
||||
self._tts.Speak('', 3)
|
||||
|
||||
def save_to_file(self, text, filename):
|
||||
cwd = os.getcwd()
|
||||
stream = comtypes.client.CreateObject('SAPI.SPFileStream')
|
||||
stream.Open(filename, SpeechLib.SSFMCreateForWrite)
|
||||
temp_stream = self._tts.AudioOutputStream
|
||||
self._tts.AudioOutputStream = stream
|
||||
self._tts.Speak(fromUtf8(toUtf8(text)))
|
||||
self._tts.AudioOutputStream = temp_stream
|
||||
stream.close()
|
||||
os.chdir(cwd)
|
||||
|
||||
@staticmethod
|
||||
def _toVoice(attr):
|
||||
return Voice(attr.Id, attr.GetDescription())
|
||||
|
||||
def _tokenFromId(self, id_):
|
||||
tokens = self._tts.GetVoices()
|
||||
for token in tokens:
|
||||
if token.Id == id_:
|
||||
return token
|
||||
raise ValueError('unknown voice id %s', id_)
|
||||
|
||||
def getProperty(self, name):
|
||||
if name == 'voices':
|
||||
return [self._toVoice(attr) for attr in self._tts.GetVoices()]
|
||||
elif name == 'voice':
|
||||
return self._tts.Voice.Id
|
||||
elif name == 'rate':
|
||||
return self._rateWpm
|
||||
elif name == 'volume':
|
||||
return self._tts.Volume / 100.0
|
||||
elif name == 'pitch':
|
||||
print("Pitch adjustment not supported when using SAPI5")
|
||||
else:
|
||||
raise KeyError('unknown property %s' % name)
|
||||
|
||||
def setProperty(self, name, value):
|
||||
if name == 'voice':
|
||||
token = self._tokenFromId(value)
|
||||
self._tts.Voice = token
|
||||
a, b = E_REG.get(value, E_REG[MSMARY])
|
||||
self._tts.Rate = int(math.log(self._rateWpm / a, b))
|
||||
elif name == 'rate':
|
||||
id_ = self._tts.Voice.Id
|
||||
a, b = E_REG.get(id_, E_REG[MSMARY])
|
||||
try:
|
||||
self._tts.Rate = int(math.log(value / a, b))
|
||||
except TypeError as e:
|
||||
raise ValueError(str(e))
|
||||
self._rateWpm = value
|
||||
elif name == 'volume':
|
||||
try:
|
||||
self._tts.Volume = int(round(value * 100, 2))
|
||||
except TypeError as e:
|
||||
raise ValueError(str(e))
|
||||
elif name == 'pitch':
|
||||
print("Pitch adjustment not supported when using SAPI5")
|
||||
else:
|
||||
raise KeyError('unknown property %s' % name)
|
||||
|
||||
def startLoop(self):
|
||||
first = True
|
||||
self._looping = True
|
||||
while self._looping:
|
||||
if first:
|
||||
self._proxy.setBusy(False)
|
||||
first = False
|
||||
pythoncom.PumpWaitingMessages()
|
||||
time.sleep(0.05)
|
||||
|
||||
def endLoop(self):
|
||||
self._looping = False
|
||||
|
||||
def iterate(self):
|
||||
self._proxy.setBusy(False)
|
||||
while 1:
|
||||
pythoncom.PumpWaitingMessages()
|
||||
yield
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyProtectedMember,PyUnusedLocal,PyShadowingNames
|
||||
class SAPI5DriverEventSink(object):
|
||||
def __init__(self):
|
||||
self._driver = None
|
||||
|
||||
def setDriver(self, driver):
|
||||
self._driver = driver
|
||||
|
||||
def _ISpeechVoiceEvents_StartStream(self, stream_number, stream_position):
|
||||
self._driver._proxy.notify(
|
||||
'started-word', location=stream_number, length=stream_position)
|
||||
|
||||
def _ISpeechVoiceEvents_EndStream(self, stream_number, stream_position):
|
||||
d = self._driver
|
||||
if d._speaking:
|
||||
d._proxy.notify('finished-utterance', completed=not d._stopping)
|
||||
d._speaking = False
|
||||
d._stopping = False
|
||||
d._proxy.setBusy(False)
|
||||
d.endLoop() # hangs if you dont have this
|
||||
|
||||
def _ISpeechVoiceEvents_Word(self, stream_number, stream_position, char, length):
|
||||
current_text = self._driver._current_text
|
||||
if current_text:
|
||||
current_word = current_text[char:char + length]
|
||||
else:
|
||||
current_word = "Unknown"
|
||||
|
||||
self._driver._proxy.notify(
|
||||
'started-word', name=current_word, location=char, length=length)
|
220
venv/lib/python3.11/site-packages/pyttsx3/engine.py
Normal file
220
venv/lib/python3.11/site-packages/pyttsx3/engine.py
Normal file
@@ -0,0 +1,220 @@
|
||||
from . import driver
|
||||
import traceback
|
||||
import weakref
|
||||
|
||||
|
||||
class Engine(object):
|
||||
"""
|
||||
@ivar proxy: Proxy to a driver implementation
|
||||
@type proxy: L{DriverProxy}
|
||||
@ivar _connects: Array of subscriptions
|
||||
@type _connects: list
|
||||
@ivar _inLoop: Running an event loop or not
|
||||
@type _inLoop: bool
|
||||
@ivar _driverLoop: Using a driver event loop or not
|
||||
@type _driverLoop: bool
|
||||
@ivar _debug: Print exceptions or not
|
||||
@type _debug: bool
|
||||
"""
|
||||
|
||||
def __init__(self, driverName=None, debug=False):
|
||||
"""
|
||||
Constructs a new TTS engine instance.
|
||||
|
||||
@param driverName: Name of the platform specific driver to use. If
|
||||
None, selects the default driver for the operating system.
|
||||
@type: str
|
||||
@param debug: Debugging output enabled or not
|
||||
@type debug: bool
|
||||
"""
|
||||
self.proxy = driver.DriverProxy(weakref.proxy(self), driverName, debug)
|
||||
# initialize other vars
|
||||
self._connects = {}
|
||||
self._inLoop = False
|
||||
self._driverLoop = True
|
||||
self._debug = debug
|
||||
|
||||
def _notify(self, topic, **kwargs):
|
||||
"""
|
||||
Invokes callbacks for an event topic.
|
||||
|
||||
@param topic: String event name
|
||||
@type topic: str
|
||||
@param kwargs: Values associated with the event
|
||||
@type kwargs: dict
|
||||
"""
|
||||
for cb in self._connects.get(topic, []):
|
||||
try:
|
||||
cb(**kwargs)
|
||||
except Exception:
|
||||
if self._debug:
|
||||
traceback.print_exc()
|
||||
|
||||
def connect(self, topic, cb):
|
||||
"""
|
||||
Registers a callback for an event topic. Valid topics and their
|
||||
associated values:
|
||||
|
||||
started-utterance: name=<str>
|
||||
started-word: name=<str>, location=<int>, length=<int>
|
||||
finished-utterance: name=<str>, completed=<bool>
|
||||
error: name=<str>, exception=<exception>
|
||||
|
||||
@param topic: Event topic name
|
||||
@type topic: str
|
||||
@param cb: Callback function
|
||||
@type cb: callable
|
||||
@return: Token to use to unregister
|
||||
@rtype: dict
|
||||
"""
|
||||
arr = self._connects.setdefault(topic, [])
|
||||
arr.append(cb)
|
||||
return {'topic': topic, 'cb': cb}
|
||||
|
||||
def disconnect(self, token):
|
||||
"""
|
||||
Unregisters a callback for an event topic.
|
||||
|
||||
@param token: Token of the callback to unregister
|
||||
@type token: dict
|
||||
"""
|
||||
topic = token['topic']
|
||||
try:
|
||||
arr = self._connects[topic]
|
||||
except KeyError:
|
||||
return
|
||||
arr.remove(token['cb'])
|
||||
if len(arr) == 0:
|
||||
del self._connects[topic]
|
||||
|
||||
def say(self, text, name=None):
|
||||
"""
|
||||
Adds an utterance to speak to the event queue.
|
||||
|
||||
@param text: Text to speak
|
||||
@type text: unicode
|
||||
@param name: Name to associate with this utterance. Included in
|
||||
notifications about this utterance.
|
||||
@type name: str
|
||||
"""
|
||||
if text == None:
|
||||
return "Argument value can't be none or empty"
|
||||
else:
|
||||
self.proxy.say(text, name)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stops the current utterance and clears the event queue.
|
||||
"""
|
||||
self.proxy.stop()
|
||||
|
||||
def save_to_file(self, text, filename, name=None):
|
||||
'''
|
||||
Adds an utterance to speak to the event queue.
|
||||
|
||||
@param text: Text to speak
|
||||
@type text: unicode
|
||||
@param filename: the name of file to save.
|
||||
@param name: Name to associate with this utterance. Included in
|
||||
notifications about this utterance.
|
||||
@type name: str
|
||||
'''
|
||||
self.proxy.save_to_file(text, filename, name)
|
||||
|
||||
def isBusy(self):
|
||||
"""
|
||||
@return: True if an utterance is currently being spoken, false if not
|
||||
@rtype: bool
|
||||
"""
|
||||
return self.proxy.isBusy()
|
||||
|
||||
def getProperty(self, name):
|
||||
"""
|
||||
Gets the current value of a property. Valid names and values include:
|
||||
|
||||
voices: List of L{voice.Voice} objects supported by the driver
|
||||
voice: String ID of the current voice
|
||||
rate: Integer speech rate in words per minute
|
||||
volume: Floating point volume of speech in the range [0.0, 1.0]
|
||||
|
||||
Numeric values outside the valid range supported by the driver are
|
||||
clipped.
|
||||
|
||||
@param name: Name of the property to fetch
|
||||
@type name: str
|
||||
@return: Value associated with the property
|
||||
@rtype: object
|
||||
@raise KeyError: When the property name is unknown
|
||||
"""
|
||||
return self.proxy.getProperty(name)
|
||||
|
||||
def setProperty(self, name, value):
|
||||
"""
|
||||
Adds a property value to set to the event queue. Valid names and values
|
||||
include:
|
||||
|
||||
voice: String ID of the voice
|
||||
rate: Integer speech rate in words per minute
|
||||
volume: Floating point volume of speech in the range [0.0, 1.0]
|
||||
|
||||
Numeric values outside the valid range supported by the driver are
|
||||
clipped.
|
||||
|
||||
@param name: Name of the property to fetch
|
||||
@type name: str
|
||||
@param: Value to set for the property
|
||||
@rtype: object
|
||||
@raise KeyError: When the property name is unknown
|
||||
"""
|
||||
self.proxy.setProperty(name, value)
|
||||
|
||||
def runAndWait(self):
|
||||
"""
|
||||
Runs an event loop until all commands queued up until this method call
|
||||
complete. Blocks during the event loop and returns when the queue is
|
||||
cleared.
|
||||
|
||||
@raise RuntimeError: When the loop is already running
|
||||
"""
|
||||
if self._inLoop:
|
||||
raise RuntimeError('run loop already started')
|
||||
self._inLoop = True
|
||||
self._driverLoop = True
|
||||
self.proxy.runAndWait()
|
||||
|
||||
def startLoop(self, useDriverLoop=True):
|
||||
"""
|
||||
Starts an event loop to process queued commands and callbacks.
|
||||
|
||||
@param useDriverLoop: If True, uses the run loop provided by the driver
|
||||
(the default). If False, assumes the caller will enter its own
|
||||
run loop which will pump any events for the TTS engine properly.
|
||||
@type useDriverLoop: bool
|
||||
@raise RuntimeError: When the loop is already running
|
||||
"""
|
||||
if self._inLoop:
|
||||
raise RuntimeError('run loop already started')
|
||||
self._inLoop = True
|
||||
self._driverLoop = useDriverLoop
|
||||
self.proxy.startLoop(self._driverLoop)
|
||||
|
||||
def endLoop(self):
|
||||
"""
|
||||
Stops a running event loop.
|
||||
|
||||
@raise RuntimeError: When the loop is not running
|
||||
"""
|
||||
if not self._inLoop:
|
||||
raise RuntimeError('run loop not started')
|
||||
self.proxy.endLoop(self._driverLoop)
|
||||
self._inLoop = False
|
||||
|
||||
def iterate(self):
|
||||
"""
|
||||
Must be called regularly when using an external event loop.
|
||||
"""
|
||||
if not self._inLoop:
|
||||
raise RuntimeError('run loop not started')
|
||||
elif self._driverLoop:
|
||||
raise RuntimeError('iterate not valid in driver run loop')
|
||||
self.proxy.iterate()
|
836
venv/lib/python3.11/site-packages/pyttsx3/six.py
Normal file
836
venv/lib/python3.11/site-packages/pyttsx3/six.py
Normal file
@@ -0,0 +1,836 @@
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import operator
|
||||
import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.9.0"
|
||||
|
||||
|
||||
# Useful for very coarse version differentiation.
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
integer_types = int,
|
||||
class_types = type,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
|
||||
MAXSIZE = sys.maxsize
|
||||
else:
|
||||
string_types = basestring,
|
||||
integer_types = (int, long)
|
||||
class_types = (type, types.ClassType)
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
|
||||
if sys.platform.startswith("java"):
|
||||
# Jython always uses 32 bits.
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||
class X(object):
|
||||
def __len__(self):
|
||||
return 1 << 31
|
||||
try:
|
||||
len(X())
|
||||
except OverflowError:
|
||||
# 32-bit
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# 64-bit
|
||||
MAXSIZE = int((1 << 63) - 1)
|
||||
del X
|
||||
|
||||
|
||||
def _add_doc(func, doc):
|
||||
"""Add documentation to a function."""
|
||||
func.__doc__ = doc
|
||||
|
||||
|
||||
def _import_module(name):
|
||||
"""Import module, returning the module after the last dot."""
|
||||
__import__(name)
|
||||
return sys.modules[name]
|
||||
|
||||
|
||||
class _LazyDescr(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __get__(self, obj, tp):
|
||||
result = self._resolve()
|
||||
setattr(obj, self.name, result) # Invokes __set__.
|
||||
try:
|
||||
# This is a bit ugly, but it avoids running this again by
|
||||
# removing this descriptor.
|
||||
delattr(obj.__class__, self.name)
|
||||
except AttributeError:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
class MovedModule(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old, new=None):
|
||||
super(MovedModule, self).__init__(name)
|
||||
if PY3:
|
||||
if new is None:
|
||||
new = name
|
||||
self.mod = new
|
||||
else:
|
||||
self.mod = old
|
||||
|
||||
def _resolve(self):
|
||||
return _import_module(self.mod)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
_module = self._resolve()
|
||||
value = getattr(_module, attr)
|
||||
setattr(self, attr, value)
|
||||
return value
|
||||
|
||||
|
||||
class _LazyModule(types.ModuleType):
|
||||
|
||||
def __init__(self, name):
|
||||
super(_LazyModule, self).__init__(name)
|
||||
self.__doc__ = self.__class__.__doc__
|
||||
|
||||
def __dir__(self):
|
||||
attrs = ["__doc__", "__name__"]
|
||||
attrs += [attr.name for attr in self._moved_attributes]
|
||||
return attrs
|
||||
|
||||
# Subclasses should override this
|
||||
_moved_attributes = []
|
||||
|
||||
|
||||
class MovedAttribute(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||
super(MovedAttribute, self).__init__(name)
|
||||
if PY3:
|
||||
if new_mod is None:
|
||||
new_mod = name
|
||||
self.mod = new_mod
|
||||
if new_attr is None:
|
||||
if old_attr is None:
|
||||
new_attr = name
|
||||
else:
|
||||
new_attr = old_attr
|
||||
self.attr = new_attr
|
||||
else:
|
||||
self.mod = old_mod
|
||||
if old_attr is None:
|
||||
old_attr = name
|
||||
self.attr = old_attr
|
||||
|
||||
def _resolve(self):
|
||||
module = _import_module(self.mod)
|
||||
return getattr(module, self.attr)
|
||||
|
||||
|
||||
class _SixMetaPathImporter(object):
|
||||
"""
|
||||
A meta path importer to import six.moves and its submodules.
|
||||
|
||||
This class implements a PEP302 finder and loader. It should be compatible
|
||||
with Python 2.5 and all existing versions of Python3
|
||||
"""
|
||||
|
||||
def __init__(self, six_module_name):
|
||||
self.name = six_module_name
|
||||
self.known_modules = {}
|
||||
|
||||
def _add_module(self, mod, *fullnames):
|
||||
for fullname in fullnames:
|
||||
self.known_modules[self.name + "." + fullname] = mod
|
||||
|
||||
def _get_module(self, fullname):
|
||||
return self.known_modules[self.name + "." + fullname]
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
if fullname in self.known_modules:
|
||||
return self
|
||||
return None
|
||||
|
||||
def __get_module(self, fullname):
|
||||
try:
|
||||
return self.known_modules[fullname]
|
||||
except KeyError:
|
||||
raise ImportError("This loader does not know module " + fullname)
|
||||
|
||||
def load_module(self, fullname):
|
||||
try:
|
||||
# in case of a reload
|
||||
return sys.modules[fullname]
|
||||
except KeyError:
|
||||
pass
|
||||
mod = self.__get_module(fullname)
|
||||
if isinstance(mod, MovedModule):
|
||||
mod = mod._resolve()
|
||||
else:
|
||||
mod.__loader__ = self
|
||||
sys.modules[fullname] = mod
|
||||
return mod
|
||||
|
||||
def is_package(self, fullname):
|
||||
"""
|
||||
Return true, if the named module is a package.
|
||||
|
||||
We need this method to get correct spec objects with
|
||||
Python 3.4 (see PEP451)
|
||||
"""
|
||||
return hasattr(self.__get_module(fullname), "__path__")
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Return None
|
||||
|
||||
Required, if is_package is implemented"""
|
||||
self.__get_module(fullname) # eventually raises ImportError
|
||||
return None
|
||||
get_source = get_code # same as get_code
|
||||
|
||||
|
||||
_importer = _SixMetaPathImporter(__name__)
|
||||
|
||||
|
||||
class _MovedItems(_LazyModule):
|
||||
"""Lazy loading of moved objects"""
|
||||
__path__ = [] # mark as package
|
||||
|
||||
|
||||
_moved_attributes = [
|
||||
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||
MovedAttribute("filterfalse", "itertools", "itertools",
|
||||
"ifilterfalse", "filterfalse"),
|
||||
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
||||
MovedAttribute("intern", "__builtin__", "sys"),
|
||||
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
|
||||
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
|
||||
MovedAttribute("StringIO", "StringIO", "io"),
|
||||
MovedAttribute("UserDict", "UserDict", "collections"),
|
||||
MovedAttribute("UserList", "UserList", "collections"),
|
||||
MovedAttribute("UserString", "UserString", "collections"),
|
||||
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||
MovedAttribute("zip_longest", "itertools", "itertools",
|
||||
"izip_longest", "zip_longest"),
|
||||
|
||||
MovedModule("builtins", "__builtin__"),
|
||||
MovedModule("configparser", "ConfigParser"),
|
||||
MovedModule("copyreg", "copy_reg"),
|
||||
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
||||
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
|
||||
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||
MovedModule("http_client", "httplib", "http.client"),
|
||||
MovedModule("email_mime_multipart", "email.MIMEMultipart",
|
||||
"email.mime.multipart"),
|
||||
MovedModule("email_mime_nonmultipart",
|
||||
"email.MIMENonMultipart", "email.mime.nonmultipart"),
|
||||
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
||||
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
||||
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||
MovedModule("cPickle", "cPickle", "pickle"),
|
||||
MovedModule("queue", "Queue"),
|
||||
MovedModule("reprlib", "repr"),
|
||||
MovedModule("socketserver", "SocketServer"),
|
||||
MovedModule("_thread", "thread", "_thread"),
|
||||
MovedModule("tkinter", "Tkinter"),
|
||||
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
||||
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_scrolledtext", "ScrolledText",
|
||||
"tkinter.scrolledtext"),
|
||||
MovedModule("tkinter_simpledialog", "SimpleDialog",
|
||||
"tkinter.simpledialog"),
|
||||
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
||||
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
|
||||
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
||||
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
||||
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
||||
"tkinter.colorchooser"),
|
||||
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
||||
"tkinter.commondialog"),
|
||||
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
||||
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
||||
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
||||
"tkinter.simpledialog"),
|
||||
MovedModule("urllib_parse", __name__ +
|
||||
".moves.urllib_parse", "urllib.parse"),
|
||||
MovedModule("urllib_error", __name__ +
|
||||
".moves.urllib_error", "urllib.error"),
|
||||
MovedModule("urllib", __name__ + ".moves.urllib",
|
||||
__name__ + ".moves.urllib"),
|
||||
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
||||
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
|
||||
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
|
||||
MovedModule("winreg", "_winreg"),
|
||||
]
|
||||
for attr in _moved_attributes:
|
||||
setattr(_MovedItems, attr.name, attr)
|
||||
if isinstance(attr, MovedModule):
|
||||
_importer._add_module(attr, "moves." + attr.name)
|
||||
del attr
|
||||
|
||||
_MovedItems._moved_attributes = _moved_attributes
|
||||
|
||||
moves = _MovedItems(__name__ + ".moves")
|
||||
_importer._add_module(moves, "moves")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_parse(_LazyModule):
|
||||
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
||||
|
||||
|
||||
_urllib_parse_moved_attributes = [
|
||||
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("quote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
||||
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
|
||||
]
|
||||
for attr in _urllib_parse_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_parse, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
|
||||
"moves.urllib_parse", "moves.urllib.parse")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_error(_LazyModule):
|
||||
"""Lazy loading of moved objects in six.moves.urllib_error"""
|
||||
|
||||
|
||||
_urllib_error_moved_attributes = [
|
||||
MovedAttribute("URLError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
|
||||
]
|
||||
for attr in _urllib_error_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_error, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
|
||||
"moves.urllib_error", "moves.urllib.error")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_request(_LazyModule):
|
||||
"""Lazy loading of moved objects in six.moves.urllib_request"""
|
||||
|
||||
|
||||
_urllib_request_moved_attributes = [
|
||||
MovedAttribute("urlopen", "urllib2", "urllib.request"),
|
||||
MovedAttribute("install_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("build_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("pathname2url", "urllib", "urllib.request"),
|
||||
MovedAttribute("url2pathname", "urllib", "urllib.request"),
|
||||
MovedAttribute("getproxies", "urllib", "urllib.request"),
|
||||
MovedAttribute("Request", "urllib2", "urllib.request"),
|
||||
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgrWithDefaultRealm",
|
||||
"urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
|
||||
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
|
||||
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
||||
]
|
||||
for attr in _urllib_request_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
|
||||
"moves.urllib_request", "moves.urllib.request")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_response(_LazyModule):
|
||||
"""Lazy loading of moved objects in six.moves.urllib_response"""
|
||||
|
||||
|
||||
_urllib_response_moved_attributes = [
|
||||
MovedAttribute("addbase", "urllib", "urllib.response"),
|
||||
MovedAttribute("addclosehook", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfo", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfourl", "urllib", "urllib.response"),
|
||||
]
|
||||
for attr in _urllib_response_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_response, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
|
||||
"moves.urllib_response", "moves.urllib.response")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_robotparser(_LazyModule):
|
||||
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
|
||||
|
||||
|
||||
_urllib_robotparser_moved_attributes = [
|
||||
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
|
||||
]
|
||||
for attr in _urllib_robotparser_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
|
||||
"moves.urllib_robotparser", "moves.urllib.robotparser")
|
||||
|
||||
|
||||
class Module_six_moves_urllib(types.ModuleType):
|
||||
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
|
||||
__path__ = [] # mark as package
|
||||
parse = _importer._get_module("moves.urllib_parse")
|
||||
error = _importer._get_module("moves.urllib_error")
|
||||
request = _importer._get_module("moves.urllib_request")
|
||||
response = _importer._get_module("moves.urllib_response")
|
||||
robotparser = _importer._get_module("moves.urllib_robotparser")
|
||||
|
||||
def __dir__(self):
|
||||
return ['parse', 'error', 'request', 'response', 'robotparser']
|
||||
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
|
||||
"moves.urllib")
|
||||
|
||||
|
||||
def add_move(move):
|
||||
"""Add an item to six.moves."""
|
||||
setattr(_MovedItems, move.name, move)
|
||||
|
||||
|
||||
def remove_move(name):
|
||||
"""Remove item from six.moves."""
|
||||
try:
|
||||
delattr(_MovedItems, name)
|
||||
except AttributeError:
|
||||
try:
|
||||
del moves.__dict__[name]
|
||||
except KeyError:
|
||||
raise AttributeError("no such move, %r" % (name,))
|
||||
|
||||
|
||||
if PY3:
|
||||
_meth_func = "__func__"
|
||||
_meth_self = "__self__"
|
||||
|
||||
_func_closure = "__closure__"
|
||||
_func_code = "__code__"
|
||||
_func_defaults = "__defaults__"
|
||||
_func_globals = "__globals__"
|
||||
else:
|
||||
_meth_func = "im_func"
|
||||
_meth_self = "im_self"
|
||||
|
||||
_func_closure = "func_closure"
|
||||
_func_code = "func_code"
|
||||
_func_defaults = "func_defaults"
|
||||
_func_globals = "func_globals"
|
||||
|
||||
|
||||
try:
|
||||
advance_iterator = next
|
||||
except NameError:
|
||||
def advance_iterator(it):
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
|
||||
try:
|
||||
callable = callable
|
||||
except NameError:
|
||||
def callable(obj):
|
||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||
|
||||
|
||||
if PY3:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound
|
||||
|
||||
create_bound_method = types.MethodType
|
||||
|
||||
Iterator = object
|
||||
else:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound.im_func
|
||||
|
||||
def create_bound_method(func, obj):
|
||||
return types.MethodType(func, obj, obj.__class__)
|
||||
|
||||
class Iterator(object):
|
||||
|
||||
def next(self):
|
||||
return type(self).__next__(self)
|
||||
|
||||
callable = callable
|
||||
_add_doc(get_unbound_function,
|
||||
"""Get the function out of a possibly unbound function""")
|
||||
|
||||
|
||||
get_method_function = operator.attrgetter(_meth_func)
|
||||
get_method_self = operator.attrgetter(_meth_self)
|
||||
get_function_closure = operator.attrgetter(_func_closure)
|
||||
get_function_code = operator.attrgetter(_func_code)
|
||||
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||
get_function_globals = operator.attrgetter(_func_globals)
|
||||
|
||||
|
||||
if PY3:
|
||||
def iterkeys(d, **kw):
|
||||
return iter(d.keys(**kw))
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return iter(d.values(**kw))
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return iter(d.items(**kw))
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return iter(d.lists(**kw))
|
||||
|
||||
viewkeys = operator.methodcaller("keys")
|
||||
|
||||
viewvalues = operator.methodcaller("values")
|
||||
|
||||
viewitems = operator.methodcaller("items")
|
||||
else:
|
||||
def iterkeys(d, **kw):
|
||||
return iter(d.iterkeys(**kw))
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return iter(d.itervalues(**kw))
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return iter(d.iteritems(**kw))
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return iter(d.iterlists(**kw))
|
||||
|
||||
viewkeys = operator.methodcaller("viewkeys")
|
||||
|
||||
viewvalues = operator.methodcaller("viewvalues")
|
||||
|
||||
viewitems = operator.methodcaller("viewitems")
|
||||
|
||||
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
|
||||
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
|
||||
_add_doc(iteritems,
|
||||
"Return an iterator over the (key, value) pairs of a dictionary.")
|
||||
_add_doc(iterlists,
|
||||
"Return an iterator over the (key, [values]) pairs of a dictionary.")
|
||||
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
def u(s):
|
||||
return s
|
||||
unichr = chr
|
||||
if sys.version_info[1] <= 1:
|
||||
def int2byte(i):
|
||||
return bytes((i,))
|
||||
else:
|
||||
# This is about 2x faster than the implementation above on 3.2+
|
||||
int2byte = operator.methodcaller("to_bytes", 1, "big")
|
||||
byte2int = operator.itemgetter(0)
|
||||
indexbytes = operator.getitem
|
||||
iterbytes = iter
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
BytesIO = io.BytesIO
|
||||
_assertCountEqual = "assertCountEqual"
|
||||
_assertRaisesRegex = "assertRaisesRegex"
|
||||
_assertRegex = "assertRegex"
|
||||
else:
|
||||
def b(s):
|
||||
return s
|
||||
# Workaround for standalone backslash
|
||||
|
||||
def u(s):
|
||||
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
|
||||
unichr = unichr
|
||||
int2byte = chr
|
||||
|
||||
def byte2int(bs):
|
||||
return ord(bs[0])
|
||||
|
||||
def indexbytes(buf, i):
|
||||
return ord(buf[i])
|
||||
iterbytes = functools.partial(itertools.imap, ord)
|
||||
import StringIO
|
||||
StringIO = BytesIO = StringIO.StringIO
|
||||
_assertCountEqual = "assertItemsEqual"
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
_add_doc(b, """Byte literal""")
|
||||
_add_doc(u, """Text literal""")
|
||||
|
||||
|
||||
def assertCountEqual(self, *args, **kwargs):
|
||||
return getattr(self, _assertCountEqual)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRaisesRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
if PY3:
|
||||
exec_ = getattr(moves.builtins, "exec")
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value is None:
|
||||
value = tp()
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
else:
|
||||
def exec_(_code_, _globs_=None, _locs_=None):
|
||||
"""Execute code in a namespace."""
|
||||
if _globs_ is None:
|
||||
frame = sys._getframe(1)
|
||||
_globs_ = frame.f_globals
|
||||
if _locs_ is None:
|
||||
_locs_ = frame.f_locals
|
||||
del frame
|
||||
elif _locs_ is None:
|
||||
_locs_ = _globs_
|
||||
exec("""exec _code_ in _globs_, _locs_""")
|
||||
|
||||
exec_("""def reraise(tp, value, tb=None):
|
||||
raise tp, value, tb
|
||||
""")
|
||||
|
||||
|
||||
if sys.version_info[:2] == (3, 2):
|
||||
exec_("""def raise_from(value, from_value):
|
||||
if from_value is None:
|
||||
raise value
|
||||
raise value from from_value
|
||||
""")
|
||||
elif sys.version_info[:2] > (3, 2):
|
||||
exec_("""def raise_from(value, from_value):
|
||||
raise value from from_value
|
||||
""")
|
||||
else:
|
||||
def raise_from(value, from_value):
|
||||
raise value
|
||||
|
||||
|
||||
print_ = getattr(moves.builtins, "print", None)
|
||||
if print_ is None:
|
||||
def print_(*args, **kwargs):
|
||||
"""The new-style print function for Python 2.4 and 2.5."""
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
|
||||
def write(data):
|
||||
if not isinstance(data, basestring):
|
||||
data = str(data)
|
||||
# If the file has an encoding, encode unicode with it.
|
||||
if (isinstance(fp, file) and
|
||||
isinstance(data, unicode) and
|
||||
fp.encoding is not None):
|
||||
errors = getattr(fp, "errors", None)
|
||||
if errors is None:
|
||||
errors = "strict"
|
||||
data = data.encode(fp.encoding, errors)
|
||||
fp.write(data)
|
||||
want_unicode = False
|
||||
sep = kwargs.pop("sep", None)
|
||||
if sep is not None:
|
||||
if isinstance(sep, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(sep, str):
|
||||
raise TypeError("sep must be None or a string")
|
||||
end = kwargs.pop("end", None)
|
||||
if end is not None:
|
||||
if isinstance(end, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(end, str):
|
||||
raise TypeError("end must be None or a string")
|
||||
if kwargs:
|
||||
raise TypeError("invalid keyword arguments to print()")
|
||||
if not want_unicode:
|
||||
for arg in args:
|
||||
if isinstance(arg, unicode):
|
||||
want_unicode = True
|
||||
break
|
||||
if want_unicode:
|
||||
newline = unicode("\n")
|
||||
space = unicode(" ")
|
||||
else:
|
||||
newline = "\n"
|
||||
space = " "
|
||||
if sep is None:
|
||||
sep = space
|
||||
if end is None:
|
||||
end = newline
|
||||
for i, arg in enumerate(args):
|
||||
if i:
|
||||
write(sep)
|
||||
write(arg)
|
||||
write(end)
|
||||
if sys.version_info[:2] < (3, 3):
|
||||
_print = print_
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
fp = kwargs.get("file", sys.stdout)
|
||||
flush = kwargs.pop("flush", False)
|
||||
_print(*args, **kwargs)
|
||||
if flush and fp is not None:
|
||||
fp.flush()
|
||||
|
||||
_add_doc(reraise, """Reraise an exception.""")
|
||||
|
||||
if sys.version_info[0:2] < (3, 4):
|
||||
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||
updated=functools.WRAPPER_UPDATES):
|
||||
def wrapper(f):
|
||||
f = functools.wraps(wrapped, assigned, updated)(f)
|
||||
f.__wrapped__ = wrapped
|
||||
return f
|
||||
return wrapper
|
||||
else:
|
||||
wraps = functools.wraps
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass."""
|
||||
# This requires a bit of explanation: the basic idea is to make a dummy
|
||||
# metaclass for one level of class instantiation that replaces itself with
|
||||
# the actual metaclass.
|
||||
class metaclass(meta):
|
||||
def __new__(cls, name, this_bases, d):
|
||||
return meta(name, bases, d)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
|
||||
|
||||
def add_metaclass(metaclass):
|
||||
"""Class decorator for creating a class with a metaclass."""
|
||||
def wrapper(cls):
|
||||
orig_vars = cls.__dict__.copy()
|
||||
slots = orig_vars.get('__slots__')
|
||||
if slots is not None:
|
||||
if isinstance(slots, str):
|
||||
slots = [slots]
|
||||
for slots_var in slots:
|
||||
orig_vars.pop(slots_var)
|
||||
orig_vars.pop('__dict__', None)
|
||||
orig_vars.pop('__weakref__', None)
|
||||
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||
return wrapper
|
||||
|
||||
|
||||
def python_2_unicode_compatible(klass):
|
||||
"""
|
||||
A decorator that defines __unicode__ and __str__ methods under Python 2.
|
||||
Under Python 3 it does nothing.
|
||||
|
||||
To support Python 2 and 3 with a single code base, define a __str__ method
|
||||
returning text and apply this decorator to the class.
|
||||
"""
|
||||
if PY2:
|
||||
if '__str__' not in klass.__dict__:
|
||||
raise ValueError("@python_2_unicode_compatible cannot be applied "
|
||||
"to %s because it doesn't define __str__()." %
|
||||
klass.__name__)
|
||||
klass.__unicode__ = klass.__str__
|
||||
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
|
||||
return klass
|
||||
|
||||
|
||||
# Complete the moves implementation.
|
||||
# This code is at the end of this module to speed up module loading.
|
||||
# Turn this module into a package.
|
||||
__path__ = [] # required for PEP 302 and PEP 451
|
||||
__package__ = __name__ # see PEP 366 @ReservedAssignment
|
||||
if globals().get("__spec__") is not None:
|
||||
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
|
||||
# Remove other six meta path importers, since they cause problems. This can
|
||||
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
|
||||
# this for some reason.)
|
||||
if sys.meta_path:
|
||||
for i, importer in enumerate(sys.meta_path):
|
||||
# Here's some real nastiness: Another "instance" of the six module might
|
||||
# be floating around. Therefore, we can't use isinstance() to check for
|
||||
# the six meta path importer, since the other six instance will have
|
||||
# inserted an importer with different class.
|
||||
if (type(importer).__name__ == "_SixMetaPathImporter" and
|
||||
importer.name == __name__):
|
||||
del sys.meta_path[i]
|
||||
break
|
||||
del i, importer
|
||||
# Finally, add the importer to the meta path import hook.
|
||||
sys.meta_path.append(_importer)
|
14
venv/lib/python3.11/site-packages/pyttsx3/voice.py
Normal file
14
venv/lib/python3.11/site-packages/pyttsx3/voice.py
Normal file
@@ -0,0 +1,14 @@
|
||||
class Voice(object):
|
||||
def __init__(self, id, name=None, languages=[], gender=None, age=None):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.languages = languages
|
||||
self.gender = gender
|
||||
self.age = age
|
||||
|
||||
def __str__(self):
|
||||
return """<Voice id=%(id)s
|
||||
name=%(name)s
|
||||
languages=%(languages)s
|
||||
gender=%(gender)s
|
||||
age=%(age)s>""" % self.__dict__
|
Reference in New Issue
Block a user