Commit 8da81127 authored by Ontje Lünsdorf's avatar Ontje Lünsdorf
Browse files

AnyOf and AllOf are now subclasses of Condition and bound to the environment....

AnyOf and AllOf are now subclasses of Condition and bound to the environment. Using any_of or all_of with no events is now supported.
parent 5f4e4092
......@@ -30,7 +30,6 @@ Example By:
import random
import simpy
import simpy.util
RANDOM_SEED = 42
......@@ -59,7 +58,7 @@ class BroadcastPipe(object):
if not self.pipes:
raise RuntimeError('There are no output pipes.')
events = [store.put(value) for store in self.pipes]
return simpy.util.all_of(events) # Condition event for all "events"
return self.env.all_of(events) # Condition event for all "events"
def get_output_conn(self):
"""Get a new output connection for this broadcast pipe.
......
......@@ -193,10 +193,10 @@ class Event(object):
return self
def __and__(self, other):
return Condition(self.env, all_events, [self, other])
return Condition(self.env, Condition.all_events, [self, other])
def __or__(self, other):
return Condition(self.env, any_event, [self, other])
return Condition(self.env, Condition.any_events, [self, other])
class Condition(Event):
......@@ -236,6 +236,10 @@ class Condition(Event):
# condition once it is being processed.
self.callbacks.append(self._collect_values)
# Immediately trigger the condition if it is already met.
if self._evaluate(self._events, self._interim_values):
self.succeed({})
def _desc(self):
"""Return a string *Condition(and_or_or, [events])*."""
return '%s(%s, %s)' % (self.__class__.__name__,
......@@ -268,7 +272,7 @@ class Condition(Event):
if event.callbacks is None:
raise RuntimeError('Event %s has already been triggered' % event)
if type(event) is Condition:
if isinstance(event, Condition):
self._sub_conditions.append(event)
self._events.append(event)
......@@ -293,30 +297,39 @@ class Condition(Event):
self.succeed({})
def __iand__(self, other):
if self._evaluate is not all_events:
if self._evaluate is not Condition.all_events:
# Use self.__and__
return NotImplemented
return self._add_event(other)
def __ior__(self, other):
if self._evaluate is not any_event:
if self._evaluate is not Condition.any_events:
# Use self.__or__
return NotImplemented
return self._add_event(other)
@staticmethod
def all_events(events, values):
return len(events) == len(values)
@staticmethod
def any_events(events, values):
return len(values) > 0 or len(events) == 0
def all_events(events, values):
"""Helper for :class:`Condition`. Return ``True`` if there are
values for all ``events``."""
return len(events) == len(values)
class AllOf(Condition):
"""A condition event that waits for all ``events``."""
def __init__(self, env, events):
Condition.__init__(self, env, Condition.all_events, events)
def any_event(events, values):
"""Helper for :class:`Condition`. Return ``True`` if there is at
least one value available from ``events``."""
return len(values) > 0
class AnyOf(Condition):
def __init__(self, env, events):
"""A condition event that waits until the first of ``events`` is
triggered."""
Condition.__init__(self, env, Condition.any_events, events)
class Timeout(Event):
......@@ -583,6 +596,8 @@ class Environment(object):
process = BoundClass(Process)
timeout = BoundClass(Timeout)
event = BoundClass(Event)
all_of = BoundClass(AllOf)
any_of = BoundClass(AnyOf)
suspend = event
start = process
......
......@@ -7,7 +7,7 @@ import re
import pytest
from simpy import Interrupt, simulate
from simpy.util import start_delayed, subscribe_at, all_of, any_of
from simpy.util import start_delayed, subscribe_at
def test_start_delayed(env):
......@@ -121,7 +121,7 @@ def test_all_of(env):
def parent(env):
# Start 10 events.
events = [env.timeout(i, value=i) for i in range(10)]
results = yield all_of(events)
results = yield env.all_of(events)
assert results == {events[i]: i for i in range(10)}
assert env.now == 9
......@@ -131,7 +131,7 @@ def test_all_of(env):
def test_wait_for_all_with_errors(env):
"""On default wait_for_all should fail immediately if one of its events
"""On default AllOf should fail immediately if one of its events
fails."""
def child_with_error(env, value):
yield env.timeout(value)
......@@ -143,7 +143,7 @@ def test_wait_for_all_with_errors(env):
env.timeout(3, value=3)]
try:
condition = all_of(events)
condition = env.all_of(events)
yield condition
assert False, 'There should have been an exception'
except RuntimeError as e:
......@@ -163,8 +163,8 @@ def test_all_of_chaining(env):
"""If a wait_for_all condition A is chained to a wait_for_all condition B,
B will be merged into A."""
def parent(env):
condition_A = all_of([env.timeout(i, value=i) for i in range(2)])
condition_B = all_of([env.timeout(i, value=i) for i in range(2)])
condition_A = env.all_of([env.timeout(i, value=i) for i in range(2)])
condition_B = env.all_of([env.timeout(i, value=i) for i in range(2)])
condition_A &= condition_B
......@@ -180,8 +180,8 @@ def test_all_of_chaining_intermediate_results(env):
another wait_for_all condition B, the results are copied into condition
A."""
def parent(env):
condition_A = all_of([env.timeout(i, value=i) for i in range(2)])
condition_B = all_of([env.timeout(i, value=i) for i in range(2)])
condition_A = env.all_of([env.timeout(i, value=i) for i in range(2)])
condition_B = env.all_of([env.timeout(i, value=i) for i in range(2)])
yield env.timeout(0)
......@@ -202,7 +202,7 @@ def test_all_of_with_triggered_events(env):
yield env.timeout(2)
try:
all_of([event])
env.all_of([event])
assert False, 'Expected an exception'
except RuntimeError as e:
assert re.match(r'Event <Timeout\(1\) object at 0x.*> has already '
......@@ -217,7 +217,7 @@ def test_any_of(env):
def parent(env):
# Start 10 events.
events = [env.timeout(i, value=i) for i in range(10)]
results = yield any_of(events)
results = yield env.any_of(events)
assert results == {events[0]: 0}
assert env.now == 0
......@@ -237,7 +237,7 @@ def test_any_of_with_errors(env):
env.timeout(2, value=2)]
try:
condition = any_of(events)
condition = env.any_of(events)
yield condition
assert False, 'There should have been an exception'
except RuntimeError as e:
......@@ -255,8 +255,8 @@ def test_any_of_chaining(env):
"""If a any_of condition A is chained to a any_of condition B,
B will be merged into A."""
def parent(env):
condition_A = any_of([env.timeout(2, value='a')])
condition_B = any_of([env.timeout(1, value='b')])
condition_A = env.any_of([env.timeout(2, value='a')])
condition_B = env.any_of([env.timeout(1, value='b')])
condition_A |= condition_B
......@@ -274,7 +274,7 @@ def test_any_of_with_triggered_events(env):
yield env.timeout(2)
try:
any_of([event])
env.any_of([event])
assert False, 'Expected an exception'
except RuntimeError as e:
assert re.match(r'Event <Timeout\(1\) object at 0x.*> has already '
......@@ -282,3 +282,23 @@ def test_any_of_with_triggered_events(env):
env.start(parent(env))
simulate(env)
def test_empty_any_of(env):
"""AnyOf will triggered immediately if there are no events."""
def parent(env):
results = yield env.any_of([])
assert results == {'a': 1}
env.start(parent(env))
simulate(env)
def test_empty_all_of(env):
"""AllOf will triggered immediately if there are no events."""
def parent(env):
results = yield env.all_of([])
assert results == {}
env.start(parent(env))
simulate(env)
......@@ -7,7 +7,7 @@ This modules contains various utility functions:
- :func:`any_of()`: Wait until one of the passed events occurred.
"""
from simpy.core import Condition, all_events, any_event
from simpy.core import Condition
def start_delayed(env, peg, delay):
......@@ -66,31 +66,3 @@ def subscribe_at(event):
env.start(signaller(event, subscriber))
else:
raise RuntimeError('%s has already terminated.' % event)
def all_of(events):
"""Return a :class:`~simpy.core.Condition` event that waits for all
``events``.
Raise a :exc:`ValueError` if no events are passed.
"""
if not events:
raise ValueError('No events were passed.')
env = events[0].env
return Condition(env, all_events, events)
def any_of(events):
"""Return a :class:`~simpy.core.Condition` event that waits for the
first of ``events`` to occur.
Raise a :exc:`ValueError` if no events are passed.
"""
if not events:
raise ValueError('No events were passed.')
env = events[0].env
return Condition(env, any_event, events)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment