コード例 #1
0
class DerivedValue(Readable):
    """'DerivedValue(formula, *values)' - a value derived from other values

    Usage::

        # 'derived' changes whenever x or y change
        derived = DerivedValue(lambda: x()+y(), x, y)

    A 'DerivedValue' fires an event equal to 'formula()' whenever any of the
    supplied 'values' fire, and the value of 'formula()' is not equal to its
    last known value (if any).
    """

    __slots__ = '_formula'

    protocols.advise(instancesProvide=[IValue])

    def __init__(self, formula, *values):
        self._formula = formula
        super(DerivedValue, self).__init__()
        subscribe(AnyOf(*[adapt(v, IValue) for v in values]), self._set)  #

    def __call__(self):
        """Get current value of 'formula()'"""
        return self._formula()

    def _set(self, source, event):
        value = self._formula()
        if not hasattr(self, '_value') or self._value <> value:
            self._value = value
            self._fire(value)
コード例 #2
0
class NullClass(object):
    """Null condition: never fires, never has a value other than None"""

    __slots__ = ()

    protocols.advise(instancesProvide=[IConditional, IValue])

    value = property(lambda self: self)

    def __call__(self):
        pass

    def __repr__(self):
        return 'events.Null'

    def conjuncts(self):
        return ()

    def disjuncts(self):
        return ()

    def __invert__(self):
        return self

    def __and__(self, other):
        return other

    def __or__(self, other):
        return other

    def addCallback(self, cb):
        return lambda: None  # we'll never fire, and so can't call back

    def nextAction(self, task=None, state=None):
        pass  # always suspend
コード例 #3
0
class Value(Writable):
    """Broadcast changes in a variable to all observers

    'events.Value()' instances implement a near-trivial version of the
    'Observer' pattern: callbacks can be informed that a change has been
    made to the 'Value'.  Example::

        aValue = events.Value(42)
        assert aValue()==42
        aValue.set(78)  # fires any registered callbacks
        aValue.set(78)  # doesn't fire, value hasn't changed
        aValue.set(78,force=True)   # force firing even though value's the same

    Events are broadcast to all callbacks, whether they "accept" or "reject"
    the event, and tasks yielding to a 'Value' are suspended until the next
    event is broadcast.  The current value of the 'Value' is supplied to
    callbacks and tasks via the 'event' parameter, and the 'Value' itself
    is supplied as the 'source'.  (See 'events.IEventSink'.)
    """

    __slots__ = ()

    protocols.advise(instancesProvide=[IValue])

    defaultValue = NOT_GIVEN

    def __init__(self, value=NOT_GIVEN):
        if value is NOT_GIVEN:
            value = self.defaultValue
        self._value = value
        super(Value, self).__init__()

    isTrue = property(lambda self: adapt(self, IConditional))

    isFalse = property(lambda self: ~adapt(self, IConditional))
コード例 #4
0
class SignalEvents(binding.Singleton):
    """Global signal manager"""

    protocols.advise(classProvides=[ISignalSource])

    _events = {None: sources.Broadcaster()}  # Null signal

    def signals(self, *signames):
        """'IEventSource' that triggers whenever any of named signals occur"""

        if len(signames) == 1:
            signum = signals.get(signames[0])
            try:
                return self._events[signum]
            except KeyError:
                e = self._events[signum] = SignalEvent(signum)
                return e
        else:
            return sources.AnyOf(*[self.signals(n) for n in signames])

    def haveSignal(self, signame):
        """Return true if signal named 'signame' exists"""
        return signame in signals

    def __call__(self, *args):
        return self
コード例 #5
0
class _STask(Task):
    """'events.Task' that handles errors better, by relying on a scheduler"""

    __slots__ = 'scheduler', 'aborted'

    protocols.advise(instancesProvide=[IScheduledTask])

    def __init__(self, iterator, scheduler):
        self.scheduler = scheduler
        self.aborted = Condition()
        Task.__init__(self, iterator)

    def _start(self):
        super(_STask, self).step()

    def _uncaughtError(self):
        condition = self.aborted
        try:
            try:
                raise
            except SystemExit, v:
                condition = self.isFinished
                return True
        finally:
            self.scheduler._callAt(lambda e, s: condition.set(True),
                                   self.scheduler.now())

    def step(self, source=None, event=NOT_GIVEN):
        """See 'events.IScheduledTask.step'"""
        self.scheduler._callAt(
            lambda e, s: super(_STask, self).step(source, event),
            self.scheduler.now())
コード例 #6
0
class TaskState(object):
    """Tracks the state of a task; see 'events.ITaskState' for details"""

    __slots__ = 'lastEvent', 'handlingError', 'stack'

    protocols.advise(instancesProvide=[ITaskState])

    def __init__(self):
        self.lastEvent = NOT_GIVEN
        self.handlingError = False
        self.stack = []

    def CALL(self, iterator):
        self.stack.append(adapt(iterator, IProcedure))

    def YIELD(self, value):
        self.lastEvent = value

    def RETURN(self):
        self.stack.pop()

    def THROW(self):
        self.handlingError = True
        self.lastEvent = NOT_GIVEN

    def CATCH(self):
        self.handlingError = False
コード例 #7
0
class ReadableAsCondition(AbstractConditional):
    """Wrap an 'IReadableSource' as an 'IConditional'"""

    __slots__ = 'value'

    protocols.advise(instancesProvide=[IConditional],
                     asAdapterForProtocols=[IValue])

    singleFire = False

    def __init__(self, subject):
        self.value = subject
        super(ReadableAsCondition, self).__init__()
        subscribe(subject, self._set)
        self.cmpval = self.__class__, subject

    def __call__(self):
        return self.value()

    def derive(self, func):
        return self.value.derive(func)

    def _set(self, src, evt):
        if evt:
            self._fire(evt)
コード例 #8
0
class Semaphore(Condition):
    """Allow up to 'n' tasks to proceed simultaneously

    A 'Semaphore' is like a 'Condition', except that it does not broadcast
    its events.  Each event is supplied to callbacks only until one "accepts"
    the event (by returning a true value).

    'Semaphore' instances also have 'put()' and 'take()' methods that
    respectively increase or decrease their value by 1.

    Note that 'Semaphore' does not automatically decrease its count due to
    a callback or task resumption.  You must explicitly 'take()' the
    semaphore in your task or callback to reduce its count.
    """

    __slots__ = ()

    protocols.advise(instancesProvide=[ISemaphore])

    singleFire = True
    defaultValue = 0

    def put(self):
        """See 'events.ICondition.put()'"""
        self.set(self() + 1)

    def take(self):
        """See 'events.ICondition.take()'"""
        self.set(self() - 1)
コード例 #9
0
class WritableAsCondition(ReadableAsCondition):
    """Wrap an 'IWritableValue' as an 'IConditional'"""

    protocols.advise(instancesProvide=[IWritableSource, IConditional],
                     asAdapterForProtocols=[IWritableValue])

    def set(self, value, force=False):
        self.value.set(value, force)
コード例 #10
0
class Scheduler(object):
    """Time-based event sources; see 'events.IScheduler' for interface"""

    protocols.advise(instancesProvide=[IScheduler])

    def __init__(self, time=time.time):
        self.now = time
        self._appointments = []
        self.isEmpty = Condition(True)

    def time_available(self):
        if self._appointments:
            return max(0, self._appointments[0][0] - self.now())

    def tick(self, stop=None):
        now = self.now()
        while self._appointments and self._appointments[0][
                0] <= now and not stop:
            self._appointments.pop(0)[1](self, now)
        self.isEmpty.set(not self._appointments)

    def sleep(self, secs=0):
        return _Sleeper(self, secs)

    def until(self, time):
        c = Condition()
        if time <= self.now():
            c.set(True)
        else:
            self._callAt(lambda s, e: c.set(True), time)
        return c

    def timeout(self, secs):
        return self.until(self.now() + secs)

    def spawn(self, iterator):
        return _STask(iterator, self)

    def alarm(self, iterator, timeout, errorType=TimeoutError):
        return Interrupt(iterator, self.timeout(timeout), errorType)

    def _callAt(self, what, when):
        self.isEmpty.set(False)
        appts = self._appointments
        item = (when, what)
        lo = 0
        hi = len(appts)

        while lo < hi:
            mid = (lo + hi) // 2
            if when < appts[mid][0]:
                hi = mid
            else:
                lo = mid + 1

        appts.insert(lo, item)
        return lambda: (item in appts) and appts.remove(item)
コード例 #11
0
class ProcedureAsTaskSwitch(protocols.Adapter):

    protocols.advise(instancesProvide=[ITaskSwitch],
                     asAdapterForProtocols=[IProcedure])

    def nextAction(self, task=None, state=None):
        if state is not None:
            state.CALL(self.subject)
        return True
コード例 #12
0
class Writable(Readable):
    """Base class for an 'IWritableSource' -- adds a 'set()' method"""

    __slots__ = ()

    protocols.advise(instancesProvide=[IWritableSource])

    def set(self, value, force=False):
        """See 'events.IWritableSource.set()'"""
        if force or value <> self._value:
            self._value = value
            self._fire(value)
コード例 #13
0
class Task(Observable):
    """Thread-like "task" that pauses and resumes in response to events

    Usage::

        def aGenerator(someArg):
            yield untilSomeCondition; events.resume()
            # ... do something ...

        events.Task(aGenerator(someValue))    # create a task

    When created, tasks run until the first 'yield' operation yielding an
    'ITaskSwitch' results in the task being suspended.  The task will
    then be resumed when the waited-on event fires its callbacks, and so on.

    Tasks offer an 'isFinished' 'ICondition' that can be waited on, or checked
    to find out whether the task has successfully completed.  See
    'events.ITask' for more details."""

    __slots__ = 'isFinished', '_state'

    protocols.advise(
        instancesProvide=[ITask],
        asAdapterForProtocols=[IProcedure],
        factoryMethod='fromProcedure',
    )

    singleFire = False  # Broadcast results to listeners
    overrunOK = False  # Results are not bufferable

    def __init__(self, iterator):
        Observable.__init__(self)
        self.isFinished = Condition()
        self._state = TaskState()
        self._state.CALL(iterator)
        self._start()

    def fromProcedure(klass, ob):
        return klass(ob)

    fromProcedure = classmethod(fromProcedure)

    def _start(self):
        """Hook for subclasses to start the task"""
        self.step()

    def _uncaughtError(self):
        """Hook for subclasses to catch exceptions that escape the task"""
        try:
            raise
        except SystemExit, v:
            self.isFinished.set(True)
            return True
コード例 #14
0
class Readable(Observable):
    """Base class for an 'IReadableSource' -- adds a '_value' and '__call__'"""

    __slots__ = '_value'

    singleFire = False

    protocols.advise(instancesProvide=[IReadableSource])

    def __call__(self):
        """See 'events.IReadableSource.__call__()'"""
        return self._value

    def derive(self, func):
        return DerivedValue(lambda: func(self()), self)
コード例 #15
0
class _Sleeper(object):

    protocols.advise(instancesProvide=[IEventSource])

    def __init__(self, scheduler, delay):
        self.scheduler = scheduler
        self.delay = delay

    def nextAction(self, task=None, state=None):
        if task is not None:
            self.addCallback(task.step)

    def addCallback(self, func):
        return self.scheduler._callAt(lambda s, e: func(self, e),
                                      self.scheduler.now() + self.delay)
コード例 #16
0
class EventLoop(binding.Component):
    """All-in-one event source and loop runner"""

    protocols.advise(instancesProvide=[IEventLoop])

    sigsrc = binding.Obtain(ISignalSource)
    haveSignal = signals = binding.Delegate('sigsrc')

    scheduler = binding.Obtain(IScheduler)
    spawn = now = tick = sleep = until = timeout = time_available \
        = binding.Delegate('scheduler')

    selector = binding.Obtain(ISelector)
    readable = writable = exceptional = binding.Delegate('selector')

    log = binding.Obtain('logger:peak.events.loop')

    def runUntil(self, eventSource, suppressErrors=False, idle=sleep):

        running = [True]
        exit = []
        tick = self.tick
        time_available = self.time_available

        adapt(eventSource, IEventSource).addCallback(
            lambda s, e: [running.pop(), exit.append(e)])

        if suppressErrors:

            def tick(exit, doTick=tick):
                try:
                    doTick(exit)
                except:
                    self.log.exception("Unexpected error in event loop:")

        while running:
            tick(exit)
            if running:
                delay = time_available()
                if delay is None:
                    raise StopIteration("Nothing scheduled to execute")
                if delay > 0:
                    idle(delay)

        return exit.pop()
