[Pipet Devel] J.W. Bizzaro; TODO 20000218

J.W. Bizzaro bizzaro at geoserve.net
Thu Feb 24 10:46:02 EST 2000


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()


More information about the Pipet-Devel mailing list