# # # add_file "genproxy.py" # content [408f46a3f5fe0d792eb62e92a8faaf5c28c67a54] # # patch "common.py" # from [3f13b48c9393d3d7b2cf98e6c41fb750f6980f12] # to [6b2e20c94f126e5c42ff25f9c953c96883d5d87f] # # patch "mtn.py" # from [5974c2d53154febf368c5d2435dd84c6e23d819a] # to [129ee303fd073267a567dcf79c20517f3590c856] # # set "genproxy.py" # attr "mtn:execute" # value "true" # ============================================================ --- genproxy.py 408f46a3f5fe0d792eb62e92a8faaf5c28c67a54 +++ genproxy.py 408f46a3f5fe0d792eb62e92a8faaf5c28c67a54 @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +class GeneratorProxy(object): + def __init__(self, generator): + self.generator = generator + def __iter__(self): + return self + def next(self): + return self.generator.next() + +class Seedy(GeneratorProxy): + def __del__(self): + print "testing" + +def test(): + yield 2 + yield 3 + yield 4 + +if __name__ == '__main__': + a = test() + b = Seedy(test()) + for i in b: + print i + ============================================================ --- common.py 3f13b48c9393d3d7b2cf98e6c41fb750f6980f12 +++ common.py 6b2e20c94f126e5c42ff25f9c953c96883d5d87f @@ -1,9 +1,10 @@ from web import debug import datetime import time import fcntl import os from web import debug +import traceback def parse_timecert(value): return apply(datetime.datetime, time.strptime(value, "%Y-%m-%dT%H:%M:%S")[:6]) @@ -23,7 +24,7 @@ def terminate_popen3(process): os.kill(process.pid, signal.SIGKILL) process.wait() except: - debug("%s failed_to_stop %s" % (os.getpid(), process.pid)) + debug("%s failed_to_stop %s (%s)" % (os.getpid(), process.pid, traceback.format_exc())) def ago(event): def plural(v, singular, plural): ============================================================ --- mtn.py 5974c2d53154febf368c5d2435dd84c6e23d819a +++ mtn.py 129ee303fd073267a567dcf79c20517f3590c856 @@ -8,6 +8,7 @@ from traceback import format_exc import popen2 from common import set_nonblocking, terminate_popen3 from traceback import format_exc +import genproxy import web from web import debug @@ -60,6 +61,7 @@ class Automate(Runner): """ def __init__(self, *args, **kwargs): Runner.__init__(*[self] + list(args), **kwargs) + debug("** a new automate instance just started **") self.lock = threading.Lock() self.process = None @@ -80,14 +82,44 @@ class Automate(Runner): def run(self, *args, **kwargs): # debug(("automate is running:", args, kwargs)) - if not self.lock.acquire(False): - raise MonotoneException("Automate process can't be called: it is already locked.") - try: - rv = apply(self.__run, args, kwargs) - finally: - self.lock.release() - return rv + lock = self.lock + stop = self.stop + class CleanRequest(genproxy.GeneratorProxy): + def __init__(self, *args, **kwargs): + genproxy.GeneratorProxy.__init__(self, *args, **kwargs) + + # nb; this used to be False, but True seems to behave more sensibly. + # in particular, if someone holds down Refresh sometimes the code + # gets here before __del__ is called on the previous iterator, + # causing a pointless error to occur + if not lock.acquire(True): + # I've checked; this exception does _not_ cause __del__ to run, so + # we don't accidentally unlock a lock below + raise MonotoneException("Automate request cannot be called: it is already locked! This indicates a logic error in ViewMTN; please report.") + + def __del__(self): + def read_any_unread_output(): + try: + # this'll raise StopIteration if we're done + self.next() + # okay, we're not done.. + debug("warning: Automate output not completely read; reading manually.") + for stanza in self: + pass + except StopIteration: + debug("got a StopIteration ok.") + pass + + try: + read_any_unread_output() + lock.release() + except: + debug("exception cleaning up after Automation; calling stop()!") + stop() + + return CleanRequest(self.__run(*args, **kwargs)) + def __run(self, command, args): enc = "l%d:%s" % (len(command), command) enc += ''.join(map(lambda x: "%d:%s" % (len(x), x), args)) + 'e' @@ -303,9 +335,13 @@ class Operations: class Operations: def __init__(self, runner_args): + debug ("** a new Operations just started **") self.standalone = apply(Standalone, runner_args) self.automate = apply(Automate, runner_args) + def __del__(self): + debug ( "** DESTROY operations") + def tags(self): for stanza in basic_io_from_stream(self.automate.run('tags', [])): if stanza[0] == 'tag':