From f86aee27859fb6a3c4e6a94abfc61839d408e42d Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 15 Apr 2015 04:48:25 -0300 Subject: [PATCH] updated from Atlas --- {control => 15-context-mngr}/mirror.py | 2 + {control => 15-context-mngr}/mirror_gen.py | 0 .../mirror_gen_exc.py | 0 {control => 16-coroutine}/coro_exc_demo.py | 0 .../coro_finally_demo.py | 0 {control => 16-coroutine}/coroaverager0.py | 9 +- {control => 16-coroutine}/coroaverager1.py | 0 {control => 16-coroutine}/coroaverager2.py | 6 +- {control => 16-coroutine}/coroaverager3.py | 2 +- {control => 16-coroutine}/coroutil.py | 0 16-coroutine/taxi_sim.py | 203 +++++++++++++ .../taxi_sim.py => 16-coroutine/taxi_sim0.py | 0 16-coroutine/taxi_sim_delay.py | 215 ++++++++++++++ .../yield_from_expansion.py | 0 .../yield_from_expansion_simplified.py | 32 +++ .../countries/country_codes.txt | 0 {futures => 17-futures}/countries/flags.py | 0 {futures => 17-futures}/countries/flags.zip | Bin .../countries/flags2_asyncio.py | 18 +- .../countries/flags2_asyncio_executor.py | 0 .../countries/flags2_common.py | 0 .../countries/flags2_sequential.py | 0 .../countries/flags2_threadpool.py | 6 +- .../countries/flags3_asyncio.py | 10 +- .../countries/flags3_threadpool.py | 0 .../countries/flags_asyncio.py | 0 .../countries/flags_threadpool.py | 0 .../countries/flags_threadpool_ac.py | 0 .../countries/vaurien_delay.sh | 0 .../countries/vaurien_error_delay.sh | 0 {futures => 17-futures}/demo_executor_map.py | 0 .../charfinder/charfinder.py | 0 .../charfinder/http_charfinder.html | 0 .../charfinder/http_charfinder.py | 0 .../charfinder/tcp_charfinder.py | 0 .../charfinder/test_charfinder.py | 0 18-asyncio/spinner_asyncio.py | 28 +- 18-asyncio/spinner_thread.py | 8 +- {metaprog => 19-dyn-attr-prop}/blackknight.py | 0 .../bulkfood}/bulkfood_v1.py | 0 .../bulkfood}/bulkfood_v2.py | 0 .../bulkfood}/bulkfood_v2b.py | 0 .../bulkfood}/bulkfood_v2prop.py | 0 .../doc_property.py | 0 .../oscon}/data/osconfeed.json | 0 .../oscon/explore0.py | 15 +- 19-dyn-attr-prop/oscon/explore1.py | 78 +++++ .../oscon}/explore2.py | 6 +- .../oscon}/osconfeed-sample.json | 0 .../oscon}/osconfeed.py | 0 .../oscon}/schedule1.py | 0 .../oscon}/schedule2.py | 0 {control => attic/control}/adder/coroadder.py | 0 .../control}/adder/coroadder0.py | 0 .../control}/adder/coroadder_deco.py | 0 {control => attic/control}/adder/soma.py | 0 {control => attic/control}/adder/soma_deco.py | 0 .../control}/adder/yetanother.py | 0 .../control}/adder/yield_from_input.py | 0 {control => attic/control}/coro_demo.rst | 0 .../control}/coro_simple_demo.rst | 0 {control => attic/control}/coroaverager.py | 0 {control => attic/control}/countdown_yf.py | 0 {control => attic/control}/demo_coro.py | 0 {control => attic/control}/exemplo0.py | 0 {control => attic/control}/exemplo1.py | 0 {control => attic/control}/flatten.py | 0 {control => attic/control}/guido/guido0.py | 0 {control => attic/control}/guido/guido1.py | 0 {control => attic/control}/guido/guido1b.py | 0 {control => attic/control}/guido/guido2.py | 0 {control => attic/control}/guido/guido3.py | 0 {control => attic/control}/http_cli0.py | 0 {control => attic/control}/kwcombos.py | 0 .../iterables}/aritprog_v4.py | 0 .../iterables}/aritprog_v5.py | 0 control/referencias.txt | 12 - control/taxi_sim2.py | 270 ------------------ 78 files changed, 587 insertions(+), 333 deletions(-) rename {control => 15-context-mngr}/mirror.py (97%) rename {control => 15-context-mngr}/mirror_gen.py (100%) rename {control => 15-context-mngr}/mirror_gen_exc.py (100%) rename {control => 16-coroutine}/coro_exc_demo.py (100%) rename {control => 16-coroutine}/coro_finally_demo.py (100%) rename {control => 16-coroutine}/coroaverager0.py (78%) rename {control => 16-coroutine}/coroaverager1.py (100%) rename {control => 16-coroutine}/coroaverager2.py (93%) rename {control => 16-coroutine}/coroaverager3.py (98%) rename {control => 16-coroutine}/coroutil.py (100%) create mode 100644 16-coroutine/taxi_sim.py rename control/taxi_sim.py => 16-coroutine/taxi_sim0.py (100%) create mode 100644 16-coroutine/taxi_sim_delay.py rename {control => 16-coroutine}/yield_from_expansion.py (100%) create mode 100644 16-coroutine/yield_from_expansion_simplified.py rename {futures => 17-futures}/countries/country_codes.txt (100%) rename {futures => 17-futures}/countries/flags.py (100%) rename {futures => 17-futures}/countries/flags.zip (100%) rename {futures => 17-futures}/countries/flags2_asyncio.py (90%) rename {futures => 17-futures}/countries/flags2_asyncio_executor.py (100%) rename {futures => 17-futures}/countries/flags2_common.py (100%) rename {futures => 17-futures}/countries/flags2_sequential.py (100%) rename {futures => 17-futures}/countries/flags2_threadpool.py (91%) rename {futures => 17-futures}/countries/flags3_asyncio.py (95%) rename {futures => 17-futures}/countries/flags3_threadpool.py (100%) rename {futures => 17-futures}/countries/flags_asyncio.py (100%) rename {futures => 17-futures}/countries/flags_threadpool.py (100%) rename {futures => 17-futures}/countries/flags_threadpool_ac.py (100%) rename {futures => 17-futures}/countries/vaurien_delay.sh (100%) rename {futures => 17-futures}/countries/vaurien_error_delay.sh (100%) rename {futures => 17-futures}/demo_executor_map.py (100%) rename {futures => 18-asyncio}/charfinder/charfinder.py (100%) rename {futures => 18-asyncio}/charfinder/http_charfinder.html (100%) rename {futures => 18-asyncio}/charfinder/http_charfinder.py (100%) rename {futures => 18-asyncio}/charfinder/tcp_charfinder.py (100%) rename {futures => 18-asyncio}/charfinder/test_charfinder.py (100%) rename {metaprog => 19-dyn-attr-prop}/blackknight.py (100%) rename {descriptors => 19-dyn-attr-prop/bulkfood}/bulkfood_v1.py (100%) rename {descriptors => 19-dyn-attr-prop/bulkfood}/bulkfood_v2.py (100%) rename {descriptors => 19-dyn-attr-prop/bulkfood}/bulkfood_v2b.py (100%) rename {descriptors => 19-dyn-attr-prop/bulkfood}/bulkfood_v2prop.py (100%) rename {metaprog => 19-dyn-attr-prop}/doc_property.py (100%) rename {metaprog/oscon-schedule => 19-dyn-attr-prop/oscon}/data/osconfeed.json (100%) rename metaprog/oscon-schedule/explore1.py => 19-dyn-attr-prop/oscon/explore0.py (86%) create mode 100644 19-dyn-attr-prop/oscon/explore1.py rename {metaprog/oscon-schedule => 19-dyn-attr-prop/oscon}/explore2.py (88%) rename {metaprog/oscon-schedule => 19-dyn-attr-prop/oscon}/osconfeed-sample.json (100%) rename {metaprog/oscon-schedule => 19-dyn-attr-prop/oscon}/osconfeed.py (100%) rename {metaprog/oscon-schedule => 19-dyn-attr-prop/oscon}/schedule1.py (100%) rename {metaprog/oscon-schedule => 19-dyn-attr-prop/oscon}/schedule2.py (100%) rename {control => attic/control}/adder/coroadder.py (100%) rename {control => attic/control}/adder/coroadder0.py (100%) rename {control => attic/control}/adder/coroadder_deco.py (100%) rename {control => attic/control}/adder/soma.py (100%) rename {control => attic/control}/adder/soma_deco.py (100%) rename {control => attic/control}/adder/yetanother.py (100%) rename {control => attic/control}/adder/yield_from_input.py (100%) rename {control => attic/control}/coro_demo.rst (100%) rename {control => attic/control}/coro_simple_demo.rst (100%) rename {control => attic/control}/coroaverager.py (100%) rename {control => attic/control}/countdown_yf.py (100%) rename {control => attic/control}/demo_coro.py (100%) rename {control => attic/control}/exemplo0.py (100%) rename {control => attic/control}/exemplo1.py (100%) rename {control => attic/control}/flatten.py (100%) rename {control => attic/control}/guido/guido0.py (100%) rename {control => attic/control}/guido/guido1.py (100%) rename {control => attic/control}/guido/guido1b.py (100%) rename {control => attic/control}/guido/guido2.py (100%) rename {control => attic/control}/guido/guido3.py (100%) rename {control => attic/control}/http_cli0.py (100%) rename {control => attic/control}/kwcombos.py (100%) rename {14-it-generator => attic/iterables}/aritprog_v4.py (100%) rename {14-it-generator => attic/iterables}/aritprog_v5.py (100%) delete mode 100644 control/referencias.txt delete mode 100644 control/taxi_sim2.py diff --git a/control/mirror.py b/15-context-mngr/mirror.py similarity index 97% rename from control/mirror.py rename to 15-context-mngr/mirror.py index 27f6d75..67782ff 100644 --- a/control/mirror.py +++ b/15-context-mngr/mirror.py @@ -15,6 +15,8 @@ While active, the context manager reverses text output to YKCOWREBBAJ >>> what # <4> 'JABBERWOCKY' + >>> print('Back to normal.') # <5> + Back to normal. # END MIRROR_DEMO_1 diff --git a/control/mirror_gen.py b/15-context-mngr/mirror_gen.py similarity index 100% rename from control/mirror_gen.py rename to 15-context-mngr/mirror_gen.py diff --git a/control/mirror_gen_exc.py b/15-context-mngr/mirror_gen_exc.py similarity index 100% rename from control/mirror_gen_exc.py rename to 15-context-mngr/mirror_gen_exc.py diff --git a/control/coro_exc_demo.py b/16-coroutine/coro_exc_demo.py similarity index 100% rename from control/coro_exc_demo.py rename to 16-coroutine/coro_exc_demo.py diff --git a/control/coro_finally_demo.py b/16-coroutine/coro_finally_demo.py similarity index 100% rename from control/coro_finally_demo.py rename to 16-coroutine/coro_finally_demo.py diff --git a/control/coroaverager0.py b/16-coroutine/coroaverager0.py similarity index 78% rename from control/coroaverager0.py rename to 16-coroutine/coroaverager0.py index f3713dc..440a521 100644 --- a/control/coroaverager0.py +++ b/16-coroutine/coroaverager0.py @@ -1,7 +1,7 @@ -# BEGIN CORO_AVERAGER """ A coroutine to compute a running average +# BEGIN CORO_AVERAGER_TEST >>> coro_avg = averager() # <1> >>> next(coro_avg) # <2> >>> coro_avg.send(10) # <3> @@ -11,14 +11,17 @@ A coroutine to compute a running average >>> coro_avg.send(5) 15.0 +# END CORO_AVERAGER_TEST + """ +# BEGIN CORO_AVERAGER def averager(): total = 0.0 count = 0 average = None - while True: - term = yield average # <4> + while True: # <1> + term = yield average # <2> total += term count += 1 average = total/count diff --git a/control/coroaverager1.py b/16-coroutine/coroaverager1.py similarity index 100% rename from control/coroaverager1.py rename to 16-coroutine/coroaverager1.py diff --git a/control/coroaverager2.py b/16-coroutine/coroaverager2.py similarity index 93% rename from control/coroaverager2.py rename to 16-coroutine/coroaverager2.py index b3d6244..8f52300 100644 --- a/control/coroaverager2.py +++ b/16-coroutine/coroaverager2.py @@ -6,8 +6,8 @@ Testing ``averager`` by itself:: # BEGIN RETURNING_AVERAGER_DEMO1 >>> coro_avg = averager() - >>> next(coro_avg) # <1> - >>> coro_avg.send(10) + >>> next(coro_avg) + >>> coro_avg.send(10) # <1> >>> coro_avg.send(30) >>> coro_avg.send(6.5) >>> coro_avg.send(None) # <2> @@ -58,4 +58,4 @@ def averager(): count += 1 average = total/count return Result(count, average) # <2> -# END RETURNING_AVERAGER \ No newline at end of file +# END RETURNING_AVERAGER diff --git a/control/coroaverager3.py b/16-coroutine/coroaverager3.py similarity index 98% rename from control/coroaverager3.py rename to 16-coroutine/coroaverager3.py index f2b419b..c89a3fd 100644 --- a/control/coroaverager3.py +++ b/16-coroutine/coroaverager3.py @@ -75,7 +75,7 @@ def main(data): # <8> next(group) # <10> for value in values: group.send(value) # <11> - group.send(None) # <12> + group.send(None) # important! <12> # print(results) # uncomment to debug report(results) diff --git a/control/coroutil.py b/16-coroutine/coroutil.py similarity index 100% rename from control/coroutil.py rename to 16-coroutine/coroutil.py diff --git a/16-coroutine/taxi_sim.py b/16-coroutine/taxi_sim.py new file mode 100644 index 0000000..705418a --- /dev/null +++ b/16-coroutine/taxi_sim.py @@ -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 "", line 1, in + 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 + +""" diff --git a/control/taxi_sim.py b/16-coroutine/taxi_sim0.py similarity index 100% rename from control/taxi_sim.py rename to 16-coroutine/taxi_sim0.py diff --git a/16-coroutine/taxi_sim_delay.py b/16-coroutine/taxi_sim_delay.py new file mode 100644 index 0000000..8f38e4f --- /dev/null +++ b/16-coroutine/taxi_sim_delay.py @@ -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 "", line 1, in + 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 + +""" diff --git a/control/yield_from_expansion.py b/16-coroutine/yield_from_expansion.py similarity index 100% rename from control/yield_from_expansion.py rename to 16-coroutine/yield_from_expansion.py diff --git a/16-coroutine/yield_from_expansion_simplified.py b/16-coroutine/yield_from_expansion_simplified.py new file mode 100644 index 0000000..2da7fe0 --- /dev/null +++ b/16-coroutine/yield_from_expansion_simplified.py @@ -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 diff --git a/futures/countries/country_codes.txt b/17-futures/countries/country_codes.txt similarity index 100% rename from futures/countries/country_codes.txt rename to 17-futures/countries/country_codes.txt diff --git a/futures/countries/flags.py b/17-futures/countries/flags.py similarity index 100% rename from futures/countries/flags.py rename to 17-futures/countries/flags.py diff --git a/futures/countries/flags.zip b/17-futures/countries/flags.zip similarity index 100% rename from futures/countries/flags.zip rename to 17-futures/countries/flags.zip diff --git a/futures/countries/flags2_asyncio.py b/17-futures/countries/flags2_asyncio.py similarity index 90% rename from futures/countries/flags2_asyncio.py rename to 17-futures/countries/flags2_asyncio.py index f07c12f..a74c975 100644 --- a/futures/countries/flags2_asyncio.py +++ b/17-futures/countries/flags2_asyncio.py @@ -92,25 +92,23 @@ def downloader_coro(cc_list, base_url, verbose, concur_req): # <1> error_msg = exc.__cause__.args[0] # <10> except IndexError: 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: - error_msg = '' status = res.status - if error_msg: # <12> - status = HTTPStatus.error - counter[status] += 1 - if verbose and error_msg: - msg = '*** Error for {}: {}' - print(msg.format(country_code, error_msg)) + counter[status] += 1 # <12> - return counter + return counter # <13> def download_many(cc_list, base_url, verbose, concur_req): loop = asyncio.get_event_loop() coro = downloader_coro(cc_list, base_url, verbose, concur_req) - counts = loop.run_until_complete(coro) # <13> - loop.close() # <14> + counts = loop.run_until_complete(coro) # <14> + loop.close() # <15> return counts diff --git a/futures/countries/flags2_asyncio_executor.py b/17-futures/countries/flags2_asyncio_executor.py similarity index 100% rename from futures/countries/flags2_asyncio_executor.py rename to 17-futures/countries/flags2_asyncio_executor.py diff --git a/futures/countries/flags2_common.py b/17-futures/countries/flags2_common.py similarity index 100% rename from futures/countries/flags2_common.py rename to 17-futures/countries/flags2_common.py diff --git a/futures/countries/flags2_sequential.py b/17-futures/countries/flags2_sequential.py similarity index 100% rename from futures/countries/flags2_sequential.py rename to 17-futures/countries/flags2_sequential.py diff --git a/futures/countries/flags2_threadpool.py b/17-futures/countries/flags2_threadpool.py similarity index 91% rename from futures/countries/flags2_threadpool.py rename to 17-futures/countries/flags2_threadpool.py index b11ccb8..5808780 100644 --- a/futures/countries/flags2_threadpool.py +++ b/17-futures/countries/flags2_threadpool.py @@ -38,10 +38,10 @@ def download_many(cc_list, base_url, verbose, concur_req): future = executor.submit(download_one, cc, base_url, verbose) # <9> 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: - to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <12> - for future in to_do_iter: # <13> + done_iter = tqdm.tqdm(done_iter, total=len(cc_list)) # <12> + for future in done_iter: # <13> try: res = future.result() # <14> except requests.exceptions.HTTPError as exc: # <15> diff --git a/futures/countries/flags3_asyncio.py b/17-futures/countries/flags3_asyncio.py similarity index 95% rename from futures/countries/flags3_asyncio.py rename to 17-futures/countries/flags3_asyncio.py index af8be50..f3961a1 100644 --- a/futures/countries/flags3_asyncio.py +++ b/17-futures/countries/flags3_asyncio.py @@ -105,16 +105,14 @@ def downloader_coro(cc_list, base_url, verbose, concur_req): error_msg = exc.__cause__.args[0] except IndexError: 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: - error_msg = '' status = res.status - if error_msg: - status = HTTPStatus.error counter[status] += 1 - if verbose and error_msg: - msg = '*** Error for {}: {}' - print(msg.format(country_code, error_msg)) return counter diff --git a/futures/countries/flags3_threadpool.py b/17-futures/countries/flags3_threadpool.py similarity index 100% rename from futures/countries/flags3_threadpool.py rename to 17-futures/countries/flags3_threadpool.py diff --git a/futures/countries/flags_asyncio.py b/17-futures/countries/flags_asyncio.py similarity index 100% rename from futures/countries/flags_asyncio.py rename to 17-futures/countries/flags_asyncio.py diff --git a/futures/countries/flags_threadpool.py b/17-futures/countries/flags_threadpool.py similarity index 100% rename from futures/countries/flags_threadpool.py rename to 17-futures/countries/flags_threadpool.py diff --git a/futures/countries/flags_threadpool_ac.py b/17-futures/countries/flags_threadpool_ac.py similarity index 100% rename from futures/countries/flags_threadpool_ac.py rename to 17-futures/countries/flags_threadpool_ac.py diff --git a/futures/countries/vaurien_delay.sh b/17-futures/countries/vaurien_delay.sh similarity index 100% rename from futures/countries/vaurien_delay.sh rename to 17-futures/countries/vaurien_delay.sh diff --git a/futures/countries/vaurien_error_delay.sh b/17-futures/countries/vaurien_error_delay.sh similarity index 100% rename from futures/countries/vaurien_error_delay.sh rename to 17-futures/countries/vaurien_error_delay.sh diff --git a/futures/demo_executor_map.py b/17-futures/demo_executor_map.py similarity index 100% rename from futures/demo_executor_map.py rename to 17-futures/demo_executor_map.py diff --git a/futures/charfinder/charfinder.py b/18-asyncio/charfinder/charfinder.py similarity index 100% rename from futures/charfinder/charfinder.py rename to 18-asyncio/charfinder/charfinder.py diff --git a/futures/charfinder/http_charfinder.html b/18-asyncio/charfinder/http_charfinder.html similarity index 100% rename from futures/charfinder/http_charfinder.html rename to 18-asyncio/charfinder/http_charfinder.html diff --git a/futures/charfinder/http_charfinder.py b/18-asyncio/charfinder/http_charfinder.py similarity index 100% rename from futures/charfinder/http_charfinder.py rename to 18-asyncio/charfinder/http_charfinder.py diff --git a/futures/charfinder/tcp_charfinder.py b/18-asyncio/charfinder/tcp_charfinder.py similarity index 100% rename from futures/charfinder/tcp_charfinder.py rename to 18-asyncio/charfinder/tcp_charfinder.py diff --git a/futures/charfinder/test_charfinder.py b/18-asyncio/charfinder/test_charfinder.py similarity index 100% rename from futures/charfinder/test_charfinder.py rename to 18-asyncio/charfinder/test_charfinder.py diff --git a/18-asyncio/spinner_asyncio.py b/18-asyncio/spinner_asyncio.py index 7c500e0..3366998 100644 --- a/18-asyncio/spinner_asyncio.py +++ b/18-asyncio/spinner_asyncio.py @@ -10,8 +10,8 @@ import itertools import sys -@asyncio.coroutine -def spin(msg): # <1> +@asyncio.coroutine # <1> +def spin(msg): # <2> write, flush = sys.stdout.write, sys.stdout.flush for char in itertools.cycle('|/-\\'): status = char + ' ' + msg @@ -19,31 +19,31 @@ def spin(msg): # <1> flush() write('\x08' * len(status)) try: - yield from asyncio.sleep(.1) # <2> - except asyncio.CancelledError: # <3> + yield from asyncio.sleep(.1) # <3> + except asyncio.CancelledError: # <4> break write(' ' * len(status) + '\x08' * len(status)) @asyncio.coroutine -def slow_computation(): # <4> - # fake computation waiting a long time for I/O - yield from asyncio.sleep(3) # <5> +def slow_function(): # <5> + # pretend waiting a long time for I/O + yield from asyncio.sleep(3) # <6> return 42 @asyncio.coroutine -def supervisor(): # <6> - spinner = asyncio.async(spin('thinking!')) # <7> - print('spinner object:', spinner) # <8> - result = yield from slow_computation() # <9> - spinner.cancel() # <10> +def supervisor(): # <7> + spinner = asyncio.async(spin('thinking!')) # <8> + print('spinner object:', spinner) # <9> + result = yield from slow_function() # <10> + spinner.cancel() # <11> return result def main(): - loop = asyncio.get_event_loop() # <11> - result = loop.run_until_complete(supervisor()) # <12> + loop = asyncio.get_event_loop() # <12> + result = loop.run_until_complete(supervisor()) # <13> loop.close() print('Answer:', result) diff --git a/18-asyncio/spinner_thread.py b/18-asyncio/spinner_thread.py index af80ec6..23feb8f 100644 --- a/18-asyncio/spinner_thread.py +++ b/18-asyncio/spinner_thread.py @@ -28,19 +28,19 @@ def spin(msg, signal): # <2> write(' ' * len(status) + '\x08' * len(status)) # <6> -def slow_computation(): # <7> - # fake computation waiting a long time for I/O +def slow_function(): # <7> + # pretend waiting a long time for I/O time.sleep(3) # <8> return 42 def supervisor(): # <9> signal = Signal() - spinner = threading.Thread(None, spin, + spinner = threading.Thread(target=spin, args=('thinking!', signal)) print('spinner object:', spinner) # <10> spinner.start() # <11> - result = slow_computation() # <12> + result = slow_function() # <12> signal.go = False # <13> spinner.join() # <14> return result diff --git a/metaprog/blackknight.py b/19-dyn-attr-prop/blackknight.py similarity index 100% rename from metaprog/blackknight.py rename to 19-dyn-attr-prop/blackknight.py diff --git a/descriptors/bulkfood_v1.py b/19-dyn-attr-prop/bulkfood/bulkfood_v1.py similarity index 100% rename from descriptors/bulkfood_v1.py rename to 19-dyn-attr-prop/bulkfood/bulkfood_v1.py diff --git a/descriptors/bulkfood_v2.py b/19-dyn-attr-prop/bulkfood/bulkfood_v2.py similarity index 100% rename from descriptors/bulkfood_v2.py rename to 19-dyn-attr-prop/bulkfood/bulkfood_v2.py diff --git a/descriptors/bulkfood_v2b.py b/19-dyn-attr-prop/bulkfood/bulkfood_v2b.py similarity index 100% rename from descriptors/bulkfood_v2b.py rename to 19-dyn-attr-prop/bulkfood/bulkfood_v2b.py diff --git a/descriptors/bulkfood_v2prop.py b/19-dyn-attr-prop/bulkfood/bulkfood_v2prop.py similarity index 100% rename from descriptors/bulkfood_v2prop.py rename to 19-dyn-attr-prop/bulkfood/bulkfood_v2prop.py diff --git a/metaprog/doc_property.py b/19-dyn-attr-prop/doc_property.py similarity index 100% rename from metaprog/doc_property.py rename to 19-dyn-attr-prop/doc_property.py diff --git a/metaprog/oscon-schedule/data/osconfeed.json b/19-dyn-attr-prop/oscon/data/osconfeed.json similarity index 100% rename from metaprog/oscon-schedule/data/osconfeed.json rename to 19-dyn-attr-prop/oscon/data/osconfeed.json diff --git a/metaprog/oscon-schedule/explore1.py b/19-dyn-attr-prop/oscon/explore0.py similarity index 86% rename from metaprog/oscon-schedule/explore1.py rename to 19-dyn-attr-prop/oscon/explore0.py index 49a95f8..9dc589d 100644 --- a/metaprog/oscon-schedule/explore1.py +++ b/19-dyn-attr-prop/oscon/explore0.py @@ -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 >>> raw_feed = load() >>> feed = FrozenJSON(raw_feed) # <1> @@ -18,7 +18,9 @@ explore1.py: Script to explore the OSCON schedule feed 53 venues >>> feed.Schedule.speakers[-1].name # <5> 'Carina C. Zona' - >>> talk = feed.Schedule.events[40] # <6> + >>> talk = feed.Schedule.events[40] + >>> type(talk) # <6> + >>> talk.name 'There *Will* Be Bugs' >>> talk.speakers # <7> @@ -28,10 +30,11 @@ explore1.py: Script to explore the OSCON schedule feed ... KeyError: 'flavor' -# END EXPLORE1_DEMO +# END EXPLORE0_DEMO + """ -# BEGIN EXPLORE1 +# BEGIN EXPLORE0 from collections import abc @@ -57,4 +60,4 @@ class FrozenJSON: return [cls.build(item) for item in obj] else: # <8> return obj -# END EXPLORE1 +# END EXPLORE0 diff --git a/19-dyn-attr-prop/oscon/explore1.py b/19-dyn-attr-prop/oscon/explore1.py new file mode 100644 index 0000000..5ef0968 --- /dev/null +++ b/19-dyn-attr-prop/oscon/explore1.py @@ -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) + + >>> 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 + diff --git a/metaprog/oscon-schedule/explore2.py b/19-dyn-attr-prop/oscon/explore2.py similarity index 88% rename from metaprog/oscon-schedule/explore2.py rename to 19-dyn-attr-prop/oscon/explore2.py index 19040d2..8d0088c 100644 --- a/metaprog/oscon-schedule/explore2.py +++ b/19-dyn-attr-prop/oscon/explore2.py @@ -40,7 +40,11 @@ class FrozenJSON: return arg 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): if hasattr(self.__data, name): diff --git a/metaprog/oscon-schedule/osconfeed-sample.json b/19-dyn-attr-prop/oscon/osconfeed-sample.json similarity index 100% rename from metaprog/oscon-schedule/osconfeed-sample.json rename to 19-dyn-attr-prop/oscon/osconfeed-sample.json diff --git a/metaprog/oscon-schedule/osconfeed.py b/19-dyn-attr-prop/oscon/osconfeed.py similarity index 100% rename from metaprog/oscon-schedule/osconfeed.py rename to 19-dyn-attr-prop/oscon/osconfeed.py diff --git a/metaprog/oscon-schedule/schedule1.py b/19-dyn-attr-prop/oscon/schedule1.py similarity index 100% rename from metaprog/oscon-schedule/schedule1.py rename to 19-dyn-attr-prop/oscon/schedule1.py diff --git a/metaprog/oscon-schedule/schedule2.py b/19-dyn-attr-prop/oscon/schedule2.py similarity index 100% rename from metaprog/oscon-schedule/schedule2.py rename to 19-dyn-attr-prop/oscon/schedule2.py diff --git a/control/adder/coroadder.py b/attic/control/adder/coroadder.py similarity index 100% rename from control/adder/coroadder.py rename to attic/control/adder/coroadder.py diff --git a/control/adder/coroadder0.py b/attic/control/adder/coroadder0.py similarity index 100% rename from control/adder/coroadder0.py rename to attic/control/adder/coroadder0.py diff --git a/control/adder/coroadder_deco.py b/attic/control/adder/coroadder_deco.py similarity index 100% rename from control/adder/coroadder_deco.py rename to attic/control/adder/coroadder_deco.py diff --git a/control/adder/soma.py b/attic/control/adder/soma.py similarity index 100% rename from control/adder/soma.py rename to attic/control/adder/soma.py diff --git a/control/adder/soma_deco.py b/attic/control/adder/soma_deco.py similarity index 100% rename from control/adder/soma_deco.py rename to attic/control/adder/soma_deco.py diff --git a/control/adder/yetanother.py b/attic/control/adder/yetanother.py similarity index 100% rename from control/adder/yetanother.py rename to attic/control/adder/yetanother.py diff --git a/control/adder/yield_from_input.py b/attic/control/adder/yield_from_input.py similarity index 100% rename from control/adder/yield_from_input.py rename to attic/control/adder/yield_from_input.py diff --git a/control/coro_demo.rst b/attic/control/coro_demo.rst similarity index 100% rename from control/coro_demo.rst rename to attic/control/coro_demo.rst diff --git a/control/coro_simple_demo.rst b/attic/control/coro_simple_demo.rst similarity index 100% rename from control/coro_simple_demo.rst rename to attic/control/coro_simple_demo.rst diff --git a/control/coroaverager.py b/attic/control/coroaverager.py similarity index 100% rename from control/coroaverager.py rename to attic/control/coroaverager.py diff --git a/control/countdown_yf.py b/attic/control/countdown_yf.py similarity index 100% rename from control/countdown_yf.py rename to attic/control/countdown_yf.py diff --git a/control/demo_coro.py b/attic/control/demo_coro.py similarity index 100% rename from control/demo_coro.py rename to attic/control/demo_coro.py diff --git a/control/exemplo0.py b/attic/control/exemplo0.py similarity index 100% rename from control/exemplo0.py rename to attic/control/exemplo0.py diff --git a/control/exemplo1.py b/attic/control/exemplo1.py similarity index 100% rename from control/exemplo1.py rename to attic/control/exemplo1.py diff --git a/control/flatten.py b/attic/control/flatten.py similarity index 100% rename from control/flatten.py rename to attic/control/flatten.py diff --git a/control/guido/guido0.py b/attic/control/guido/guido0.py similarity index 100% rename from control/guido/guido0.py rename to attic/control/guido/guido0.py diff --git a/control/guido/guido1.py b/attic/control/guido/guido1.py similarity index 100% rename from control/guido/guido1.py rename to attic/control/guido/guido1.py diff --git a/control/guido/guido1b.py b/attic/control/guido/guido1b.py similarity index 100% rename from control/guido/guido1b.py rename to attic/control/guido/guido1b.py diff --git a/control/guido/guido2.py b/attic/control/guido/guido2.py similarity index 100% rename from control/guido/guido2.py rename to attic/control/guido/guido2.py diff --git a/control/guido/guido3.py b/attic/control/guido/guido3.py similarity index 100% rename from control/guido/guido3.py rename to attic/control/guido/guido3.py diff --git a/control/http_cli0.py b/attic/control/http_cli0.py similarity index 100% rename from control/http_cli0.py rename to attic/control/http_cli0.py diff --git a/control/kwcombos.py b/attic/control/kwcombos.py similarity index 100% rename from control/kwcombos.py rename to attic/control/kwcombos.py diff --git a/14-it-generator/aritprog_v4.py b/attic/iterables/aritprog_v4.py similarity index 100% rename from 14-it-generator/aritprog_v4.py rename to attic/iterables/aritprog_v4.py diff --git a/14-it-generator/aritprog_v5.py b/attic/iterables/aritprog_v5.py similarity index 100% rename from 14-it-generator/aritprog_v5.py rename to attic/iterables/aritprog_v5.py diff --git a/control/referencias.txt b/control/referencias.txt deleted file mode 100644 index 26fa6c3..0000000 --- a/control/referencias.txt +++ /dev/null @@ -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/ - diff --git a/control/taxi_sim2.py b/control/taxi_sim2.py deleted file mode 100644 index e42f6bf..0000000 --- a/control/taxi_sim2.py +++ /dev/null @@ -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 - -"""