1

In my project I use a Worker to do some event related file processing and avoid blocking the handling of other events. What I need is to be able to put the event (that requires the file processing) in stand by (no further handling by other Components) while the Worker finishes and, then, resume the handling sequence.

I can't issue a new event when the Worker finishes because that may cause other Components to re-process the "same" event more than once.

Is there any way to delay (i.e. suspend and resume) the event propagation to accomplish what I need? Is there a better way to solve my use case?

Additional note: I can't avoid the blocking behavior because I need to use some external (blocking) library calls.

EDIT: Source code example:

from time import sleep
from circuits import Component, Debugger, handler, Event, Worker, task


class my_event(Event):
    pass


def heavy_task():
    print "heavy task"
    sleep(3)


class NextHandler(Component):
    @handler("my_event", priority=10)
    def my_event(self, event):
        print "Handler 2"


class Handler(Component):
    _worker = Worker()

    @handler("my_event", priority=20)
    def my_event(self, event):
        self.fire(task(heavy_task), self._worker)
        print "Handler 1"
        # how do I delay "event" until "heavy_task" is completed?


class App(Component):
    h1 = Handler()
    h2 = NextHandler()

    def started(self, component):
        print "Running"
        self.fire(my_event())


if __name__ == '__main__':
    (App() + Debugger()).run()

In this case I want to delay "Handler" event so "NextHandler" don't receive it until "heavy_task" has finished working.

Matias SM
  • 364
  • 3
  • 7

1 Answers1

1

Based on your updated question and edit; this is how you would "wait" for an event's completion:

Example:

from time import sleep
from circuits import Component, Debugger, handler, Event, Worker, task


class my_event(Event):
    pass


def heavy_task():
    print "heavy task"
    sleep(3)


class NextHandler(Component):
    @handler("my_event", priority=10)
    def my_event(self, event):
        print "Handler 2"


class Handler(Component):
    _worker = Worker(process=True)

    @handler("my_event", priority=20)
    def my_event(self, event):
        # Fire and Wait for: task()
        # This happens asynchornously under the covers
        # but gives you a synchronous API. Effectively
        # turning my_event into a coroutine.
        yield self.call(task(heavy_task), self._worker)

        # This will only print after task() is complete.
        print "Handler 1"


class App(Component):
    h1 = Handler()
    h2 = NextHandler()

    def started(self, component):
        print "Running"
        self.fire(my_event())


if __name__ == '__main__':
    (App() + Debugger()).run()

Output:

$ python app.py 
<registered[worker] (<Worker/worker 5848:MainThread (queued=0) [S]>, <Handler/* 5848:MainThread (queued=0) [S]> )>
<registered[*] (<Handler/* 5848:MainThread (queued=0) [S]>, <App/* 5848:MainThread (queued=4) [R]> )>
<registered[*] (<NextHandler/* 5848:MainThread (queued=0) [S]>, <App/* 5848:MainThread (queued=3) [R]> )>
<registered[*] (<Debugger/* 5848:MainThread (queued=0) [S]>, <App/* 5848:MainThread (queued=2) [R]> )>
<started[*] (<App/* 5848:MainThread (queued=1) [R]> )>
Running
<my_event[*] ( )>
Handler 2
<task[<Worker/worker 5848:MainThread (queued=0) [S]>] (<function heavy_task at 0x7f359df6ade8> )>
heavy task
<task_done[<Worker/worker 5848:MainThread (queued=0) [S]>] (None )>
<task_success[<Worker/worker 5848:MainThread (queued=0) [S]>] (<task[<Worker/worker 5848:MainThread (queued=0) [S]>] (<function heavy_task at 0x7f359df6ade8> )>, None )>
Handler 1

See the comments above.

James Mills
  • 18,669
  • 3
  • 49
  • 62
  • Thank you for your answer (great framwork BTW ;-) ). I added some code to clarify my question because I think I wasn't clear enough. – Matias SM Aug 07 '15 at 22:41
  • I think [callEvent](http://circuits.readthedocs.org/en/latest/api/circuits.core.manager.html#circuits.core.manager.Manager.callEvent) (or the `yield` option) may be what I'm looking for, but I'm not sure how to use it :( (to accomplish what I need) – Matias SM Aug 07 '15 at 22:52
  • So ``.call()`` and ``.wait()`` basically turn the event handler where you use this into a "coroutine" which gets scheduled by the top-level "manager/scheduler". You want to delay further propagation until when? That's why I suggested you wait for ``task_success`` or ``task_complete``. – James Mills Aug 08 '15 at 00:07
  • I'm not sure I'm following. Do you mean something like this? http://pastebin.com/PYri44Qe – Matias SM Aug 08 '15 at 17:06
  • @MatiasSM See update response above; hopefully this is what you're after; there is another way to do this of course and that is to coordinate your event flows manually instead of relying on "coroutines". – James Mills Aug 10 '15 at 00:41
  • Thank you for your answer. However, (maybe not clear enough in the question) what I'm looking for is a way of stopping **NextHandler** from being called before task is completed. That would be equivalent of executing the task synchronously but, would have the benefit of not blocking any _other_ event for processing. Hope my requirement is clearer now! (I may update the question if not clear enough) – Matias SM Aug 10 '15 at 22:36
  • Ahh I see; I think this comment is fine :) -- But basically there is no real "auto-magical" way to do this aside from by hand (*by orchestrating the event flow yourself*) -- because event handlers don't know about each other -- hence the "loose coupling". You would have to call ``event.stop()`` in ``Handler.my_event()``'s event handler then after the ``yield self.call(...)`` finishes re-fire the event (*untested*). – James Mills Aug 11 '15 at 01:21
  • As an aside; if you find yourself needing this particular use-case more often and you find a way to generalize it without breaking the architecture, [File an issue](https://github.com/circuits/circuits/issues/new) :) Please! – James Mills Aug 11 '15 at 01:22
  • Great, thank you for the feedback. Sadly, re-firing the event wouldn't solve my issue because then other previous handlers would re-process the event (that is not intended). Currently I "solved" it by chaining my event handlers in a sequence of handler logics (thus creating a kindof long handler logic separated in different components). This of course has its own set of difficulties, but was good enough for me – Matias SM Aug 11 '15 at 22:58
  • Cool! Often "good enough" or "worse is better" is wel better :) Please don't hesitate to file issues, bug reports or even pull requests or other questions down the track! – James Mills Aug 12 '15 at 03:41