コード例 #17
0
class AnyOf(object):
    """Union of multiple event sources

    Example usage::

        timedOut = scheduler.timeout(30)
        untilSomethingHappens = events.AnyOf(stream.dataRcvd, timedOut)

        while not timedOut():
            yield untilSomethingHappens; src,evt = events.resume()
            if src is stream.dataRcvd:
                data = event
                print data

    'AnyOf' fires callbacks whenever any of its actual event sources fire (and
    allows tasks to continue if any of its actual event sources allow it).
    The 'event' it supplies to its user is actually a '(source,event)' tuple
    as shown above, so you can distinguish which of the actual event sources
    fired, as well as receive the event from it.

    Note that callbacks registered with an 'AnyOf' instance will fire at most
    once, even if more than one of the original event sources fires.  Thus,
    you should not assume in a callback or task that the event you received
    is the only one that has occurred.  This is especially true in scheduled
    tasks, where many things may happen between the triggering of an event
    and the resumption of a task that was waiting for the event.
    """

    __slots__ = '_sources'

    protocols.advise(instancesProvide=[IEventSource])

    def __new__(klass, *sources):

        if sources:

            # Flatten sources
            srclist = []
            for src in adapt(sources, protocols.sequenceOf(IEventSource)):
                if isinstance(src, AnyOf):
                    srclist.extend(src._sources)
                else:
                    srclist.append(src)

            # Eliminate duplicates
            srcids = {}
            sources = tuple([
                src for src in srclist
                if src not in srcids and srcids.setdefault(src, True)
            ])

            # Return the result
            if len(sources) == 1:
                return adapt(sources[0], IEventSource)
            else:
                self = object.__new__(klass)
                self._sources = sources
                return self

        raise ValueError, "AnyOf must be called with one or more IEventSources"

    def nextAction(self, task=None, state=None):
        """See 'events.ITaskSwitch.nextAction()'"""

        for source in self._sources:
            action = source.nextAction()

            if action:
                flag = source.nextAction(task, state)

                if state is not None:
                    state.YIELD((source, state.lastEvent))
                return flag

        if task is not None:
            self.addCallback(task.step)

    def addCallback(self, func):
        """See 'events.IEventSource.addCallback()'"""

        cancels = []

        def onceOnly(source, event):
            if cancels:
                while cancels:
                    cancels.pop()()
                return func(self, (source, event))

        cancels = [source.addCallback(onceOnly) for source in self._sources]
        return lambda: [(c(), cancels.remove(c)) for c in cancels]
コード例 #18
0
class Selector(binding.Component):
    """Simple task-based implementation of an ISelector"""

    protocols.advise(instancesProvide=[ISelector])

    sigsrc = binding.Obtain(ISignalSource)
    haveSignal = signals = binding.Delegate('sigsrc')

    cache = binding.Make([dict, dict, dict])
    rwe = binding.Make([dict, dict, dict])
    count = binding.Make(lambda: sources.Semaphore(0))
    scheduler = binding.Obtain(IScheduler)

    checkInterval = binding.Obtain(
        PropertyName('peak.running.reactor.checkInterval'))

    sleep = binding.Obtain('import:time.sleep')
    select = binding.Obtain('import:select.select')
    _error = binding.Obtain('import:select.error')

    def readable(self, stream):
        """'IEventSource' that fires when 'stream' is readable"""
        return self._getEvent(0, stream)

    def writable(self, stream):
        """'IEventSource' that fires when 'stream' is writable"""
        return self._getEvent(1, stream)

    def exceptional(self, stream):
        """'IEventSource' that fires when 'stream' is in error/out-of-band"""
        return self._getEvent(2, stream)

    def monitor(self):

        r, w, e = self.rwe
        count = self.count
        sleep = self.scheduler.sleep()
        time_available = self.scheduler.time_available
        select = self.select
        error = self._error

        while True:
            yield count
            resume()  # wait until there are selectables
            yield sleep
            resume()  # ensure we are in top-level loop

            delay = time_available()
            if delay is None:
                delay = self.checkInterval

            try:
                fr, fw, fe = self.select(r.keys(), w.keys(), e.keys(), delay)
            except error, v:
                if v.args[0] == EINTR:
                    continue  # signal received during select, try again
                else:
                    raise

            for fired, events in (fe, e), (fr, r), (fw, w):
                for stream in fired:
                    events[stream].send(True)
