updated from Atlas
This commit is contained in:
@@ -15,6 +15,8 @@ While active, the context manager reverses text output to
|
|||||||
YKCOWREBBAJ
|
YKCOWREBBAJ
|
||||||
>>> what # <4>
|
>>> what # <4>
|
||||||
'JABBERWOCKY'
|
'JABBERWOCKY'
|
||||||
|
>>> print('Back to normal.') # <5>
|
||||||
|
Back to normal.
|
||||||
|
|
||||||
# END MIRROR_DEMO_1
|
# END MIRROR_DEMO_1
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# BEGIN CORO_AVERAGER
|
|
||||||
"""
|
"""
|
||||||
A coroutine to compute a running average
|
A coroutine to compute a running average
|
||||||
|
|
||||||
|
# BEGIN CORO_AVERAGER_TEST
|
||||||
>>> coro_avg = averager() # <1>
|
>>> coro_avg = averager() # <1>
|
||||||
>>> next(coro_avg) # <2>
|
>>> next(coro_avg) # <2>
|
||||||
>>> coro_avg.send(10) # <3>
|
>>> coro_avg.send(10) # <3>
|
||||||
@@ -11,14 +11,17 @@ A coroutine to compute a running average
|
|||||||
>>> coro_avg.send(5)
|
>>> coro_avg.send(5)
|
||||||
15.0
|
15.0
|
||||||
|
|
||||||
|
# END CORO_AVERAGER_TEST
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# BEGIN CORO_AVERAGER
|
||||||
def averager():
|
def averager():
|
||||||
total = 0.0
|
total = 0.0
|
||||||
count = 0
|
count = 0
|
||||||
average = None
|
average = None
|
||||||
while True:
|
while True: # <1>
|
||||||
term = yield average # <4>
|
term = yield average # <2>
|
||||||
total += term
|
total += term
|
||||||
count += 1
|
count += 1
|
||||||
average = total/count
|
average = total/count
|
||||||
@@ -6,8 +6,8 @@ Testing ``averager`` by itself::
|
|||||||
# BEGIN RETURNING_AVERAGER_DEMO1
|
# BEGIN RETURNING_AVERAGER_DEMO1
|
||||||
|
|
||||||
>>> coro_avg = averager()
|
>>> coro_avg = averager()
|
||||||
>>> next(coro_avg) # <1>
|
>>> next(coro_avg)
|
||||||
>>> coro_avg.send(10)
|
>>> coro_avg.send(10) # <1>
|
||||||
>>> coro_avg.send(30)
|
>>> coro_avg.send(30)
|
||||||
>>> coro_avg.send(6.5)
|
>>> coro_avg.send(6.5)
|
||||||
>>> coro_avg.send(None) # <2>
|
>>> coro_avg.send(None) # <2>
|
||||||
@@ -58,4 +58,4 @@ def averager():
|
|||||||
count += 1
|
count += 1
|
||||||
average = total/count
|
average = total/count
|
||||||
return Result(count, average) # <2>
|
return Result(count, average) # <2>
|
||||||
# END RETURNING_AVERAGER
|
# END RETURNING_AVERAGER
|
||||||
@@ -75,7 +75,7 @@ def main(data): # <8>
|
|||||||
next(group) # <10>
|
next(group) # <10>
|
||||||
for value in values:
|
for value in values:
|
||||||
group.send(value) # <11>
|
group.send(value) # <11>
|
||||||
group.send(None) # <12>
|
group.send(None) # important! <12>
|
||||||
|
|
||||||
# print(results) # uncomment to debug
|
# print(results) # uncomment to debug
|
||||||
report(results)
|
report(results)
|
||||||
203
16-coroutine/taxi_sim.py
Normal file
203
16-coroutine/taxi_sim.py
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
|
||||||
|
"""
|
||||||
|
Taxi simulator
|
||||||
|
==============
|
||||||
|
|
||||||
|
Driving a taxi from the console::
|
||||||
|
|
||||||
|
>>> from taxi_sim import taxi_process
|
||||||
|
>>> taxi = taxi_process(ident=13, trips=2, start_time=0)
|
||||||
|
>>> next(taxi)
|
||||||
|
Event(time=0, proc=13, action='leave garage')
|
||||||
|
>>> taxi.send(_.time + 7)
|
||||||
|
Event(time=7, proc=13, action='pick up passenger')
|
||||||
|
>>> taxi.send(_.time + 23)
|
||||||
|
Event(time=30, proc=13, action='drop off passenger')
|
||||||
|
>>> taxi.send(_.time + 5)
|
||||||
|
Event(time=35, proc=13, action='pick up passenger')
|
||||||
|
>>> taxi.send(_.time + 48)
|
||||||
|
Event(time=83, proc=13, action='drop off passenger')
|
||||||
|
>>> taxi.send(_.time + 1)
|
||||||
|
Event(time=84, proc=13, action='going home')
|
||||||
|
>>> taxi.send(_.time + 10)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<stdin>", line 1, in <module>
|
||||||
|
StopIteration
|
||||||
|
|
||||||
|
Sample run with two cars, random seed 10. This is a valid doctest::
|
||||||
|
|
||||||
|
>>> main(num_taxis=2, seed=10)
|
||||||
|
taxi: 0 Event(time=0, proc=0, action='leave garage')
|
||||||
|
taxi: 0 Event(time=5, proc=0, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=5, proc=1, action='leave garage')
|
||||||
|
taxi: 1 Event(time=10, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=15, proc=1, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=17, proc=0, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=24, proc=1, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=26, proc=0, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=30, proc=0, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=34, proc=0, action='going home')
|
||||||
|
taxi: 1 Event(time=46, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=48, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=110, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=139, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=140, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=150, proc=1, action='going home')
|
||||||
|
*** end of events ***
|
||||||
|
|
||||||
|
See longer sample run at the end of this module.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random
|
||||||
|
import collections
|
||||||
|
import queue
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
DEFAULT_NUMBER_OF_TAXIS = 3
|
||||||
|
DEFAULT_END_TIME = 180
|
||||||
|
SEARCH_DURATION = 5
|
||||||
|
TRIP_DURATION = 20
|
||||||
|
DEPARTURE_INTERVAL = 5
|
||||||
|
|
||||||
|
Event = collections.namedtuple('Event', 'time proc action')
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN TAXI_PROCESS
|
||||||
|
def taxi_process(ident, trips, start_time=0): # <1>
|
||||||
|
"""Yield to simulator issuing event at each state change"""
|
||||||
|
time = yield Event(start_time, ident, 'leave garage') # <2>
|
||||||
|
for i in range(trips): # <3>
|
||||||
|
time = yield Event(time, ident, 'pick up passenger') # <4>
|
||||||
|
time = yield Event(time, ident, 'drop off passenger') # <5>
|
||||||
|
|
||||||
|
yield Event(time, ident, 'going home') # <6>
|
||||||
|
# end of taxi process # <7>
|
||||||
|
# END TAXI_PROCESS
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN TAXI_SIMULATOR
|
||||||
|
class Simulator:
|
||||||
|
|
||||||
|
def __init__(self, procs_map):
|
||||||
|
self.events = queue.PriorityQueue()
|
||||||
|
self.procs = dict(procs_map)
|
||||||
|
|
||||||
|
def run(self, end_time): # <1>
|
||||||
|
"""Schedule and display events until time is up"""
|
||||||
|
# schedule the first event for each cab
|
||||||
|
for _, proc in sorted(self.procs.items()): # <2>
|
||||||
|
first_event = next(proc) # <3>
|
||||||
|
self.events.put(first_event) # <4>
|
||||||
|
|
||||||
|
# main loop of the simulation
|
||||||
|
sim_time = 0 # <5>
|
||||||
|
while sim_time < end_time: # <6>
|
||||||
|
if self.events.empty(): # <7>
|
||||||
|
print('*** end of events ***')
|
||||||
|
break
|
||||||
|
|
||||||
|
current_event = self.events.get() # <8>
|
||||||
|
sim_time, proc_id, previous_action = current_event # <9>
|
||||||
|
print('taxi:', proc_id, proc_id * ' ', current_event) # <10>
|
||||||
|
active_proc = self.procs[proc_id] # <11>
|
||||||
|
next_time = sim_time + compute_duration(previous_action) # <12>
|
||||||
|
try:
|
||||||
|
next_event = active_proc.send(next_time) # <13>
|
||||||
|
except StopIteration:
|
||||||
|
del self.procs[proc_id] # <14>
|
||||||
|
else:
|
||||||
|
self.events.put(next_event) # <15>
|
||||||
|
else: # <16>
|
||||||
|
msg = '*** end of simulation time: {} events pending ***'
|
||||||
|
print(msg.format(self.events.qsize()))
|
||||||
|
# END TAXI_SIMULATOR
|
||||||
|
|
||||||
|
|
||||||
|
def compute_duration(previous_action):
|
||||||
|
"""Compute action duration using exponential distribution"""
|
||||||
|
if previous_action in ['leave garage', 'drop off passenger']:
|
||||||
|
# new state is prowling
|
||||||
|
interval = SEARCH_DURATION
|
||||||
|
elif previous_action == 'pick up passenger':
|
||||||
|
# new state is trip
|
||||||
|
interval = TRIP_DURATION
|
||||||
|
elif previous_action == 'going home':
|
||||||
|
interval = 1
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown previous_action: %s' % previous_action)
|
||||||
|
return int(random.expovariate(1/interval)) + 1
|
||||||
|
|
||||||
|
|
||||||
|
def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS,
|
||||||
|
seed=None):
|
||||||
|
"""Initialize random generator, build procs and run simulation"""
|
||||||
|
if seed is not None:
|
||||||
|
random.seed(seed) # get reproducible results
|
||||||
|
|
||||||
|
taxis = {i: taxi_process(i, (i+1)*2, i*DEPARTURE_INTERVAL)
|
||||||
|
for i in range(num_taxis)}
|
||||||
|
sim = Simulator(taxis)
|
||||||
|
sim.run(end_time)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Taxi fleet simulator.')
|
||||||
|
parser.add_argument('-e', '--end-time', type=int,
|
||||||
|
default=DEFAULT_END_TIME,
|
||||||
|
help='simulation end time; default = %s'
|
||||||
|
% DEFAULT_END_TIME)
|
||||||
|
parser.add_argument('-t', '--taxis', type=int,
|
||||||
|
default=DEFAULT_NUMBER_OF_TAXIS,
|
||||||
|
help='number of taxis running; default = %s'
|
||||||
|
% DEFAULT_NUMBER_OF_TAXIS)
|
||||||
|
parser.add_argument('-s', '--seed', type=int, default=None,
|
||||||
|
help='random generator seed (for testing)')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
main(args.end_time, args.taxis, args.seed)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
Sample run from the command line, seed=3, maximum elapsed time=120::
|
||||||
|
|
||||||
|
# BEGIN TAXI_SAMPLE_RUN
|
||||||
|
$ python3 taxi_sim.py -s 3 -e 120
|
||||||
|
taxi: 0 Event(time=0, proc=0, action='leave garage')
|
||||||
|
taxi: 0 Event(time=2, proc=0, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=5, proc=1, action='leave garage')
|
||||||
|
taxi: 1 Event(time=8, proc=1, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=10, proc=2, action='leave garage')
|
||||||
|
taxi: 2 Event(time=15, proc=2, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=17, proc=2, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=18, proc=0, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=18, proc=2, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=25, proc=2, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=27, proc=1, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=27, proc=2, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=28, proc=0, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=40, proc=2, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=44, proc=2, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=55, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=59, proc=1, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=65, proc=0, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=65, proc=1, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=65, proc=2, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=72, proc=2, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=76, proc=0, action='going home')
|
||||||
|
taxi: 1 Event(time=80, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=88, proc=1, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=95, proc=2, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=97, proc=2, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=98, proc=2, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=106, proc=1, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=109, proc=2, action='going home')
|
||||||
|
taxi: 1 Event(time=110, proc=1, action='going home')
|
||||||
|
*** end of events ***
|
||||||
|
# END TAXI_SAMPLE_RUN
|
||||||
|
|
||||||
|
"""
|
||||||
215
16-coroutine/taxi_sim_delay.py
Normal file
215
16-coroutine/taxi_sim_delay.py
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
|
||||||
|
"""
|
||||||
|
Taxi simulator with delay on output
|
||||||
|
===================================
|
||||||
|
|
||||||
|
This is a variation of ``taxi_sim.py`` which adds a ``-d`` comand-line
|
||||||
|
option. When given, that option adds a delay in the main loop, pausing
|
||||||
|
the simulation for .5s for each "minute" of simulation time.
|
||||||
|
|
||||||
|
|
||||||
|
Driving a taxi from the console::
|
||||||
|
|
||||||
|
>>> from taxi_sim import taxi_process
|
||||||
|
>>> taxi = taxi_process(ident=13, trips=2, start_time=0)
|
||||||
|
>>> next(taxi)
|
||||||
|
Event(time=0, proc=13, action='leave garage')
|
||||||
|
>>> taxi.send(_.time + 7)
|
||||||
|
Event(time=7, proc=13, action='pick up passenger')
|
||||||
|
>>> taxi.send(_.time + 23)
|
||||||
|
Event(time=30, proc=13, action='drop off passenger')
|
||||||
|
>>> taxi.send(_.time + 5)
|
||||||
|
Event(time=35, proc=13, action='pick up passenger')
|
||||||
|
>>> taxi.send(_.time + 48)
|
||||||
|
Event(time=83, proc=13, action='drop off passenger')
|
||||||
|
>>> taxi.send(_.time + 1)
|
||||||
|
Event(time=84, proc=13, action='going home')
|
||||||
|
>>> taxi.send(_.time + 10)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<stdin>", line 1, in <module>
|
||||||
|
StopIteration
|
||||||
|
|
||||||
|
Sample run with two cars, random seed 10. This is a valid doctest::
|
||||||
|
|
||||||
|
>>> main(num_taxis=2, seed=10)
|
||||||
|
taxi: 0 Event(time=0, proc=0, action='leave garage')
|
||||||
|
taxi: 0 Event(time=5, proc=0, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=5, proc=1, action='leave garage')
|
||||||
|
taxi: 1 Event(time=10, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=15, proc=1, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=17, proc=0, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=24, proc=1, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=26, proc=0, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=30, proc=0, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=34, proc=0, action='going home')
|
||||||
|
taxi: 1 Event(time=46, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=48, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=110, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=139, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=140, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=150, proc=1, action='going home')
|
||||||
|
*** end of events ***
|
||||||
|
|
||||||
|
See longer sample run at the end of this module.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random
|
||||||
|
import collections
|
||||||
|
import queue
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
DEFAULT_NUMBER_OF_TAXIS = 3
|
||||||
|
DEFAULT_END_TIME = 180
|
||||||
|
SEARCH_DURATION = 5
|
||||||
|
TRIP_DURATION = 20
|
||||||
|
DEPARTURE_INTERVAL = 5
|
||||||
|
|
||||||
|
Event = collections.namedtuple('Event', 'time proc action')
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN TAXI_PROCESS
|
||||||
|
def taxi_process(ident, trips, start_time=0): # <1>
|
||||||
|
"""Yield to simulator issuing event at each state change"""
|
||||||
|
time = yield Event(start_time, ident, 'leave garage') # <2>
|
||||||
|
for i in range(trips): # <3>
|
||||||
|
time = yield Event(time, ident, 'pick up passenger') # <4>
|
||||||
|
time = yield Event(time, ident, 'drop off passenger') # <5>
|
||||||
|
|
||||||
|
yield Event(time, ident, 'going home') # <6>
|
||||||
|
# end of taxi process # <7>
|
||||||
|
# END TAXI_PROCESS
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN TAXI_SIMULATOR
|
||||||
|
class Simulator:
|
||||||
|
|
||||||
|
def __init__(self, procs_map):
|
||||||
|
self.events = queue.PriorityQueue()
|
||||||
|
self.procs = dict(procs_map)
|
||||||
|
|
||||||
|
def run(self, end_time, delay=False): # <1>
|
||||||
|
"""Schedule and display events until time is up"""
|
||||||
|
# schedule the first event for each cab
|
||||||
|
for _, proc in sorted(self.procs.items()): # <2>
|
||||||
|
first_event = next(proc) # <3>
|
||||||
|
self.events.put(first_event) # <4>
|
||||||
|
|
||||||
|
# main loop of the simulation
|
||||||
|
sim_time = 0 # <5>
|
||||||
|
while sim_time < end_time: # <6>
|
||||||
|
if self.events.empty(): # <7>
|
||||||
|
print('*** end of events ***')
|
||||||
|
break
|
||||||
|
|
||||||
|
# get and display current event
|
||||||
|
current_event = self.events.get() # <8>
|
||||||
|
if delay:
|
||||||
|
time.sleep((current_event.time - sim_time) / 2)
|
||||||
|
# update the simulation time
|
||||||
|
sim_time, proc_id, previous_action = current_event
|
||||||
|
print('taxi:', proc_id, proc_id * ' ', current_event)
|
||||||
|
active_proc = self.procs[proc_id]
|
||||||
|
# schedule next action for current proc
|
||||||
|
next_time = sim_time + compute_duration(previous_action)
|
||||||
|
try:
|
||||||
|
next_event = active_proc.send(next_time) # <12>
|
||||||
|
except StopIteration:
|
||||||
|
del self.procs[proc_id] # <13>
|
||||||
|
else:
|
||||||
|
self.events.put(next_event) # <14>
|
||||||
|
else: # <15>
|
||||||
|
msg = '*** end of simulation time: {} events pending ***'
|
||||||
|
print(msg.format(self.events.qsize()))
|
||||||
|
# END TAXI_SIMULATOR
|
||||||
|
|
||||||
|
|
||||||
|
def compute_duration(previous_action):
|
||||||
|
"""Compute action duration using exponential distribution"""
|
||||||
|
if previous_action in ['leave garage', 'drop off passenger']:
|
||||||
|
# new state is prowling
|
||||||
|
interval = SEARCH_DURATION
|
||||||
|
elif previous_action == 'pick up passenger':
|
||||||
|
# new state is trip
|
||||||
|
interval = TRIP_DURATION
|
||||||
|
elif previous_action == 'going home':
|
||||||
|
interval = 1
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown previous_action: %s' % previous_action)
|
||||||
|
return int(random.expovariate(1/interval)) + 1
|
||||||
|
|
||||||
|
|
||||||
|
def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS,
|
||||||
|
seed=None, delay=False):
|
||||||
|
"""Initialize random generator, build procs and run simulation"""
|
||||||
|
if seed is not None:
|
||||||
|
random.seed(seed) # get reproducible results
|
||||||
|
|
||||||
|
taxis = {i: taxi_process(i, (i+1)*2, i*DEPARTURE_INTERVAL)
|
||||||
|
for i in range(num_taxis)}
|
||||||
|
sim = Simulator(taxis)
|
||||||
|
sim.run(end_time, delay)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Taxi fleet simulator.')
|
||||||
|
parser.add_argument('-e', '--end-time', type=int,
|
||||||
|
default=DEFAULT_END_TIME,
|
||||||
|
help='simulation end time; default = %s'
|
||||||
|
% DEFAULT_END_TIME)
|
||||||
|
parser.add_argument('-t', '--taxis', type=int,
|
||||||
|
default=DEFAULT_NUMBER_OF_TAXIS,
|
||||||
|
help='number of taxis running; default = %s'
|
||||||
|
% DEFAULT_NUMBER_OF_TAXIS)
|
||||||
|
parser.add_argument('-s', '--seed', type=int, default=None,
|
||||||
|
help='random generator seed (for testing)')
|
||||||
|
parser.add_argument('-d', '--delay', action='store_true',
|
||||||
|
help='introduce delay proportional to simulation time')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
main(args.end_time, args.taxis, args.seed, args.delay)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
Sample run from the command line, seed=3, maximum elapsed time=120::
|
||||||
|
|
||||||
|
# BEGIN TAXI_SAMPLE_RUN
|
||||||
|
$ python3 taxi_sim.py -s 3 -e 120
|
||||||
|
taxi: 0 Event(time=0, proc=0, action='leave garage')
|
||||||
|
taxi: 0 Event(time=2, proc=0, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=5, proc=1, action='leave garage')
|
||||||
|
taxi: 1 Event(time=8, proc=1, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=10, proc=2, action='leave garage')
|
||||||
|
taxi: 2 Event(time=15, proc=2, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=17, proc=2, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=18, proc=0, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=18, proc=2, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=25, proc=2, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=27, proc=1, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=27, proc=2, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=28, proc=0, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=40, proc=2, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=44, proc=2, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=55, proc=1, action='pick up passenger')
|
||||||
|
taxi: 1 Event(time=59, proc=1, action='drop off passenger')
|
||||||
|
taxi: 0 Event(time=65, proc=0, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=65, proc=1, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=65, proc=2, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=72, proc=2, action='pick up passenger')
|
||||||
|
taxi: 0 Event(time=76, proc=0, action='going home')
|
||||||
|
taxi: 1 Event(time=80, proc=1, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=88, proc=1, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=95, proc=2, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=97, proc=2, action='pick up passenger')
|
||||||
|
taxi: 2 Event(time=98, proc=2, action='drop off passenger')
|
||||||
|
taxi: 1 Event(time=106, proc=1, action='drop off passenger')
|
||||||
|
taxi: 2 Event(time=109, proc=2, action='going home')
|
||||||
|
taxi: 1 Event(time=110, proc=1, action='going home')
|
||||||
|
*** end of events ***
|
||||||
|
# END TAXI_SAMPLE_RUN
|
||||||
|
|
||||||
|
"""
|
||||||
32
16-coroutine/yield_from_expansion_simplified.py
Normal file
32
16-coroutine/yield_from_expansion_simplified.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Code below is a very simplified expansion of the statement:
|
||||||
|
#
|
||||||
|
# RESULT = yield from EXPR
|
||||||
|
#
|
||||||
|
# This code assumes that the subgenerator will run to completion,
|
||||||
|
# without the client ever calling ``.throw()`` or ``.close()``.
|
||||||
|
# Also, this code makes no distinction between the client
|
||||||
|
# calling ``next(subgen)`` or ``subgen.send(...)``
|
||||||
|
#
|
||||||
|
# The full expansion is in:
|
||||||
|
# PEP 380 -- Syntax for Delegating to a Subgenerator
|
||||||
|
#
|
||||||
|
# https://www.python.org/dev/peps/pep-0380/#formal-semantics
|
||||||
|
|
||||||
|
|
||||||
|
# BEGIN YIELD_FROM_EXPANSION_SIMPLIFIED
|
||||||
|
_i = iter(EXPR) # <1>
|
||||||
|
try:
|
||||||
|
_y = next(_i) # <2>
|
||||||
|
except StopIteration as _e:
|
||||||
|
_r = _e.value # <3>
|
||||||
|
else:
|
||||||
|
while 1: # <4>
|
||||||
|
_s = yield _y # <5>
|
||||||
|
try:
|
||||||
|
_y = _i.send(_s) # <6>
|
||||||
|
except StopIteration as _e: # <7>
|
||||||
|
_r = _e.value
|
||||||
|
break
|
||||||
|
|
||||||
|
RESULT = _r # <8>
|
||||||
|
# END YIELD_FROM_EXPANSION_SIMPLIFIED
|
||||||
@@ -92,25 +92,23 @@ def downloader_coro(cc_list, base_url, verbose, concur_req): # <1>
|
|||||||
error_msg = exc.__cause__.args[0] # <10>
|
error_msg = exc.__cause__.args[0] # <10>
|
||||||
except IndexError:
|
except IndexError:
|
||||||
error_msg = exc.__cause__.__class__.__name__ # <11>
|
error_msg = exc.__cause__.__class__.__name__ # <11>
|
||||||
|
if verbose and error_msg:
|
||||||
|
msg = '*** Error for {}: {}'
|
||||||
|
print(msg.format(country_code, error_msg))
|
||||||
|
status = HTTPStatus.error
|
||||||
else:
|
else:
|
||||||
error_msg = ''
|
|
||||||
status = res.status
|
status = res.status
|
||||||
|
|
||||||
if error_msg: # <12>
|
counter[status] += 1 # <12>
|
||||||
status = HTTPStatus.error
|
|
||||||
counter[status] += 1
|
|
||||||
if verbose and error_msg:
|
|
||||||
msg = '*** Error for {}: {}'
|
|
||||||
print(msg.format(country_code, error_msg))
|
|
||||||
|
|
||||||
return counter
|
return counter # <13>
|
||||||
|
|
||||||
|
|
||||||
def download_many(cc_list, base_url, verbose, concur_req):
|
def download_many(cc_list, base_url, verbose, concur_req):
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
coro = downloader_coro(cc_list, base_url, verbose, concur_req)
|
coro = downloader_coro(cc_list, base_url, verbose, concur_req)
|
||||||
counts = loop.run_until_complete(coro) # <13>
|
counts = loop.run_until_complete(coro) # <14>
|
||||||
loop.close() # <14>
|
loop.close() # <15>
|
||||||
|
|
||||||
return counts
|
return counts
|
||||||
|
|
||||||
@@ -38,10 +38,10 @@ def download_many(cc_list, base_url, verbose, concur_req):
|
|||||||
future = executor.submit(download_one,
|
future = executor.submit(download_one,
|
||||||
cc, base_url, verbose) # <9>
|
cc, base_url, verbose) # <9>
|
||||||
to_do_map[future] = cc # <10>
|
to_do_map[future] = cc # <10>
|
||||||
to_do_iter = futures.as_completed(to_do_map) # <11>
|
done_iter = futures.as_completed(to_do_map) # <11>
|
||||||
if not verbose:
|
if not verbose:
|
||||||
to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <12>
|
done_iter = tqdm.tqdm(done_iter, total=len(cc_list)) # <12>
|
||||||
for future in to_do_iter: # <13>
|
for future in done_iter: # <13>
|
||||||
try:
|
try:
|
||||||
res = future.result() # <14>
|
res = future.result() # <14>
|
||||||
except requests.exceptions.HTTPError as exc: # <15>
|
except requests.exceptions.HTTPError as exc: # <15>
|
||||||
@@ -105,16 +105,14 @@ def downloader_coro(cc_list, base_url, verbose, concur_req):
|
|||||||
error_msg = exc.__cause__.args[0]
|
error_msg = exc.__cause__.args[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
error_msg = exc.__cause__.__class__.__name__
|
error_msg = exc.__cause__.__class__.__name__
|
||||||
|
if verbose and error_msg:
|
||||||
|
msg = '*** Error for {}: {}'
|
||||||
|
print(msg.format(country_code, error_msg))
|
||||||
|
status = HTTPStatus.error
|
||||||
else:
|
else:
|
||||||
error_msg = ''
|
|
||||||
status = res.status
|
status = res.status
|
||||||
|
|
||||||
if error_msg:
|
|
||||||
status = HTTPStatus.error
|
|
||||||
counter[status] += 1
|
counter[status] += 1
|
||||||
if verbose and error_msg:
|
|
||||||
msg = '*** Error for {}: {}'
|
|
||||||
print(msg.format(country_code, error_msg))
|
|
||||||
|
|
||||||
return counter
|
return counter
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ import itertools
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine # <1>
|
||||||
def spin(msg): # <1>
|
def spin(msg): # <2>
|
||||||
write, flush = sys.stdout.write, sys.stdout.flush
|
write, flush = sys.stdout.write, sys.stdout.flush
|
||||||
for char in itertools.cycle('|/-\\'):
|
for char in itertools.cycle('|/-\\'):
|
||||||
status = char + ' ' + msg
|
status = char + ' ' + msg
|
||||||
@@ -19,31 +19,31 @@ def spin(msg): # <1>
|
|||||||
flush()
|
flush()
|
||||||
write('\x08' * len(status))
|
write('\x08' * len(status))
|
||||||
try:
|
try:
|
||||||
yield from asyncio.sleep(.1) # <2>
|
yield from asyncio.sleep(.1) # <3>
|
||||||
except asyncio.CancelledError: # <3>
|
except asyncio.CancelledError: # <4>
|
||||||
break
|
break
|
||||||
write(' ' * len(status) + '\x08' * len(status))
|
write(' ' * len(status) + '\x08' * len(status))
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def slow_computation(): # <4>
|
def slow_function(): # <5>
|
||||||
# fake computation waiting a long time for I/O
|
# pretend waiting a long time for I/O
|
||||||
yield from asyncio.sleep(3) # <5>
|
yield from asyncio.sleep(3) # <6>
|
||||||
return 42
|
return 42
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def supervisor(): # <6>
|
def supervisor(): # <7>
|
||||||
spinner = asyncio.async(spin('thinking!')) # <7>
|
spinner = asyncio.async(spin('thinking!')) # <8>
|
||||||
print('spinner object:', spinner) # <8>
|
print('spinner object:', spinner) # <9>
|
||||||
result = yield from slow_computation() # <9>
|
result = yield from slow_function() # <10>
|
||||||
spinner.cancel() # <10>
|
spinner.cancel() # <11>
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
loop = asyncio.get_event_loop() # <11>
|
loop = asyncio.get_event_loop() # <12>
|
||||||
result = loop.run_until_complete(supervisor()) # <12>
|
result = loop.run_until_complete(supervisor()) # <13>
|
||||||
loop.close()
|
loop.close()
|
||||||
print('Answer:', result)
|
print('Answer:', result)
|
||||||
|
|
||||||
|
|||||||
@@ -28,19 +28,19 @@ def spin(msg, signal): # <2>
|
|||||||
write(' ' * len(status) + '\x08' * len(status)) # <6>
|
write(' ' * len(status) + '\x08' * len(status)) # <6>
|
||||||
|
|
||||||
|
|
||||||
def slow_computation(): # <7>
|
def slow_function(): # <7>
|
||||||
# fake computation waiting a long time for I/O
|
# pretend waiting a long time for I/O
|
||||||
time.sleep(3) # <8>
|
time.sleep(3) # <8>
|
||||||
return 42
|
return 42
|
||||||
|
|
||||||
|
|
||||||
def supervisor(): # <9>
|
def supervisor(): # <9>
|
||||||
signal = Signal()
|
signal = Signal()
|
||||||
spinner = threading.Thread(None, spin,
|
spinner = threading.Thread(target=spin,
|
||||||
args=('thinking!', signal))
|
args=('thinking!', signal))
|
||||||
print('spinner object:', spinner) # <10>
|
print('spinner object:', spinner) # <10>
|
||||||
spinner.start() # <11>
|
spinner.start() # <11>
|
||||||
result = slow_computation() # <12>
|
result = slow_function() # <12>
|
||||||
signal.go = False # <13>
|
signal.go = False # <13>
|
||||||
spinner.join() # <14>
|
spinner.join() # <14>
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
explore1.py: Script to explore the OSCON schedule feed
|
explore0.py: Script to explore the OSCON schedule feed
|
||||||
|
|
||||||
# BEGIN EXPLORE1_DEMO
|
# BEGIN EXPLORE0_DEMO
|
||||||
>>> from osconfeed import load
|
>>> from osconfeed import load
|
||||||
>>> raw_feed = load()
|
>>> raw_feed = load()
|
||||||
>>> feed = FrozenJSON(raw_feed) # <1>
|
>>> feed = FrozenJSON(raw_feed) # <1>
|
||||||
@@ -18,7 +18,9 @@ explore1.py: Script to explore the OSCON schedule feed
|
|||||||
53 venues
|
53 venues
|
||||||
>>> feed.Schedule.speakers[-1].name # <5>
|
>>> feed.Schedule.speakers[-1].name # <5>
|
||||||
'Carina C. Zona'
|
'Carina C. Zona'
|
||||||
>>> talk = feed.Schedule.events[40] # <6>
|
>>> talk = feed.Schedule.events[40]
|
||||||
|
>>> type(talk) # <6>
|
||||||
|
<class 'explore0.FrozenJSON'>
|
||||||
>>> talk.name
|
>>> talk.name
|
||||||
'There *Will* Be Bugs'
|
'There *Will* Be Bugs'
|
||||||
>>> talk.speakers # <7>
|
>>> talk.speakers # <7>
|
||||||
@@ -28,10 +30,11 @@ explore1.py: Script to explore the OSCON schedule feed
|
|||||||
...
|
...
|
||||||
KeyError: 'flavor'
|
KeyError: 'flavor'
|
||||||
|
|
||||||
# END EXPLORE1_DEMO
|
# END EXPLORE0_DEMO
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# BEGIN EXPLORE1
|
# BEGIN EXPLORE0
|
||||||
from collections import abc
|
from collections import abc
|
||||||
|
|
||||||
|
|
||||||
@@ -57,4 +60,4 @@ class FrozenJSON:
|
|||||||
return [cls.build(item) for item in obj]
|
return [cls.build(item) for item in obj]
|
||||||
else: # <8>
|
else: # <8>
|
||||||
return obj
|
return obj
|
||||||
# END EXPLORE1
|
# END EXPLORE0
|
||||||
78
19-dyn-attr-prop/oscon/explore1.py
Normal file
78
19-dyn-attr-prop/oscon/explore1.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
"""
|
||||||
|
explore1.py: Script to explore the OSCON schedule feed
|
||||||
|
|
||||||
|
>>> from osconfeed import load
|
||||||
|
>>> raw_feed = load()
|
||||||
|
>>> feed = FrozenJSON(raw_feed)
|
||||||
|
>>> len(feed.Schedule.speakers)
|
||||||
|
357
|
||||||
|
>>> sorted(feed.Schedule.keys())
|
||||||
|
['conferences', 'events', 'speakers', 'venues']
|
||||||
|
>>> for key, value in sorted(feed.Schedule.items()):
|
||||||
|
... print('{:3} {}'.format(len(value), key))
|
||||||
|
...
|
||||||
|
1 conferences
|
||||||
|
484 events
|
||||||
|
357 speakers
|
||||||
|
53 venues
|
||||||
|
>>> feed.Schedule.speakers[-1].name
|
||||||
|
'Carina C. Zona'
|
||||||
|
>>> talk = feed.Schedule.events[40]
|
||||||
|
>>> type(talk)
|
||||||
|
<class 'explore1.FrozenJSON'>
|
||||||
|
>>> talk.name
|
||||||
|
'There *Will* Be Bugs'
|
||||||
|
>>> talk.speakers
|
||||||
|
[3471, 5199]
|
||||||
|
>>> talk.flavor
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
KeyError: 'flavor'
|
||||||
|
|
||||||
|
Handle keywords by appending a `_`.
|
||||||
|
|
||||||
|
# BEGIN EXPLORE1_DEMO
|
||||||
|
|
||||||
|
>>> grad = FrozenJSON({'name': 'Jim Bo', 'class': 1982})
|
||||||
|
>>> grad.name
|
||||||
|
'Jim Bo'
|
||||||
|
>>> grad.class_
|
||||||
|
1991
|
||||||
|
|
||||||
|
# END EXPLORE1_DEMO
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections import abc
|
||||||
|
import keyword
|
||||||
|
|
||||||
|
|
||||||
|
class FrozenJSON:
|
||||||
|
"""A read-only façade for navigating a JSON-like object
|
||||||
|
using attribute notation
|
||||||
|
"""
|
||||||
|
|
||||||
|
# BEGIN EXPLORE1
|
||||||
|
def __init__(self, mapping):
|
||||||
|
self.__data = {}
|
||||||
|
for key, value in mapping.items():
|
||||||
|
if keyword.iskeyword(key): # <1>
|
||||||
|
key += '_'
|
||||||
|
self.__data[key] = value
|
||||||
|
# END EXPLORE1
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if hasattr(self.__data, name):
|
||||||
|
return getattr(self.__data, name)
|
||||||
|
else:
|
||||||
|
return FrozenJSON.build(self.__data[name])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build(cls, obj):
|
||||||
|
if isinstance(obj, abc.Mapping):
|
||||||
|
return cls(obj)
|
||||||
|
elif isinstance(obj, abc.MutableSequence):
|
||||||
|
return [cls.build(item) for item in obj]
|
||||||
|
else: # <8>
|
||||||
|
return obj
|
||||||
|
|
||||||
@@ -40,7 +40,11 @@ class FrozenJSON:
|
|||||||
return arg
|
return arg
|
||||||
|
|
||||||
def __init__(self, mapping):
|
def __init__(self, mapping):
|
||||||
self.__data = dict(mapping)
|
self.__data = {}
|
||||||
|
for key, value in mapping.items():
|
||||||
|
if iskeyword(key):
|
||||||
|
key += '_'
|
||||||
|
self.__data[key] = value
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if hasattr(self.__data, name):
|
if hasattr(self.__data, name):
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
What's New in Python 2.5 - PEP 342: New Generator Features
|
|
||||||
http://docs.python.org/release/2.5/whatsnew/pep-342.html
|
|
||||||
|
|
||||||
PEP 342 -- Coroutines via Enhanced Generators
|
|
||||||
http://www.python.org/dev/peps/pep-0342/
|
|
||||||
|
|
||||||
PEP 380 -- Syntax for Delegating to a Subgenerator
|
|
||||||
http://www.python.org/dev/peps/pep-0380/
|
|
||||||
|
|
||||||
Coroutines For the Working Python Developer
|
|
||||||
http://sdiehl.github.io/coroutine-tutorial/
|
|
||||||
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
|
|
||||||
"""
|
|
||||||
Taxi simulator
|
|
||||||
|
|
||||||
Sample run with two cars, random seed 10. This is a valid doctest.
|
|
||||||
|
|
||||||
>>> main(num_taxis=2, seed=10)
|
|
||||||
taxi: 0 Event(time=0, proc=0, action='leave garage')
|
|
||||||
taxi: 0 Event(time=4, proc=0, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=5, proc=1, action='leave garage')
|
|
||||||
taxi: 1 Event(time=9, proc=1, action='pick up passenger')
|
|
||||||
taxi: 0 Event(time=10, proc=0, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=12, proc=1, action='drop off passenger')
|
|
||||||
taxi: 0 Event(time=17, proc=0, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=19, proc=1, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=21, proc=1, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=24, proc=1, action='pick up passenger')
|
|
||||||
taxi: 0 Event(time=28, proc=0, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=28, proc=1, action='drop off passenger')
|
|
||||||
taxi: 0 Event(time=29, proc=0, action='going home')
|
|
||||||
taxi: 1 Event(time=30, proc=1, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=61, proc=1, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=62, proc=1, action='going home')
|
|
||||||
*** end of events ***
|
|
||||||
|
|
||||||
See explanation and longer sample run at the end of this module.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import random
|
|
||||||
import collections
|
|
||||||
import queue
|
|
||||||
import argparse
|
|
||||||
import time
|
|
||||||
|
|
||||||
DEFAULT_NUMBER_OF_TAXIS = 3
|
|
||||||
DEFAULT_END_TIME = 180
|
|
||||||
SEARCH_DURATION = 5
|
|
||||||
TRIP_DURATION = 20
|
|
||||||
DEPARTURE_INTERVAL = 5
|
|
||||||
|
|
||||||
Event = collections.namedtuple('Event', 'time proc action')
|
|
||||||
|
|
||||||
|
|
||||||
# BEGIN TAXI_PROCESS
|
|
||||||
def taxi_process(ident, trips, start_time=0): # <1>
|
|
||||||
"""Yield to simulator issuing event at each state change"""
|
|
||||||
time = yield Event(start_time, ident, 'leave garage') # <2>
|
|
||||||
for i in range(trips): # <3>
|
|
||||||
time = yield Event(time, ident, 'pick up passenger') # <4>
|
|
||||||
time = yield Event(time, ident, 'drop off passenger') # <5>
|
|
||||||
|
|
||||||
yield Event(time, ident, 'going home') # <6>
|
|
||||||
# end of taxi process # <7>
|
|
||||||
# END TAXI_PROCESS
|
|
||||||
|
|
||||||
|
|
||||||
# BEGIN TAXI_SIMULATOR
|
|
||||||
class Simulator:
|
|
||||||
|
|
||||||
def __init__(self, procs_map):
|
|
||||||
self.events = queue.PriorityQueue()
|
|
||||||
self.procs = dict(procs_map)
|
|
||||||
|
|
||||||
def run(self, end_time, delay=False): # <1>
|
|
||||||
"""Schedule and display events until time is up"""
|
|
||||||
# schedule the first event for each cab
|
|
||||||
for _, proc in sorted(self.procs.items()): # <2>
|
|
||||||
first_event = next(proc) # <3>
|
|
||||||
self.events.put(first_event) # <4>
|
|
||||||
|
|
||||||
# main loop of the simulation
|
|
||||||
sim_time = 0 # <5>
|
|
||||||
while sim_time < end_time: # <6>
|
|
||||||
if self.events.empty(): # <7>
|
|
||||||
print('*** end of events ***')
|
|
||||||
break
|
|
||||||
|
|
||||||
# get and display current event
|
|
||||||
current_event = self.events.get() # <8>
|
|
||||||
if delay:
|
|
||||||
time.sleep((current_event.time - sim_time) / 2)
|
|
||||||
# update the simulation time
|
|
||||||
sim_time, proc_id, previous_action = current_event
|
|
||||||
print('taxi:', proc_id, proc_id * ' ', current_event)
|
|
||||||
active_proc = self.procs[proc_id]
|
|
||||||
# schedule next action for current proc
|
|
||||||
next_time = sim_time + compute_duration(previous_action)
|
|
||||||
try:
|
|
||||||
next_event = active_proc.send(next_time) # <12>
|
|
||||||
except StopIteration:
|
|
||||||
del self.procs[proc_id] # <13>
|
|
||||||
else:
|
|
||||||
self.events.put(next_event) # <14>
|
|
||||||
else: # <15>
|
|
||||||
msg = '*** end of simulation time: {} events pending ***'
|
|
||||||
print(msg.format(self.events.qsize()))
|
|
||||||
# END TAXI_SIMULATOR
|
|
||||||
|
|
||||||
|
|
||||||
def compute_duration(previous_action):
|
|
||||||
"""Compute action duration using exponential distribution"""
|
|
||||||
if previous_action in ['leave garage', 'drop off passenger']:
|
|
||||||
# state is prowling
|
|
||||||
interval = SEARCH_DURATION
|
|
||||||
elif previous_action == 'pick up passenger':
|
|
||||||
# state is trip
|
|
||||||
interval = TRIP_DURATION
|
|
||||||
elif previous_action == 'going home':
|
|
||||||
interval = 1
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
return int(random.expovariate(1/interval)) + 1
|
|
||||||
|
|
||||||
|
|
||||||
def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS,
|
|
||||||
seed=None, delay=False):
|
|
||||||
"""Initialize random generator, build procs and run simulation"""
|
|
||||||
if seed is not None:
|
|
||||||
random.seed(seed) # get reproducible results
|
|
||||||
|
|
||||||
taxis = {i: taxi_process(i, (i+1)*2, i*DEPARTURE_INTERVAL)
|
|
||||||
for i in range(num_taxis)}
|
|
||||||
sim = Simulator(taxis)
|
|
||||||
sim.run(end_time, delay)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description='Taxi fleet simulator.')
|
|
||||||
parser.add_argument('-e', '--end-time', type=int,
|
|
||||||
default=DEFAULT_END_TIME,
|
|
||||||
help='simulation end time; default = %s'
|
|
||||||
% DEFAULT_END_TIME)
|
|
||||||
parser.add_argument('-t', '--taxis', type=int,
|
|
||||||
default=DEFAULT_NUMBER_OF_TAXIS,
|
|
||||||
help='number of taxis running; default = %s'
|
|
||||||
% DEFAULT_NUMBER_OF_TAXIS)
|
|
||||||
parser.add_argument('-s', '--seed', type=int, default=None,
|
|
||||||
help='random generator seed (for testing)')
|
|
||||||
parser.add_argument('-d', '--delay', action='store_true',
|
|
||||||
help='introduce delay proportional to simulation time')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
main(args.end_time, args.taxis, args.seed, args.delay)
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Notes for the ``taxi_process`` coroutine::
|
|
||||||
|
|
||||||
<1> `taxi_process` will be called once per taxi, creating a generator
|
|
||||||
object to represent its operations. `ident` is the number of the taxi
|
|
||||||
(eg. 0, 1, 2 in the sample run); `trips` is the number of trips this
|
|
||||||
taxi will make before going home; `start_time` is when the taxi
|
|
||||||
leaves the garage.
|
|
||||||
|
|
||||||
<2> The first `Event` yielded is `'leave garage'`. This suspends the
|
|
||||||
coroutine, and lets the simulation main loop proceed to the next
|
|
||||||
scheduled event. When it's time to reactivate this process, the main
|
|
||||||
loop will `send` the current simulation time, which is assigned to
|
|
||||||
`time`.
|
|
||||||
|
|
||||||
<3> This block will be repeated once for each trip.
|
|
||||||
|
|
||||||
<4> The ending time of the search for a passenger is computed.
|
|
||||||
|
|
||||||
<5> An `Event` signaling passenger pick up is yielded. The coroutine
|
|
||||||
pauses here. When the time comes to reactivate this coroutine,
|
|
||||||
the main loop will again `send` the current time.
|
|
||||||
|
|
||||||
<6> The ending time of the trip is computed, taking into account the
|
|
||||||
current `time`.
|
|
||||||
|
|
||||||
<7> An `Event` signaling passenger drop off is yielded. Coroutine
|
|
||||||
suspended again, waiting for the main loop to send the time of when
|
|
||||||
it's time to continue.
|
|
||||||
|
|
||||||
<8> The `for` loop ends after the given number of trips, and a final
|
|
||||||
`'going home'` event is yielded, to happen 1 minute after the current
|
|
||||||
time. The coroutine will suspend for the last time. When reactivated,
|
|
||||||
it will be sent the time from the simulation main loop, but here I
|
|
||||||
don't assign it to any variable because it will not be useful.
|
|
||||||
|
|
||||||
<9> When the coroutine falls off the end, the coroutine object raises
|
|
||||||
`StopIteration`.
|
|
||||||
|
|
||||||
|
|
||||||
Notes for the ``Simulator.run`` method::
|
|
||||||
|
|
||||||
<1> The simulation `end_time` is the only required argument for `run`.
|
|
||||||
|
|
||||||
<2> Use `sorted` to retrieve the `self.procs` items ordered by the
|
|
||||||
integer key; we don't care about the key, so assign it to `_`.
|
|
||||||
|
|
||||||
<3> `next(proc)` primes each coroutine by advancing it to the first
|
|
||||||
yield, so it's ready to be sent data. An `Event` is yielded.
|
|
||||||
|
|
||||||
<4> Add each event to the `self.events` `PriorityQueue`. The first
|
|
||||||
event for each taxi is `'leave garage'`, as seen in the sample run
|
|
||||||
(ex_taxi_process>>).
|
|
||||||
|
|
||||||
<5> Main loop of the simulation: run until the current `time` equals
|
|
||||||
or exceeds the `end_time`.
|
|
||||||
|
|
||||||
<6> The main loop may also exit if there are no pending events in the
|
|
||||||
queue.
|
|
||||||
|
|
||||||
<7> Get `Event` with the smallest `time` in the queue; this is the
|
|
||||||
`current_event`.
|
|
||||||
|
|
||||||
<8> Display the `Event`, identifying the taxi and adding indentation
|
|
||||||
according to the taxi id.
|
|
||||||
|
|
||||||
<9> Update the simulation time with the time of the `current_event`.
|
|
||||||
|
|
||||||
<10> Retrieve the coroutine for this taxi from the `self.procs`
|
|
||||||
dictionary.
|
|
||||||
|
|
||||||
<11> Send the `time` to the coroutine. The coroutine will yield the
|
|
||||||
`next_event` or raise `StopIteration` it's finished.
|
|
||||||
|
|
||||||
<12> If `StopIteration` was raised, delete the coroutine from the
|
|
||||||
`self.procs` dictionary.
|
|
||||||
|
|
||||||
<13> Otherwise, put the `next_event` in the queue.
|
|
||||||
|
|
||||||
<14> If the loop exits because the simulation time passed, display the
|
|
||||||
number of events pending (which may be zero by coincidence,
|
|
||||||
sometimes).
|
|
||||||
|
|
||||||
|
|
||||||
Sample run from the command line, seed=24, total elapsed time=160::
|
|
||||||
|
|
||||||
# BEGIN TAXI_SAMPLE_RUN
|
|
||||||
$ python3 taxi_sim.py -s 24 -e 160
|
|
||||||
taxi: 0 Event(time=0, proc=0, action='leave garage')
|
|
||||||
taxi: 0 Event(time=5, proc=0, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=5, proc=1, action='leave garage')
|
|
||||||
taxi: 1 Event(time=6, proc=1, action='pick up passenger')
|
|
||||||
taxi: 2 Event(time=10, proc=2, action='leave garage')
|
|
||||||
taxi: 2 Event(time=11, proc=2, action='pick up passenger')
|
|
||||||
taxi: 2 Event(time=23, proc=2, action='drop off passenger')
|
|
||||||
taxi: 0 Event(time=24, proc=0, action='drop off passenger')
|
|
||||||
taxi: 2 Event(time=24, proc=2, action='pick up passenger')
|
|
||||||
taxi: 2 Event(time=26, proc=2, action='drop off passenger')
|
|
||||||
taxi: 0 Event(time=30, proc=0, action='pick up passenger')
|
|
||||||
taxi: 2 Event(time=31, proc=2, action='pick up passenger')
|
|
||||||
taxi: 0 Event(time=43, proc=0, action='drop off passenger')
|
|
||||||
taxi: 0 Event(time=44, proc=0, action='going home')
|
|
||||||
taxi: 2 Event(time=46, proc=2, action='drop off passenger')
|
|
||||||
taxi: 2 Event(time=49, proc=2, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=70, proc=1, action='drop off passenger')
|
|
||||||
taxi: 2 Event(time=70, proc=2, action='drop off passenger')
|
|
||||||
taxi: 2 Event(time=71, proc=2, action='pick up passenger')
|
|
||||||
taxi: 2 Event(time=79, proc=2, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=88, proc=1, action='pick up passenger')
|
|
||||||
taxi: 2 Event(time=92, proc=2, action='pick up passenger')
|
|
||||||
taxi: 2 Event(time=98, proc=2, action='drop off passenger')
|
|
||||||
taxi: 2 Event(time=99, proc=2, action='going home')
|
|
||||||
taxi: 1 Event(time=102, proc=1, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=104, proc=1, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=135, proc=1, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=136, proc=1, action='pick up passenger')
|
|
||||||
taxi: 1 Event(time=151, proc=1, action='drop off passenger')
|
|
||||||
taxi: 1 Event(time=152, proc=1, action='going home')
|
|
||||||
*** end of events ***
|
|
||||||
# END TAXI_SAMPLE_RUN
|
|
||||||
|
|
||||||
"""
|
|
||||||
Reference in New Issue
Block a user