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

Created a new base environment class and moved step into this class. The...

Created a new base environment class and moved step into this class. The default environment is derived from there and adds the peek and simulate methods.
parent 9251555d
......@@ -65,4 +65,4 @@ env = simpy.Environment()
# Start processes and simulate
counter = simpy.Resource(env, capacity=1)
env.start(source(env, NEW_CUSTOMERS, INTERVAL_CUSTOMERS, counter))
simpy.simulate(env)
env.simulate()
......@@ -94,4 +94,4 @@ env = simpy.Environment()
env.start(setup(env, NUM_MACHINES, WASHTIME, T_INTER))
# Simulate!
simpy.simulate(env, until=SIM_TIME)
env.simulate(until=SIM_TIME)
......@@ -101,4 +101,4 @@ env.start(gas_station_control(env, fuel_pump))
env.start(car_generator(env, gas_station, fuel_pump))
# Simulate!
simpy.simulate(env, until=SIM_TIME)
env.simulate(until=SIM_TIME)
......@@ -68,4 +68,4 @@ cable = Cable(env, 10)
env.start(sender(env, cable))
env.start(receiver(env, cable))
simpy.simulate(env, until=SIM_DURATION)
env.simulate(until=SIM_DURATION)
......@@ -131,7 +131,7 @@ machines = [Machine(env, 'Machine %d' % i, repairman)
env.start(other_jobs(env, repairman))
# Simulate!
simpy.simulate(env, until=SIM_TIME)
env.simulate(until=SIM_TIME)
# Analyis/results
print('Machine shop results after %s weeks' % WEEKS)
......
......@@ -95,7 +95,7 @@ theater = Theater(counter, movies, available, sold_out, when_sold_out,
# Start process and simulate
env.start(customer_arrivals(env, theater))
simpy.simulate(env, until=SIM_TIME)
env.simulate(until=SIM_TIME)
# Analysis/results
for movie in movies:
......
......@@ -123,7 +123,7 @@ env.start(message_generator('Generator A', env, pipe))
env.start(message_consumer('Consumer A', env, pipe))
print('\nOne-to-one pipe communication\n')
simpy.simulate(env, until=SIM_TIME)
env.simulate(until=SIM_TIME)
# For one-to many use BroadcastPipe
# (Note: could also be used for one-to-one,many-to-one or many-to-many)
......@@ -135,4 +135,4 @@ env.start(message_consumer('Consumer A', env, bc_pipe.get_output_conn()))
env.start(message_consumer('Consumer B', env, bc_pipe.get_output_conn()))
print('\nOne-to-many pipe communication\n')
simpy.simulate(env, until=SIM_TIME)
env.simulate(until=SIM_TIME)
......@@ -16,9 +16,6 @@ Core classes and functions
:meth:`Environment.start()`. It inherits :class:`Event`.
- :class:`Interrupt`: This exception is thrown into a process if it gets
interrupted by another one.
- :func:`peek()`: Return the next event's time.
- :func:`step()`: Process the next event.
- :func:`simulate()`: Execute the simulation until a given time.
Resources
......@@ -68,7 +65,7 @@ Other
.. - :func:`test`: Run the test suite on the installed copy of Simpy.
"""
from simpy.core import Environment, Interrupt, Process, peek, step, simulate
from simpy.core import Environment, Interrupt, Process
from simpy.resources.resource import (
Resource, PriorityResource, PreemptiveResource)
from simpy.resources.container import Container
......@@ -77,7 +74,7 @@ from simpy.resources.store import Store, FilterStore
__all__ = [
'test',
'Environment', 'Interrupt', 'Process', 'peek', 'step', 'simulate',
'Environment', 'Interrupt', 'Process',
'Resource', 'PriorityResource', 'PreemptiveResource',
'Container',
'Store', 'FilterStore',
......
......@@ -535,23 +535,14 @@ class Scheduler(object):
raise EmptySchedule()
class Environment(object):
"""The *environment* contains the simulation state and provides a
basic API for processes to interact with it.
class BaseEnvironment(object):
"""Base class for an environment, which schedules and executes events and
processes.
"""
def __init__(self, initial_time=0, scheduler=None):
self._active_proc = None
if scheduler is None:
scheduler = Scheduler(self, initial_time)
def __init__(self, scheduler):
self.scheduler = scheduler
self.event = types.MethodType(Event, self)
self.suspend = types.MethodType(Event, self)
self.timeout = types.MethodType(Timeout, self)
self.process = types.MethodType(Process, self)
self.start = types.MethodType(Process, self)
self._active_proc = None
self.schedule = self.scheduler.schedule
self.pop = self.scheduler.pop
......@@ -577,75 +568,87 @@ class Environment(object):
"""
raise StopIteration(value)
def step(env):
"""Get and process the next event for the Environment ``env``.
def peek(env):
"""Return the time of the Environment ``env``'s next event or
``inf`` if the event queue is empty.
Raise an :exc:`IndexError` if no valid event is on the heap.
"""
return env.scheduler.peek()
"""
event = env.pop()
# Process callbacks of the event.
for callback in event.callbacks:
callback(event)
event.callbacks = None
def step(env):
"""Get and process the next event for the Environment ``env``.
if not event.ok:
# The event has failed, check if it is defused. Raise the value if not.
if not hasattr(event, 'defused'):
raise event._value
Raise an :exc:`IndexError` if no valid event is on the heap.
"""
event = env.pop()
class Environment(BaseEnvironment):
"""The *environment* contains the simulation state and provides a
basic API for processes to interact with it.
# Process callbacks of the event.
for callback in event.callbacks:
callback(event)
event.callbacks = None
"""
def __init__(self, initial_time=0, scheduler=None):
if scheduler is None:
scheduler = Scheduler(self, initial_time)
super(Environment, self).__init__(scheduler)
if not event.ok:
# The event has failed, check if it is defused. Raise the value if not.
if not hasattr(event, 'defused'):
raise event._value
self.event = types.MethodType(Event, self)
self.suspend = types.MethodType(Event, self)
self.timeout = types.MethodType(Timeout, self)
self.process = types.MethodType(Process, self)
self.start = types.MethodType(Process, self)
def peek(self):
"""Return the time at which the next event is scheduled or ``inf`` if
the event queue is empty.
def simulate(env, until=None):
"""Simulate the environment until the given criterion *until* is met.
"""
return self.scheduler.peek()
The parameter ``until`` specifies when the simulation ends.
def simulate(self, until=None):
"""Executes events until the given criterion *until* is met.
- If it is ``None`` (which is the default) the simulation will only
stop if there are no further events.
- If it is ``None`` (which is the default) the execution will only
stop if there are no further events.
- If it is an :class:`Event` the simulation will stop once this
event has happened.
- If it is an :class:`Event` the execution will stop once this
event has been triggered.
- If it can be converted to a number the simulation will stop when the
simulation time reaches *until*. (*Note:* Internally, an event is
created, so the simulation time will be exactly *until* afterwards. No
other events scheduled for *until* will be processed, though---as it is
at the very beginning of the simulation.)
- If it can be converted to a number the execution will stop when the
simulation time reaches *until*. (*Note:* Internally, an event is
created, so the simulation time will be exactly *until* afterwards.
No other events scheduled for *until* will be processed, though---as
it is at the very beginning of the simulation.)
"""
if until is None:
until = env.event()
elif not isinstance(until, Event):
at = float(until)
"""
if until is None:
until = Event(self)
elif not isinstance(until, Event):
at = float(until)
if at <= env.now:
raise ValueError('until(=%s) should be > the current simulation '
'time.' % at)
if at <= self.now:
raise ValueError('until(=%s) should be > the current '
'simulation time.' % at)
# Schedule the event with before all regular timeouts.
until = env.event()
until._value = None
env.schedule(until, HIGH_PRIORITY, at - env.now)
# Schedule the event with before all regular timeouts.
until = Event(self)
until._value = None
self.schedule(until, HIGH_PRIORITY, at - self.now)
until.callbacks.append(_stop_simulate)
until.callbacks.append(_stop_simulate)
try:
while True:
step(env)
except EmptySchedule:
pass
try:
while True:
self.step()
except EmptySchedule:
pass
return until.value if until.triggered else None
return until.value if until.triggered else None
def _describe_frame(frame):
......
import pytest
from simpy import simulate
def test_operator_and(env):
def process(env):
......@@ -15,7 +13,7 @@ def test_operator_and(env):
}
env.start(process(env))
simulate(env)
env.simulate()
def test_operator_or(env):
......@@ -28,7 +26,7 @@ def test_operator_or(env):
}
env.start(process(env))
simulate(env)
env.simulate()
def test_operator_nested_and(env):
......@@ -43,7 +41,7 @@ def test_operator_nested_and(env):
assert env.now == 1
env.start(process(env))
simulate(env)
env.simulate()
def test_operator_nested_or(env):
......@@ -59,7 +57,7 @@ def test_operator_nested_or(env):
assert env.now == 2
env.start(process(env))
simulate(env)
env.simulate()
def test_nested_cond_with_error(env):
......@@ -75,7 +73,7 @@ def test_nested_cond_with_error(env):
assert err.args == ('Onoes!',)
env.start(process(env))
simulate(env)
env.simulate()
def test_cond_with_error(env):
......@@ -91,7 +89,7 @@ def test_cond_with_error(env):
assert err.args == ('Onoes, failed after 0!',)
env.start(process(env))
simulate(env)
env.simulate()
def test_cond_with_nested_error(env):
......@@ -107,7 +105,7 @@ def test_cond_with_nested_error(env):
assert err.args == ('Onoes, failed after 0!',)
env.start(process(env))
simulate(env)
env.simulate()
def test_cond_with_uncaught_error(env):
......@@ -122,7 +120,7 @@ def test_cond_with_uncaught_error(env):
env.start(process(env))
try:
simulate(env)
env.simulate()
assert False, 'There should have been an exception.'
except ValueError:
pass
......@@ -141,7 +139,7 @@ def test_iand_with_and_cond(env):
assert sorted(results.values()) == [0, 1, 2]
env.start(process(env))
simulate(env)
env.simulate()
def test_iand_with_or_cond(env):
......@@ -156,7 +154,7 @@ def test_iand_with_or_cond(env):
assert sorted(results.values()) == [0, 1]
env.start(process(env))
simulate(env)
env.simulate()
def test_ior_with_or_cond(env):
......@@ -171,7 +169,7 @@ def test_ior_with_or_cond(env):
assert sorted(results.values()) == [0]
env.start(process(env))
simulate(env)
env.simulate()
def test_ior_with_and_cond(env):
......@@ -186,7 +184,7 @@ def test_ior_with_and_cond(env):
assert sorted(results.values()) == [0]
env.start(process(env))
simulate(env)
env.simulate()
def test_immutable_results(env):
......@@ -207,7 +205,7 @@ def test_immutable_results(env):
assert results == {timeout[0]: 0}
env.start(process(env))
simulate(env)
env.simulate()
def test_shared_and_condition(env):
......@@ -225,7 +223,7 @@ def test_shared_and_condition(env):
env.start(p1(env, c1))
env.start(p2(env, c2))
simulate(env)
env.simulate()
def test_shared_or_condition(env):
......@@ -243,4 +241,4 @@ def test_shared_or_condition(env):
env.start(p1(env, c1))
env.start(p2(env, c2))
simulate(env)
env.simulate()
......@@ -25,7 +25,7 @@ def test_error_forwarding(env):
assert err.args[0] == 'Onoes!'
env.start(parent(env))
simpy.simulate(env)
env.simulate()
def test_no_parent_process(env):
......@@ -45,7 +45,7 @@ def test_no_parent_process(env):
pytest.fail('There should be no error (%s).' % err)
env.start(parent(env))
pytest.raises(ValueError, simpy.simulate, env)
pytest.raises(ValueError, env.simulate)
def test_crashing_child_traceback(env):
......@@ -70,7 +70,7 @@ def test_crashing_child_traceback(env):
assert 'raise RuntimeError(\'Oh noes,' in stacktrace
env.start(root(env))
simpy.simulate(env)
env.simulate()
@pytest.mark.skipif('sys.version_info[0] < 3')
......@@ -93,7 +93,7 @@ def test_exception_chaining(env):
env.start(grandparent(env))
try:
simpy.simulate(env)
env.simulate()
pytest.fail('There should have been an exception')
except RuntimeError:
import traceback
......@@ -111,7 +111,7 @@ def test_invalid_event(env):
env.start(root(env))
try:
simpy.simulate(env)
env.simulate()
pytest.fail('Hey, this is not allowed!')
except RuntimeError as err:
assert err.args[0].endswith('Invalid yield value "None"')
......@@ -124,7 +124,7 @@ def test_exception_handling(env):
event = env.event()
event.fail(RuntimeError())
try:
simpy.simulate(env, until=1)
env.simulate(until=1)
assert False, 'There must be a RuntimeError!'
except RuntimeError:
pass
......@@ -140,7 +140,7 @@ def test_callback_exception_handling(env):
event.callbacks.append(callback)
event.fail(RuntimeError())
assert not hasattr(event, 'defused'), 'Event has been defused immediately'
simpy.simulate(env, until=1)
env.simulate(until=1)
assert event.defused, 'Event has not been defused'
......@@ -158,5 +158,5 @@ def test_process_exception_handling(env):
event.fail(RuntimeError())
assert not hasattr(event, 'defused'), 'Event has been defuseed immediately'
simpy.simulate(env, until=1)
env.simulate(until=1)
assert event.defused, 'Event has not been defused'
......@@ -28,7 +28,7 @@ def test_interruption(env):
child_process.interrupt('interrupt!')
env.start(interruptor(env))
simpy.simulate(env)
env.simulate()
def test_concurrent_interrupts(env, log):
......@@ -51,7 +51,7 @@ def test_concurrent_interrupts(env, log):
for name in ('boggis', 'bunce', 'beans'):
env.start(farmer(env, name, fantastic_mr_fox))
simpy.simulate(env, 20)
env.simulate(20)
assert log == [(0, 'boggis'), (0, 'bunce'), (0, 'beans')]
......@@ -72,7 +72,7 @@ def test_init_interrupt(env):
yield env.timeout(1)
env.start(root(env))
simpy.simulate(env)
env.simulate()
def test_interrupt_terminated_process(env):
......@@ -91,7 +91,7 @@ def test_interrupt_terminated_process(env):
yield env.timeout(1)
env.start(parent(env))
simpy.simulate(env)
env.simulate()
def test_multiple_interrupts(env):
......@@ -116,7 +116,7 @@ def test_multiple_interrupts(env):
assert result == 1
env.start(parent(env))
simpy.simulate(env)
env.simulate()
def test_interrupt_self(env):
......@@ -126,7 +126,7 @@ def test_interrupt_self(env):
yield env.timeout(0)
env.start(pem(env))
simpy.simulate(env)
env.simulate()
def test_immediate_interrupt(env, log):
......@@ -143,7 +143,7 @@ def test_immediate_interrupt(env, log):
c = env.start(child(env, log))
env.start(resumer(env, c))
simpy.simulate(env)
env.simulate()
# Confirm that child has been interrupted immediately at timestep 0.
assert log == [0]
......@@ -163,7 +163,7 @@ def test_interrupt_suspend(env):
child_proc.interrupt()
env.start(parent(env))
simpy.simulate(env)
env.simulate()
def test_interrupt_event(env):
......@@ -180,7 +180,7 @@ def test_interrupt_event(env):
child_proc.interrupt()
env.start(parent(env))
simpy.simulate(env)
env.simulate()
def test_concurrent_behaviour(env):
......@@ -201,4 +201,4 @@ def test_concurrent_behaviour(env):
proc_a = env.start(proc_a(env))
env.start(proc_b(env, proc_a))
simpy.simulate(env)
env.simulate()
......@@ -35,7 +35,7 @@ def test_resource(env, log):
assert resource.count == 0
env.start(pem(env, 'a', resource, log))
env.start(pem(env, 'b', resource, log))
simpy.simulate(env)
env.simulate()
assert log == [('a', 1), ('b', 2)]
......@@ -53,7 +53,7 @@ def test_resource_context_manager(env, log):
resource = simpy.Resource(env, capacity=1)
env.start(pem(env, 'a', resource, log))
env.start(pem(env, 'b', resource, log))
simpy.simulate(env)
env.simulate()
assert log == [('a', 1), ('b', 2)]
......@@ -68,7 +68,7 @@ def test_resource_slots(env, log):
resource = simpy.Resource(env, capacity=3)
for i in range(9):
env.start(pem(env, str(i), resource, log))
simpy.simulate(env)
env.simulate()
assert log == [('0', 0), ('1', 0), ('2', 0),
('3', 1), ('4', 1), ('5', 1),
......@@ -102,7 +102,7 @@ def test_resource_continue_after_interrupt(env):
env.start(pem(env, res))
proc = env.start(victim(env, res))
env.start(interruptor(env, proc))
simpy.simulate(env)
env.simulate()
def test_resource_release_after_interrupt(env):
......@@ -132,7 +132,7 @@ def test_resource_release_after_interrupt(env):
env.start(blocker(env, res))
victim_proc = env.start(victim(env, res))
env.start(interruptor(env, victim_proc))
simpy.simulate(env)
env.simulate()
def test_resource_cm_exception(env, log):
......@@ -153,7 +153,7 @@ def test_resource_cm_exception(env, log):
# The second process is used to check if it was able to access the
# resource:
env.start(process(env, resource, log, False))
simpy.simulate(env)
env.simulate()
assert log == [1, 2]
......@@ -166,7 +166,7 @@ def test_resource_with_condition(env):
resource = simpy.Resource(env, 1)
env.start(process(env, resource))
simpy.simulate(env)
env.simulate()
def test_resource_with_priority_queue(env):
......@@ -183,7 +183,7 @@ def test_resource_with_priority_queue(env):
env.start(process(env, 2, resource, 3, 10))
env.start(process(env, 2, resource, 3, 15)) # Test equal priority
env.start(process(env, 4, resource, 1, 5))
simpy.simulate(env)
env.simulate()
def test_get_users(env):
......@@ -194,11 +194,11 @@ def test_get_users(env):