1

The following python3 code is what I might have expected to generate a few calls to the doit event, followed by a call to the terminate event, which would stop the app, but only the first event fires. What am I doing wrong?

from circuits import Component, Event, Debugger
import time

times = []

class doit(Event):
    """doit Event"""

class terminate(Event):
    """terminate Event"""

class App(Component):
    def __init__(self):
        super().__init__()
        self.interval = .1
        self.last = 0
        self.count = 0

    def doit(self, origin):
        times.append(("%s from A at %.03f" % (origin, time.time())))
        self.count += 1
        self.last = time.time()

    def generate_events(self, event):
        if self.last + self.interval < time.time():
            event.stop()
            self.fire(doit('ge'))
        if self.count >= 5:
            event.stop()
            self.fire(terminate())

    def terminate(self):
        self.stop()


(Debugger() + App()).run()

print("\n".join(times))

I got the same behavior using event.reduce_time_left(0) instead of event.stop().

  • A corrected version of what you described above is: https://gist.github.com/a47368fe531880509915e5c0cd5bbdc2 -- NB: You are effectively re-implementing `circuits.Timer`. `generate_events` is an internal mechanism used to (_obviously_) "generate events" but do so in a way that doesn't consume CPU cycles "polling". This is how we implement all our I/O pollers and Timers. – James Mills Aug 07 '16 at 18:18
  • 1
    Very helpful, thank you! I will probably use Timer. I think the part I was missing was the purpose of `reduce_time_left()`. I see that any number of generators might generate some event after x time, and each should reduce the time left according to its maximum interval from the time most recently called, so that the event will occur when its next time comes up (but not before). – Jim Scarborough Aug 07 '16 at 19:00
  • Precisely :) Do you want to answer your own question then for the benefit of others? – James Mills Aug 07 '16 at 19:27

1 Answers1

1

The main error in the example is that it doesn't reduce_time_left(time.time() - self.last + self.interval) when there is nothing to do.

generate_events fires once when the app starts. Each generator needs to set reduce_time_left() to the maximum reasonable time before firing again - so that it will certainly fire again by that time - whether something is generated or not. Reducing the time to 0 indicates that this cycle is complete (and events need to be fired).

The preferred solution uses Timer to implement the time functionality, reducing this example to the logic to display how it works.

from circuits import BaseComponent, Event, Timer, Debugger, handler
import time

class doit(Event):
    """doit Event"""

class App(BaseComponent):
   timer = Timer(.1, doit('A'), persist=True)

   def __init__(self):
       super().__init__()
       self.count = 0

   @handler("doit")
   def _doit(self, origin):
       print("%s from A at %.03f" % (origin, time.time()))
       self.count += 1
       if self.count > 4:
           self.stop()

(App() + Debugger()).run()