Yield inside try...finally

Michael Sparks michaels at rd.bbc.co.uk
Tue Jun 3 18:41:05 CEST 2003


I've been working with wrapping up network client functionality as a
generator recently, and hit a "niceness"/"cleanness" speedbump.

What I'd *like* to do (which doesn't work) is:

try:
   sock = socket(AF_INET, SOCK_STREAM);
   try:
      sock.setblocking(0); yield stage.done(0)
      try:
         sock.connect((self.host, self.port)); yield stage.done(1)
         yield newComponent([self.setupCSA(sock)])
         while self.waitCSAClose():
            yield stage.current(2)
         yield stage.done(2)
      finally:
         result = s.shutdown(1) ; yield stage.done(3)
   finally:
      sock.close() ; yield stage.done(4)
except error, e:
   # Would handle error more interestingly here
   print stage.lastdone, e

Where stage is just something to track where we got to.

The above code of course doesn't work, but is the tidiest/nicest I've
got so far. The reason I have this format is due to the various socket
calls can all raise error exceptions which overlap, but should have
various things happen to close the connection etc to be nice.

Two alternatives I've tried are:

def runClient():
   try:
      sock = socket(AF_INET, SOCK_STREAM) ; yield 1
      try:
         sock.setblocking(0) ; yield 2
         sock.connect((self.host, self.port)) ; yield 3
         yield newComponent([self.setupCSA(sock)])
         while self.waitCSAClose():
            yield 4
         try:
            result = s.shutdown(1) ; yield 5
         except error, e:
            failpoint = "shutdown",5
      except error,e
         failpoint = "connect", 3
      try:
         sock.close() ; yield 6
      except error, e
         failpoint = "close", 6
   except error, e:
      failpoint = "creation", 1
   if failpoint:
      raise "Client Error", failpoint, e


And..
   def runClient(self,sock=None,restart=0):
      stage = stagedFunc(initialStage=restart)
      try:
         if stage.do(0):
            sock = socket(AF_INET, SOCK_STREAM)
            sock.setblocking(0); yield stage.done()

         if stage.do(1):
            sock.connect((self.host, self.port)); yield stage.done()
            yield newComponent([self.setupCSA(sock)])
            while self.waitCSAClose():
               yield stage.done()

         if stage.do(2):
            result = s.shutdown(1) ; yield stage.done()

         if stage.do(3):
            sock.close() ; yield stage.done()

      except error, e:
         if sock: # Failed stage 1 or 2
            for v in self.runClient(sock=sock,restart=3):  yield v
         raise "Client Socket Failed",(e,self.stage.lastattempted())

Where...

class stagedFunc:
   def __init__(self,initialStage=0):
      self.nextstage = initialStage
   def do(self,thisstage):
      if self.nextstage==thisstage:
          self.nextstage +=1
          return True
      return False
   def done(self):
      return self.nextstage-1
   lastattempted=done

Given these 3 versions, the one not allowed (the try..finally) one
strikes me as the cleanest.

I'm posting this really for a couple of reasons - a) to note that
try..yield..finally would occasionally be a really useful thing
to have (*) b) Any comments on which of the two alternatives is "better"
for whatever reasons would be appreciated. (I _am_ aware of Twisted
BTW, this is part of a deliberate wheel reinvention :)

(*) I've read Guido's comments in
    http://mail.python.org/pipermail/python-dev/2001-June/015500.html
    but these appear to mainly relate to the "def" keyword, and the
    yield issue is in passing.

Any thoughts much appreciated!

Regards,


Michael 
-- 
Michael.Sparks at rd.bbc.co.uk    
British Broadcasting Corporation, Research and Development
Kingswood Warren, Surrey KT20 6NP

This message (and any attachments) may contain personal views
which are not the views of the BBC unless specifically stated.






More information about the Python-list mailing list