Brad Chapman wrote: > > I started making the front talk to the middle via xml in the changes I > just committed. I've started with connections, and I made the front > send the middle the following XML: > > <front> > <connect> > <input id = 'workspace1/workspace2/container1.xml'/> > <output id = 'workspace1/workspace2/viewer2.xml'/> > </connect> > </front> > > This is just a semi-modification on what you were talking about before > (to make it valid XML so I can use DOM to work with it). I figured > that other actions can be a modification of this. That looks very nice, much better than my illegal XML hack. > Right now I'm just passing the info as a big string o' XML and > parsing it from this. That's pretty much what I had in mind. > I took a little look at the httplib library in > the python distribution, and it seems like it might be useful (at > least, there are send and reply functions to call). Maybe I'll try > playing with this, but as you said, I'm not positive how to do it > with "streaming" data. We'll see, I guess... I have attached exploop.py (an Expect for Python) and eventloop.py (needed by exploop.py). Perhaps these will be of some help in learning how to make a streaming dialog. > What kind of information do you want the middle to give to the front? > Would something like: > > <middle> > <connect_reply>Error: Loci cannot be connected</connect_reply> > </middle> > > be sufficient. Could we use a tag to identify the error, instead of a string? <middle> <connect_reply> <error type = 13> Loci cannot be connected. </error> </connect_reply> </middle> > I think we should keep the chatting to a minimum since > we have > to deal with everything we pass. So, if you send the middle a request > to connect it either does it and returns "Success: Loci connected" or > send an "Error" if it cannot. Then the front can decide what it wants > to do. How does this sound? Yeah, that sounds good. > Even though it is a mess to untangle them, it will be pretty cool > to have the middle and front finally free! Yes. You the man! Cheers. Jeff -- +----------------------------------+ | J.W. Bizzaro | | | | http://bioinformatics.org/~jeff/ | | | | BIOINFORMATICS.ORG | | The Open Lab | | | | http://bioinformatics.org/ | +----------------------------------+ -------------- next part -------------- #! /usr/bin/env python # ###### # $Id: exploop.py,v 1.5 1997/01/27 19:11:59 timo Exp $ # # Depends on eventloop.py(c), which may be found at # ftp://ftp.bbn.com/pub/timo/python/eventloop.py # ###### # Inspired by pyexpect, "A Poor Man's Expect", from Mitch Chapman # ###### """ Need to document the Expect interface here. """ import eventloop import regex import fcntl, FCNTL def _defaultCB(P,E): return "break" # end _defaultCB class Pattern: """ Need to document the Pattern object's public interface. I expect (no pun intended) that Pattern will often be subclassed so that additional information can be stored with each pattern. """ def __init__(self, regexp="", command=_defaultCB): """__init__(self, regexp="", command=<_defaultCB>) The paramenter 'regexp' is a *compiled* regular expression. The function 'command' will be called when a match is found. The default command simply returns 'break'. """ self.pattern = regexp self.command = command # end __init__ # end class Pattern ExpectError = "ExpectError" ExpectTimeout = "ExpectTimeout" ExpectEOF = "ExpectEOF" class Expect(eventloop.EventLoop): """ Need to add documentation for the Expect class. In particular, describe the public interface. """ def __init__(self, inf, outf): """__init__(self, infile, outfile) Constructor for an Expect instance. This routine automatically sets the input file for non-blocking. """ eventloop.EventLoop.__init__(self) # Set up for non-blocking I/O, as otherwise handleInput # will block until it has read a whole boatload of characters. fcntl.fcntl(inf.fileno(), FCNTL.O_NDELAY, 1) fcntl.fcntl(inf.fileno(), FCNTL.F_SETFL, FCNTL.O_NONBLOCK) self.__inf = inf self.__outf = outf self.AddFile(inf, self.handleInput, inf, None) # end of __init__ def send(self, str): """send(self, string) Send characters from string out file. """ self.__outf.write(str) self.__outf.flush() # end of send def expect(self, patterns=[], timeout=None): """expect(self, patterns=[], timeout=None) The expect method is modeled after the select call. The parameter 'patterns' is a list of Pattern instances. If a 'timeout' is specified, then expect() will wait at most 'timeout' seconds before returning, with a 'None' value. If a pattern is matched, then its command is called. If that command returns a true value, then expect() will return the matching Pattern. """ if hasattr(self, "pats"): raise ExpectError, "You're already expecting something." self.pats = patterns # Clear the rcvd buffer and set our default result. self.__rcvd = "" # Set a timer if given. Call Mainloop self.__timerID = None if timeout: self.__timerID = self.AddTimer(timeout, self.doTimeout) result = self.MainLoop() if timeout: self.DeleteTimer(self.__timerID) del self.pats, self.__timerID return result # end expect() def doTimeout(self, event=None): """doTimeout(self, event=None) Timeout has expired for current expect() call. """ del self.pats, self.__timerID raise ExpectTimeout # end doTimeout def handleInput(self, file, option): """handleInput(self, file, option) Read all available characters on input file 'file' and sequentially compare them to the current patterns. Once a match is found, handleInput removes all text from the beginning of the input string until the end of the match. Matches are checked sequentially. If a pattern matches, then we remove the appropriate text *BEFORE* checking the next pattern. Parameter 'option' is unused for now. """ str = file.read() if not str: raise ExpectEOF self.__rcvd = self.__rcvd + str # Ok, let's look for a match for i in self.pats: while 1: result = i.pattern.search(self.__rcvd) if result < 0: # no match found break matchlen = result + len(i.pattern.group(0)) + 1 result = apply(i.command, (i, self)) self.__rcvd = self.__rcvd[matchlen:] if result: self.Cancel(i) # end of while loop # end of for loop... # end handleInput() # end class Expect ###################################################################### ## # Example code ## def _testCB(pat, exp): reg = pat.pattern exp.send("\nMatch Found: " + `reg.group(0)` + "\n") # end testCB if __name__ == "__main__": import sys, os # Get current terminal settings p = os.popen("stty -g") savedterm = p.read() p.close() del p # Set terminal to (near) raw mode # It allows the terminal to handle RETURNs appropriately ## this works on my Solaris machine. YMMV os.system("stty cs8 -icanon min 1 time 0 -isig -xcase -inpck") exp = Expect(sys.stdin, sys.stdout) # Create our Patterns pats = [] pats.append( Pattern(regex.compile("[Pp]ython"), _testCB) ) pats.append( Pattern(regex.compile("s+tar"), _testCB) ) # This pattern uses the default Pattern callback pats.append( Pattern(regex.compile("[Bb]ye"))) # Output a prompt of sorts exp.send("\nYou have 20 seconds. Type 'Bye' or 'bye' to exit sooner.\n") # Call expect()... try: matcher = exp.expect(pats, 20) print "\nGood Bye" except ExpectTimeout: print "\nTime has expired" except ExpectEOF: print "\nInput closed" os.system("stty "+ savedterm) -------------- next part -------------- #!/usr/bin/env python # ##### # $Id: eventloop.py,v 1.4 1997/08/01 20:33:12 timo Exp $ # ##### import select, time class Event: """Event class A base class for different Event types.""" def __init__(self, func=None, arg=None): self.func = func self.arg = arg def __call__(self): apply(self.func, self.arg) # end class Event class FileEvent(Event): """A File Event We keep a reference to the file object. File objects have a tendency to close when deleted, so this reference prevents the user from accidentally closing the file without telling the EventLoop. """ def __init__(self, file, func, arg): Event.__init__(self, func, arg) self.file = file self.fileno = file.fileno return # end __init__ # end class FileEvent class TimerEvent(Event): """A Timer Event The only interesting thing here is the need to generate a unique-id for each timer. """ next_id = 1 last_id = 65535 in_use = [] def __init__(self, expire, func, arg): Event.__init__(self, func, arg) self.expire = expire self.id = N = TimerEvent.next_id # Update in_use array TimerEvent.in_use.append(N) # Make sure the next_id is unique if N == TimerEvent.last_id: N = 1 else: N = N + 1 while N in TimerEvent.in_use: N = N + 1 TimerEvent.next_id = N + 1 return # end __init__ def __del__(self): TimerEvent.in_use.remove(self.id) # end __del__ # end class TimerEvent LoopExit = "LoopExit" class EventLoop: """EventLoop creates a select-based mainloop. Events may be added as either timers, which 'fire' after a specified period, or as fileobjects, which "trigger" when available for reading. Once a timer fires, it is removed from the Eventloop. If you want your timer to fire every N seconds, then you must call AddTimer from your callback. (See example below.) A fileobject can be any object with a 'fileno' method. A file event must be explicitly removed via DeleteFile. In this respect, FileEvents differ from TimerEvents. """ ### ### Internally: ### ### We have one structure to keep file objects: ### __filelist == { fileno : file_object } ### ### We have one structure to keep timers: ### __timerlist == { expiretime : [timer ...] } ### def __init__(self): self.__filelist = {} self.__timerlist = {} return # end __init__ def Cancel(self, val=None): raise LoopExit, val def MainLoop(self): sf = self.__filelist st = self.__timerlist try: # while-loop implements the mainloop while 1: delay = None # Indicates no timers if st: when = min( st.keys() ) # Next timer now = time.time() # Current time if now >= when: self.FireTimers(when) # Fire any expired timers continue # else delay = when - now # Delay until next timer # Although undocumented, # delay==None results in a blocking select() call. r,w,e = select.select( sf.values(), [], [], delay) # For-Loop handles any file activity for File in r: File() # end while loop except LoopExit, val: return val # should never reach here return # end MainLoop def AddFile(self, obj, callback, *arg): sf = self.__filelist f_no = obj.fileno() # if sf.has_key(f_no): # raise ValueError, "FileNo already used!" sf[f_no] = FileEvent(obj, callback, arg) return # end AddFile def DeleteFile(self, obj): sf = self.__filelist f_no = obj.fileno() del sf[f_no] return # end DeleteFile def AddTimer(self, seconds, callback, *arg): st = self.__timerlist when = time.time() + seconds tobj = TimerEvent(when, callback, arg) if st.has_key(when): st[when].append(tobj) else: st[when] = [tobj] return tobj.id # end AddTimer def DeleteTimer(self, identifier): st = self.__timerlist for T in st.values(): for Timer in T: if Timer.id == identifier: when = Timer.expire T.remove(Timer) if st[when] == []: del st[when] return # We didn't find it raise ValueError, "No timer with identifier" + `identifier` # end DeleteTimer def FireTimers(self, which): st = self.__timerlist TL = st[which] del st[which] for Timer in TL: Timer() return # end FireTimer # end class EventLoop def _timertest(endtime): global Ev t_minus = endtime - time.time() if t_minus <= 0: print "Boom!!!" Ev.Cancel() print print "You have", `int(t_minus) +1`, "seconds to comply." print Ev.AddTimer(10, _timertest, end) # end _timertest def _filetest(str, endtime): global Ev ans = sys.stdin.readline() if ans[:-1] == str: print "Hooray!" Ev.Cancel() print "Wrong! Try again." # end _filetest if __name__ == "__main__": import sys global Ev phrase = "Python is my favorite language" Ev = EventLoop() print "Please type:" print " ", phrase end = time.time() + 30 Ev.AddFile(sys.stdin, _filetest, phrase, end ) Ev.AddTimer(0, _timertest, end) Ev.MainLoop()