#!/usr/bin/python # # psyche.py - reads in std input, execute a command, provide feedback # # # 2003.11.09, Steve Petrovits, Rick Wang import os, re, sys, time, ConfigParser def get_psychepath(): psychepath = sys.argv[0] s = psychepath.split('/') s = s[len(s)-1] return psychepath.replace(s,'') class FestivalWrapper: """ Nothing more than a wrapper around a pipe to festival. """ def __init__( self, program_name ): """ Store the command that is going to be the receiving end of pipe. """ sys.stderr.write('initializing %s\n'%program_name) self.__pipe = program_name def __call__( self, arg ): """ Open the pipe, write to it, and close the pipe. This is necessary because festival is retarded and needs a a closed pipe to be happy. """ f = os.popen(self.__pipe, 'w') f.write( arg+'\n' ) f.close() class SphinxWrapper: """ Nothing more than a wrapper to sphinx. """ def __init__( self, program_name ): """ Open the pipe and read until sphinx is ready """ self.__ready = re.compile(r"""(\[silence\])* (Listening)* (READY)* (\[initializing\])* """, re.VERBOSE) sys.stderr.write('initializing %s, this may take awhile\n'%program_name) self.__pipe = os.popen(program_name, 'r') while self.__ready.search(self.getline(quiet=1)).group() == '': continue def getline( self, quiet=0 ): """ Return the next line sitting in the pipe. input = self.__src.readline() if input != '' and quiet==0: sys.stderr.write('SphinxWrapper.getline(): %s\n'%input) return input """ return self.__pipe.readline() def die( self ): """ close the pipe """ pass #self.__pipe.close() class PsycheOff( Exception ): pass class Psyche: def __init__( self ): """ Set up the listening process. Set proper paths. self.line is a regex to parse the sphinx output. self.build_dictionary() reads all the settings. self.festival is the output target. """ self.running = 1 self.line = re.compile(r""" (?:(?:(?:.*:[ ])(?:\[.*\][ ])*)+) # num: or []s (.*) # what we want (?:\[.*\])* # trailing []s """, re.VERBOSE) self.greeting = 'psyche is ready for input' self.build_dictionary() def load_festival( self ): self.festival = FestivalWrapper(self.settings['festival']) def load_sphinx( self ): self.sphinx = SphinxWrapper(self.settings['sphinx']) def __call__( self, cur_line ): """ Receive and process piped input. """ cur_cmd = self.parse_line(cur_line) if cur_cmd == 'none': pass elif cur_cmd in ['shutdown', 'quit', '[exit]']: self.festival('psyche is shutting down') raise PsycheOff else: if cur_cmd in self.scripted: sys.stderr.write( '"%s" was understood!\n'%cur_cmd.strip()) x = os.popen(self.scripted[cur_cmd]).read() x = x+'\n' elif cur_cmd in self.builtins: sys.stderr.write( '"%s" was understood!\n'%cur_cmd.strip()) x = self.eval_builtin( self.builtins[cur_cmd] ) else: sys.stderr.write( 'await_input: invalid command received: %s\n'% cur_cmd.strip()) x = '' sys.stderr.write('%s\n'%x) # terminal echo self.festival(x) # pipe to festival def await_input( self ): """ Grab an input line from sphinx, and pipe command to __call__ Repeat until QUIT command is received from sphinx. """ self.festival(self.greeting) cur_cmd = '' while 1: cur_cmd = self.sphinx.getline() if cur_cmd not in ['', '\n']: try: self.__call__( cur_cmd ) except PsycheOff: sys.stderr.write('await_input: halting\n') self.sphinx.die() break def eval_builtin( self, args ): """ Import the appropriate module, create an instant of the appropriate object, and invoke the get_result() method of that object to return a festival-ready string. """ args = args.split() postproc = getattr(__import__(args[0]), args[0])(args[1:]) return postproc.get_result() def parse_line( self, input_line ): """ string <- Psyche.parse_line( string ) Parse a nnn: COMMAND formatted line. If there are no properly formatted strings, return none """ try: return self.line.findall(input_line)[0].strip().lower() except IndexError: return 'none' def build_dictionary( self ): """ Build system dictionaries: settings, builtin and scripted Set the proper paths """ self.settings = {} self.builtins = {} self.scripted = {} ppath = get_psychepath() sys.path += [ppath+'modules', ppath+'postprocess'] os.environ['PATH'] += ':%spostprocess/scripts'%ppath cfg = ConfigParser.ConfigParser() if os.path.exists('~/.psycherc'): cfg.read('~/.psycherc') else: cfg.read('%spsycherc'%get_psychepath()) for section in cfg.sections(): if section == "settings": for option in cfg.options(section): self.settings[option] = (cfg.get(section, option)) if section == "builtins": for option in cfg.options(section): self.builtins[option] = (cfg.get(section, option)) if section == "scripted": for option in cfg.options(section): self.scripted[option] = (cfg.get(section, option)) try: v = self.settings['verbose'] except KeyError: v = 1 if v == 1: print '\nSettings Read:' for key in self.settings.keys(): print '%20s - %50s'%(key, self.settings[key]) print '\nBuiltin Commands:' for key in self.builtins.keys(): print '%20s - %50s'%(key, self.builtins[key]) print '\nScripted Commands:' for key in self.scripted.keys(): print '%20s - %50s'%(key, self.scripted[key]) #################################### if __name__ == '__main__': psy_instance = Psyche() psy_instance.load_festival() psy_instance.load_sphinx() psy_instance.await_input() os.system('pkill sphinx2')