コード例 #19
0
class Interrupt(object):
    """Interrupt a task with an error if specified event occurs

    Usage::

        try:
            yield events.Interrupt( stream.readline(), scheduler.timeout(5) )
            line = events.resume()

        except events.Interruption:
            print "readline() took more than 5 seconds"

    An 'Interrupt' object is an 'events.ITaskSwitch', so you can only use it
    within a task, and you cannot set callbacks on it.  If the supplied
    generator/iterator exits for any reason, the interruption is cancelled.

    Also note that because generator objects are not reusable, neither are
    'Interrupt' objects.  You must create an 'Interrupt' for each desired
    invocation of the applicable generator.  However, the called generator
    need not create an additional 'Interrupt' for any nested generator calls,
    even though multiple interrupts may be active at the same time.  This
    allows you to do things like e.g. set one timeout for each line of data
    being received, and another timeout for receiving an entire email.
    """

    __slots__ = 'iterator', 'source', 'errorType'

    protocols.advise(instancesProvide=[ITaskSwitch], )

    def __init__(self, iterator, eventSource, errorType=Interruption):
        """'Interrupt(iterator,eventSource,errorType=Interruption)'

        Wrap execution of 'iterator' so that it will raise 'errorType' if
        'eventSource' fires before 'iterator' exits (or aborts), assuming that
        the 'Interrupt' is yielded to a task."""

        self.iterator = adapt(iterator, IProcedure)
        self.source = adapt(eventSource, IEventSource)
        self.errorType = errorType

    def nextAction(self, task=None, state=None):

        if state is not None:

            cancelled = Condition(False)

            def canceller():
                yield self.iterator
                cancelled.set(True)
                yield resume()

            def interrupt(source, event):
                if not cancelled():
                    state.CALL(doInterrupt())
                    task.step(source, event)

            def doInterrupt():
                raise self.errorType(resume())
                yield None

            state.CALL(canceller())
            self.source.addCallback(interrupt)

        return True
コード例 #20
0
class Observable(object):
    """Base class for a generic event source

    You may subclass this class to create other kinds of event sources: change
    the 'singleFire' class attribute to 'False' in your subclass if you would
    like for events to be broadcast to all callbacks, whether they accept or
    reject the event.
    """

    __slots__ = '_callbacks', '__weakref__', '_disabled', '_savedEvents'

    singleFire = True
    overrunOK = True

    protocols.advise(instancesProvide=[IEventSource])

    def __init__(self):
        self._callbacks = []
        self._disabled = 0

    def nextAction(self, task=None, state=None):
        """See 'events.ITaskSwitch.nextAction()'"""
        if task is not None:
            self.addCallback(task.step)

    def addCallback(self, func):
        """See 'events.IEventSource.addCallback()'"""
        cb = self._callbacks
        item = (func, object())
        cb.append(item)
        return lambda: (item in cb) and cb.remove(item)

    def disable(self):
        """Pause event callbacks"""
        if not self._disabled:
            self._savedEvents = []
        self._disabled += 1

    def enable(self):
        """Resume and send saved events"""

        if not self._disabled:
            raise ValueError("More enable() calls than disable() calls", self)

        self._disabled -= 1

        if not self._disabled:

            while self._savedEvents:
                self._fire(self._savedEvents.pop(0))

            del self._savedEvents

    def _fire(self, event):

        if self._disabled:
            self._buffer(event)
            return

        callbacks = self._callbacks
        count = len(callbacks)

        if self.singleFire:
            while count and callbacks:
                if callbacks.pop(0)[0](self, event):
                    return
                count -= 1
        else:
            while count and callbacks:
                callbacks.pop(0)[0](self, event)
                count -= 1

    def _buffer(self, event):

        saved = self._savedEvents

        if self.overrunOK:
            if saved:
                del saved[0]

        elif len(saved) >= len(self._callbacks):
            raise ValueError("Can't buffer event", self, event)

        saved.append(event)