From 824bfae3b9455c0630fc054c693833338eae6af9 Mon Sep 17 00:00:00 2001 From: dido Date: Wed, 30 Sep 2015 23:53:04 +0200 Subject: [PATCH] first release simpynet-1.0, setup.py working --- .gitignore | 59 + README.md | 5 +- examples/selective_repeat.pyc | Bin 5466 -> 0 bytes lib/__init__.py | 7 - lib/__init__.pyc | Bin 238 -> 0 bytes lib/prettytable/__init__.py | 8 - lib/prettytable/__init__.pyc | Bin 315 -> 0 bytes lib/prettytable/prettytable.py | 1475 ----------------- lib/prettytable/prettytable.pyc | Bin 53141 -> 0 bytes lib/prettytable/prettytable_test.py | 588 ------- lib/simpy/__init__.py | 99 -- lib/simpy/__init__.pyc | Bin 3029 -> 0 bytes lib/simpy/_compat.py | 37 - lib/simpy/_compat.pyc | Bin 1251 -> 0 bytes lib/simpy/core.py | 219 --- lib/simpy/core.pyc | Bin 7785 -> 0 bytes lib/simpy/events.py | 533 ------ lib/simpy/events.pyc | Bin 20674 -> 0 bytes lib/simpy/monitoring.py | 4 - lib/simpy/monitoring.pyc | Bin 248 -> 0 bytes lib/simpy/resources/__init__.py | 19 - lib/simpy/resources/__init__.pyc | Bin 872 -> 0 bytes lib/simpy/resources/base.py | 201 --- lib/simpy/resources/base.pyc | Bin 7162 -> 0 bytes lib/simpy/resources/container.py | 105 -- lib/simpy/resources/container.pyc | Bin 4769 -> 0 bytes lib/simpy/resources/resource.py | 220 --- lib/simpy/resources/resource.pyc | Bin 10143 -> 0 bytes lib/simpy/resources/store.py | 160 -- lib/simpy/resources/store.pyc | Bin 7873 -> 0 bytes lib/simpy/rt.py | 73 - lib/simpy/rt.pyc | Bin 2546 -> 0 bytes lib/simpy/test/__init__.py | 0 lib/simpy/test/__init__.pyc | Bin 164 -> 0 bytes lib/simpy/test/conftest.py | 13 - lib/simpy/test/conftest.pyc | Bin 611 -> 0 bytes lib/simpy/test/test_condition.py | 272 --- lib/simpy/test/test_condition.pyc | Bin 13066 -> 0 bytes lib/simpy/test/test_environment.py | 53 - lib/simpy/test/test_environment.pyc | Bin 3594 -> 0 bytes lib/simpy/test/test_event.py | 97 -- lib/simpy/test/test_event.pyc | Bin 4484 -> 0 bytes lib/simpy/test/test_exceptions.py | 161 -- lib/simpy/test/test_exceptions.pyc | Bin 7710 -> 0 bytes lib/simpy/test/test_interrupts.py | 204 --- lib/simpy/test/test_interrupts.pyc | Bin 9663 -> 0 bytes lib/simpy/test/test_process.py | 238 --- lib/simpy/test/test_process.pyc | Bin 10487 -> 0 bytes lib/simpy/test/test_resources.py | 454 ----- lib/simpy/test/test_resources.pyc | Bin 19817 -> 0 bytes lib/simpy/test/test_rt.py | 96 -- lib/simpy/test/test_rt.pyc | Bin 3721 -> 0 bytes lib/simpy/test/test_timeout.py | 72 - lib/simpy/test/test_timeout.pyc | Bin 3610 -> 0 bytes lib/simpy/test/test_util.py | 310 ---- lib/simpy/test/test_util.pyc | Bin 14746 -> 0 bytes lib/simpy/util.py | 63 - lib/simpy/util.pyc | Bin 2569 -> 0 bytes lib/statistics.py | 596 ------- setup.py | 17 + __init__.py => simpynet/__init__.py | 18 +- {examples => simpynet/examples}/dns.py | 0 .../examples}/example_DHCP.py | 43 +- .../examples}/example_P21.py | 0 .../examples}/examples_link.py | 74 +- .../examples}/examples_network.py | 0 .../examples}/examples_physical.py | 0 .../examples}/selective_repeat.py | 0 .../examples}/time_collector.py | 0 {src => simpynet/src}/__init__.py | 0 {src => simpynet/src}/application/__init__.py | 0 .../src}/application/dhcp_client.py | 113 +- .../src}/application/dhcp_server.py | 129 +- {src => simpynet/src}/environment/__init__.py | 0 .../src}/environment/environment.py | 79 +- .../src}/environment/environment.py.bk | 0 {src => simpynet/src}/link/SimPyNet | 8 +- {src => simpynet/src}/link/__init__.py | 0 {src => simpynet/src}/link/df | 0 {src => simpynet/src}/link/frame.py | 10 +- {src => simpynet/src}/link/mac.py | 16 +- {src => simpynet/src}/link/nic.py | 67 +- {src => simpynet/src}/link/switch.py | 101 +- {src => simpynet/src}/network/__init__.py | 0 {src => simpynet/src}/network/datagram.py | 10 +- {src => simpynet/src}/network/host.py | 92 +- {src => simpynet/src}/network/ip.py | 13 +- {src => simpynet/src}/network/router.py | 87 +- {src => simpynet/src}/physical/__init__.py | 0 {src => simpynet/src}/physical/databits.py | 10 +- {src => simpynet/src}/physical/hub.py | 66 +- {src => simpynet/src}/physical/link_agent.py | 106 +- {src => simpynet/src}/transport/__init__.py | 0 {src => simpynet/src}/transport/tcp_packet.py | 10 +- .../src}/transport/tcp_protocol.py | 42 +- {src => simpynet/src}/transport/udp_packet.py | 8 +- .../src}/transport/udp_protocol.py | 46 +- {src => simpynet/src}/utl.py | 0 {test => simpynet/test}/TestLink.py | 31 +- {test => simpynet/test}/TestPhysical.py | 0 src/__init__.pyc | Bin 179 -> 0 bytes src/application/__init__.pyc | Bin 375 -> 0 bytes src/application/dhcp_client.pyc | Bin 5740 -> 0 bytes src/application/dhcp_server.pyc | Bin 5822 -> 0 bytes src/environment/__init__.pyc | Bin 297 -> 0 bytes src/environment/environment.pyc | Bin 4921 -> 0 bytes src/link/__init__.pyc | Bin 436 -> 0 bytes src/link/frame.pyc | Bin 1575 -> 0 bytes src/link/mac.pyc | Bin 1546 -> 0 bytes src/link/nic.pyc | Bin 5303 -> 0 bytes src/link/switch.pyc | Bin 6876 -> 0 bytes src/network/__init__.pyc | Bin 395 -> 0 bytes src/network/datagram.pyc | Bin 1567 -> 0 bytes src/network/host.pyc | Bin 5438 -> 0 bytes src/network/ip.pyc | Bin 1312 -> 0 bytes src/network/router.pyc | Bin 6461 -> 0 bytes src/physical/__init__.pyc | Bin 1200 -> 0 bytes src/physical/databits.pyc | Bin 1086 -> 0 bytes src/physical/hub.pyc | Bin 4743 -> 0 bytes src/physical/link_agent.pyc | Bin 7974 -> 0 bytes src/transport/__init__.pyc | Bin 354 -> 0 bytes src/transport/udp_packet.pyc | Bin 1642 -> 0 bytes src/transport/udp_protocol.pyc | Bin 3962 -> 0 bytes 123 files changed, 659 insertions(+), 6978 deletions(-) create mode 100644 .gitignore delete mode 100644 examples/selective_repeat.pyc delete mode 100644 lib/__init__.py delete mode 100644 lib/__init__.pyc delete mode 100644 lib/prettytable/__init__.py delete mode 100644 lib/prettytable/__init__.pyc delete mode 100644 lib/prettytable/prettytable.py delete mode 100644 lib/prettytable/prettytable.pyc delete mode 100644 lib/prettytable/prettytable_test.py delete mode 100644 lib/simpy/__init__.py delete mode 100644 lib/simpy/__init__.pyc delete mode 100644 lib/simpy/_compat.py delete mode 100644 lib/simpy/_compat.pyc delete mode 100644 lib/simpy/core.py delete mode 100644 lib/simpy/core.pyc delete mode 100644 lib/simpy/events.py delete mode 100644 lib/simpy/events.pyc delete mode 100644 lib/simpy/monitoring.py delete mode 100644 lib/simpy/monitoring.pyc delete mode 100644 lib/simpy/resources/__init__.py delete mode 100644 lib/simpy/resources/__init__.pyc delete mode 100644 lib/simpy/resources/base.py delete mode 100644 lib/simpy/resources/base.pyc delete mode 100644 lib/simpy/resources/container.py delete mode 100644 lib/simpy/resources/container.pyc delete mode 100644 lib/simpy/resources/resource.py delete mode 100644 lib/simpy/resources/resource.pyc delete mode 100644 lib/simpy/resources/store.py delete mode 100644 lib/simpy/resources/store.pyc delete mode 100644 lib/simpy/rt.py delete mode 100644 lib/simpy/rt.pyc delete mode 100644 lib/simpy/test/__init__.py delete mode 100644 lib/simpy/test/__init__.pyc delete mode 100644 lib/simpy/test/conftest.py delete mode 100644 lib/simpy/test/conftest.pyc delete mode 100644 lib/simpy/test/test_condition.py delete mode 100644 lib/simpy/test/test_condition.pyc delete mode 100644 lib/simpy/test/test_environment.py delete mode 100644 lib/simpy/test/test_environment.pyc delete mode 100644 lib/simpy/test/test_event.py delete mode 100644 lib/simpy/test/test_event.pyc delete mode 100644 lib/simpy/test/test_exceptions.py delete mode 100644 lib/simpy/test/test_exceptions.pyc delete mode 100644 lib/simpy/test/test_interrupts.py delete mode 100644 lib/simpy/test/test_interrupts.pyc delete mode 100644 lib/simpy/test/test_process.py delete mode 100644 lib/simpy/test/test_process.pyc delete mode 100644 lib/simpy/test/test_resources.py delete mode 100644 lib/simpy/test/test_resources.pyc delete mode 100644 lib/simpy/test/test_rt.py delete mode 100644 lib/simpy/test/test_rt.pyc delete mode 100644 lib/simpy/test/test_timeout.py delete mode 100644 lib/simpy/test/test_timeout.pyc delete mode 100644 lib/simpy/test/test_util.py delete mode 100644 lib/simpy/test/test_util.pyc delete mode 100644 lib/simpy/util.py delete mode 100644 lib/simpy/util.pyc delete mode 100644 lib/statistics.py create mode 100644 setup.py rename __init__.py => simpynet/__init__.py (84%) rename {examples => simpynet/examples}/dns.py (100%) rename {examples => simpynet/examples}/example_DHCP.py (84%) rename {examples => simpynet/examples}/example_P21.py (100%) rename {examples => simpynet/examples}/examples_link.py (78%) rename {examples => simpynet/examples}/examples_network.py (100%) rename {examples => simpynet/examples}/examples_physical.py (100%) rename {examples => simpynet/examples}/selective_repeat.py (100%) rename {examples => simpynet/examples}/time_collector.py (100%) rename {src => simpynet/src}/__init__.py (100%) rename {src => simpynet/src}/application/__init__.py (100%) rename {src => simpynet/src}/application/dhcp_client.py (80%) rename {src => simpynet/src}/application/dhcp_server.py (76%) rename {src => simpynet/src}/environment/__init__.py (100%) rename {src => simpynet/src}/environment/environment.py (82%) rename {src => simpynet/src}/environment/environment.py.bk (100%) rename {src => simpynet/src}/link/SimPyNet (89%) rename {src => simpynet/src}/link/__init__.py (100%) rename {src => simpynet/src}/link/df (100%) rename {src => simpynet/src}/link/frame.py (93%) rename {src => simpynet/src}/link/mac.py (87%) rename {src => simpynet/src}/link/nic.py (84%) rename {src => simpynet/src}/link/switch.py (79%) rename {src => simpynet/src}/network/__init__.py (100%) rename {src => simpynet/src}/network/datagram.py (93%) rename {src => simpynet/src}/network/host.py (74%) rename {src => simpynet/src}/network/ip.py (88%) rename {src => simpynet/src}/network/router.py (81%) rename {src => simpynet/src}/physical/__init__.py (100%) rename {src => simpynet/src}/physical/databits.py (90%) rename {src => simpynet/src}/physical/hub.py (83%) rename {src => simpynet/src}/physical/link_agent.py (78%) rename {src => simpynet/src}/transport/__init__.py (100%) rename {src => simpynet/src}/transport/tcp_packet.py (94%) rename {src => simpynet/src}/transport/tcp_protocol.py (89%) rename {src => simpynet/src}/transport/udp_packet.py (94%) rename {src => simpynet/src}/transport/udp_protocol.py (80%) rename {src => simpynet/src}/utl.py (100%) rename {test => simpynet/test}/TestLink.py (91%) rename {test => simpynet/test}/TestPhysical.py (100%) delete mode 100644 src/__init__.pyc delete mode 100644 src/application/__init__.pyc delete mode 100644 src/application/dhcp_client.pyc delete mode 100644 src/application/dhcp_server.pyc delete mode 100644 src/environment/__init__.pyc delete mode 100644 src/environment/environment.pyc delete mode 100644 src/link/__init__.pyc delete mode 100644 src/link/frame.pyc delete mode 100644 src/link/mac.pyc delete mode 100644 src/link/nic.pyc delete mode 100644 src/link/switch.pyc delete mode 100644 src/network/__init__.pyc delete mode 100644 src/network/datagram.pyc delete mode 100644 src/network/host.pyc delete mode 100644 src/network/ip.pyc delete mode 100644 src/network/router.pyc delete mode 100644 src/physical/__init__.pyc delete mode 100644 src/physical/databits.pyc delete mode 100644 src/physical/hub.pyc delete mode 100644 src/physical/link_agent.pyc delete mode 100644 src/transport/__init__.pyc delete mode 100644 src/transport/udp_packet.pyc delete mode 100644 src/transport/udp_protocol.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b49c74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +venv/ +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/README.md b/README.md index 2540e96..e99880b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ - SymPyNet a simulator supporting teaching of computer networks + + +#SymPyNet (spn) a simulator supporting computer networks teaching. The name SimPyNet is a composizion of three words that characterise the project: - Simulator @@ -11,4 +13,3 @@ Wwith it is possible to define small networks in all the intenet protocol suit ( and experiment the execution with differetn values. SimPyNet provides olso a mechanism of Log of the execution and Statistic analysis. It born as support theaching for the course of computer network in the bachelor degree of Computer Science at Florence University. - diff --git a/examples/selective_repeat.pyc b/examples/selective_repeat.pyc deleted file mode 100644 index bf2a918ac6106b5127decf831fcc0abe685f5675..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5466 zcmcIo>rx!Y74F^(unUrGp%|3du8rhGn@WWOWXm>|Wg`h&DAGcDu@$IFb**Mb%fRl; zYGzh~lu4yZdWTf<26=?ML;M7JjO2gveWzzHNG70E1-5(p-sk$A(_8%Kx$2*8{I%(+ z{I7`bFHzDbsC=b%P;0fLm0wUh1*wb5FRGoA^6@FFPFd}gl|P~U8Rbu^mrA`*YI_I$ zDdnG2eq}f`sr)KCJD8Z3Mui77>*smjN2scIlAx6Zel3n_%~n>s-;Zj`OSPqsR<19u zEHBrV7neS)R=;fZv)wpZsjVhi(5;5c*xCNepZWKZ@68wYDf)EbwiqZg2C2344 z)2x@VKSWVp2^C8L0) z4fobjT$RqEl@8^Xi2N3MhuIXytqr3vv-SelR8c8ik^fok@j#x(<3s>F6U=4cUa37z zUgsdqxqLvFpF4s`H2CDMM#?)H!re+OC95?swNjH=;VsP)N4XF!=Z5TPPEyKXBCZ5Eh}HD@iw%z`=&ph6;BrE;#dzP=U2S#*UOUtN*{WOa<5ckf;pR zb_Tks3TvT$i0VDGhoY+UMDQq1Tgue!a*rTn7;Z=Byu!mD+di&la(Uo1?>CmL2AaIUMk3$5`R&~l!T)YuS zLB`&0oMv{n75NkFuO{UiZ~7>YC__3N859`c7wQ|S$>Nb8@i1U07Akfj=H>wm_1VeiJGUv zucD-+Yydq3pbID-DIFA%H<4_Fzz%~(?#!j;N2r7vVCXf-{FOWB9{&)rq5OUSi~YM#xq+J`mHWhmWm?WGNv+&TAQK;VmAioFU&i*E?9Tk)4l>4BSA&p(O19PJ z(`|*%BTS0=m=!qng=ndedNCiMsxvJxmstq{FzvKHa?>o3h=Rys7CFoK;k^6jYzaJE zVv7cCuCbz?4b5s~yJ7`r(EqQnYXc=^0;dpc=Jg!PCA6ygJ^jA^3}wtJ$dAYMzd?0y zpN5<~P=`Our3XBqOavIe6|F$fB3=FHX;GB`T~HFRb-${TdrE6D^50_0K`6!3s86AG z%nOecmwBHRd32$hbfjSyxqAhz^(YIHq~FWlTmY02>`bN1D2RYXF#oom9XSF$P#l-a z$~6vir^z$($0pIVb5v0+n@+1FF8tq#TgLV_${_CX;9_2cq^sySEP(;%C<03!z{!gN zia5D)s{qJ}xM7yC@ zXq@F{CO$kjr4LN#%5fzWB9*7*8Bt^)w(K*5! zdAy6riKGM>2qG#n5VWhbHB!n11cze|(HC=QADhG4X@wN^7~KO)phY$$+%GOJjD1p0 zEPcAXfYF7;*B2}uTd?HO6j>lAXOWJaI=k&|Xr#RuwE|nMC zgNg8SR6x7Q!6Z*98}y@JCGf0{8jNf?T&PJ9>~yFILOy@U>V!IaRwhi8!R^swa_ z!nnLnHe=wp^enbmryt zohB9!zUOhW-@_H+_ex*W*9t!cO3xxtUC{61-@~L>BMA||XKbVo2uL3}?3N#=#Dm0z z;ZY8~X+jP8$;-u;Y4q1duejm+C#S^R=h;P6Nk5XOj961}29|g-Hre2y)$MhHKcFi= z2Px#;=k(|Rd1%WUz*;}{tTlJBRLt3Y&K7^U5Mmvqi#AD?VLTr@2R(I?bveg!zi!+j VXr>oYP3Zi;FjJVlG~1}0{|^o+iFN=0 diff --git a/lib/__init__.py b/lib/__init__.py deleted file mode 100644 index 7f181a4..0000000 --- a/lib/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed May 21 08:45:01 2014 - -@author: dido -""" - diff --git a/lib/__init__.pyc b/lib/__init__.pyc deleted file mode 100644 index f805e35840d7a196c31a03715d067948ac943b0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmYL>(Q3j#5JguDRTlh*eaw?25iJuloZXq0oVQ z?ww({4?h!qd%Kmj`kR6HQWUhnDRmGIRGAF#w_KoC9;=AXaA)>nA60K=nMoH#J}=VD z%+f5^dRblKiG5)lIo6xxUK>XOYaQ3tCKA;@!n>@i0(Jp?fEORJg;(ZdgJ$jNim)Zm uHFd=13nRf613@@!6UTl$+`CiojK oRAJlIumn_%-Kl?%D6Ppxt*`q5&uI8v>&(7^`w8vC_(v1|3tl@=F8}}l diff --git a/lib/prettytable/prettytable.py b/lib/prettytable/prettytable.py deleted file mode 100644 index 8abb952..0000000 --- a/lib/prettytable/prettytable.py +++ /dev/null @@ -1,1475 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009-2013, Luke Maurits -# All rights reserved. -# With contributions from: -# * Chris Clark -# * Klein Stephane -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -__version__ = "0.7.2" - -import copy -import csv -import random -import re -import sys -import textwrap -import itertools -import unicodedata - -py3k = sys.version_info[0] >= 3 -if py3k: - unicode = str - basestring = str - itermap = map - iterzip = zip - uni_chr = chr - from html.parser import HTMLParser -else: - itermap = itertools.imap - iterzip = itertools.izip - uni_chr = unichr - from HTMLParser import HTMLParser - -if py3k and sys.version_info[1] >= 2: - from html import escape -else: - from cgi import escape - -# hrule styles -FRAME = 0 -ALL = 1 -NONE = 2 -HEADER = 3 - -# Table styles -DEFAULT = 10 -MSWORD_FRIENDLY = 11 -PLAIN_COLUMNS = 12 -RANDOM = 20 - -_re = re.compile("\033\[[0-9;]*m") - -def _get_size(text): - lines = text.split("\n") - height = len(lines) - width = max([_str_block_width(line) for line in lines]) - return (width, height) - -class PrettyTable(object): - - def __init__(self, field_names=None, **kwargs): - - """Return a new PrettyTable instance - - Arguments: - - encoding - Unicode encoding scheme used to decode any encoded input - field_names - list or tuple of field names - fields - list or tuple of field names to include in displays - start - index of first data row to include in output - end - index of last data row to include in output PLUS ONE (list slice style) - header - print a header showing field names (True or False) - header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None) - border - print a border around the table (True or False) - hrules - controls printing of horizontal rules after rows. Allowed values: FRAME, HEADER, ALL, NONE - vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE - int_format - controls formatting of integer data - float_format - controls formatting of floating point data - padding_width - number of spaces on either side of column data (only used if left and right paddings are None) - left_padding_width - number of spaces on left hand side of column data - right_padding_width - number of spaces on right hand side of column data - vertical_char - single character string used to draw vertical lines - horizontal_char - single character string used to draw horizontal lines - junction_char - single character string used to draw line junctions - sortby - name of field to sort rows by - sort_key - sorting key function, applied to data points before sorting - valign - default valign for each row (None, "t", "m" or "b") - reversesort - True or False to sort in descending or ascending order""" - - self.encoding = kwargs.get("encoding", "UTF-8") - - # Data - self._field_names = [] - self._align = {} - self._valign = {} - self._max_width = {} - self._rows = [] - if field_names: - self.field_names = field_names - else: - self._widths = [] - - # Options - self._options = "start end fields header border sortby reversesort sort_key attributes format hrules vrules".split() - self._options.extend("int_format float_format padding_width left_padding_width right_padding_width".split()) - self._options.extend("vertical_char horizontal_char junction_char header_style valign xhtml print_empty".split()) - for option in self._options: - if option in kwargs: - self._validate_option(option, kwargs[option]) - else: - kwargs[option] = None - - self._start = kwargs["start"] or 0 - self._end = kwargs["end"] or None - self._fields = kwargs["fields"] or None - - if kwargs["header"] in (True, False): - self._header = kwargs["header"] - else: - self._header = True - self._header_style = kwargs["header_style"] or None - if kwargs["border"] in (True, False): - self._border = kwargs["border"] - else: - self._border = True - self._hrules = kwargs["hrules"] or FRAME - self._vrules = kwargs["vrules"] or ALL - - self._sortby = kwargs["sortby"] or None - if kwargs["reversesort"] in (True, False): - self._reversesort = kwargs["reversesort"] - else: - self._reversesort = False - self._sort_key = kwargs["sort_key"] or (lambda x: x) - - self._int_format = kwargs["int_format"] or {} - self._float_format = kwargs["float_format"] or {} - self._padding_width = kwargs["padding_width"] or 1 - self._left_padding_width = kwargs["left_padding_width"] or None - self._right_padding_width = kwargs["right_padding_width"] or None - - self._vertical_char = kwargs["vertical_char"] or self._unicode("|") - self._horizontal_char = kwargs["horizontal_char"] or self._unicode("-") - self._junction_char = kwargs["junction_char"] or self._unicode("+") - - if kwargs["print_empty"] in (True, False): - self._print_empty = kwargs["print_empty"] - else: - self._print_empty = True - self._format = kwargs["format"] or False - self._xhtml = kwargs["xhtml"] or False - self._attributes = kwargs["attributes"] or {} - - def _unicode(self, value): - if not isinstance(value, basestring): - value = str(value) - if not isinstance(value, unicode): - value = unicode(value, self.encoding, "strict") - return value - - def _justify(self, text, width, align): - excess = width - _str_block_width(text) - if align == "l": - return text + excess * " " - elif align == "r": - return excess * " " + text - else: - if excess % 2: - # Uneven padding - # Put more space on right if text is of odd length... - if _str_block_width(text) % 2: - return (excess//2)*" " + text + (excess//2 + 1)*" " - # and more space on left if text is of even length - else: - return (excess//2 + 1)*" " + text + (excess//2)*" " - # Why distribute extra space this way? To match the behaviour of - # the inbuilt str.center() method. - else: - # Equal padding on either side - return (excess//2)*" " + text + (excess//2)*" " - - def __getattr__(self, name): - - if name == "rowcount": - return len(self._rows) - elif name == "colcount": - if self._field_names: - return len(self._field_names) - elif self._rows: - return len(self._rows[0]) - else: - return 0 - else: - raise AttributeError(name) - - def __getitem__(self, index): - - new = PrettyTable() - new.field_names = self.field_names - for attr in self._options: - setattr(new, "_"+attr, getattr(self, "_"+attr)) - setattr(new, "_align", getattr(self, "_align")) - if isinstance(index, slice): - for row in self._rows[index]: - new.add_row(row) - elif isinstance(index, int): - new.add_row(self._rows[index]) - else: - raise Exception("Index %s is invalid, must be an integer or slice" % str(index)) - return new - - if py3k: - def __str__(self): - return self.__unicode__() - else: - def __str__(self): - return self.__unicode__().encode(self.encoding) - - def __unicode__(self): - return self.get_string() - - ############################## - # ATTRIBUTE VALIDATORS # - ############################## - - # The method _validate_option is all that should be used elsewhere in the code base to validate options. - # It will call the appropriate validation method for that option. The individual validation methods should - # never need to be called directly (although nothing bad will happen if they *are*). - # Validation happens in TWO places. - # Firstly, in the property setters defined in the ATTRIBUTE MANAGMENT section. - # Secondly, in the _get_options method, where keyword arguments are mixed with persistent settings - - def _validate_option(self, option, val): - if option in ("field_names"): - self._validate_field_names(val) - elif option in ("start", "end", "max_width", "padding_width", "left_padding_width", "right_padding_width", "format"): - self._validate_nonnegative_int(option, val) - elif option in ("sortby"): - self._validate_field_name(option, val) - elif option in ("sort_key"): - self._validate_function(option, val) - elif option in ("hrules"): - self._validate_hrules(option, val) - elif option in ("vrules"): - self._validate_vrules(option, val) - elif option in ("fields"): - self._validate_all_field_names(option, val) - elif option in ("header", "border", "reversesort", "xhtml", "print_empty"): - self._validate_true_or_false(option, val) - elif option in ("header_style"): - self._validate_header_style(val) - elif option in ("int_format"): - self._validate_int_format(option, val) - elif option in ("float_format"): - self._validate_float_format(option, val) - elif option in ("vertical_char", "horizontal_char", "junction_char"): - self._validate_single_char(option, val) - elif option in ("attributes"): - self._validate_attributes(option, val) - else: - raise Exception("Unrecognised option: %s!" % option) - - def _validate_field_names(self, val): - # Check for appropriate length - if self._field_names: - try: - assert len(val) == len(self._field_names) - except AssertionError: - raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._field_names))) - if self._rows: - try: - assert len(val) == len(self._rows[0]) - except AssertionError: - raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._rows[0]))) - # Check for uniqueness - try: - assert len(val) == len(set(val)) - except AssertionError: - raise Exception("Field names must be unique!") - - def _validate_header_style(self, val): - try: - assert val in ("cap", "title", "upper", "lower", None) - except AssertionError: - raise Exception("Invalid header style, use cap, title, upper, lower or None!") - - def _validate_align(self, val): - try: - assert val in ["l","c","r"] - except AssertionError: - raise Exception("Alignment %s is invalid, use l, c or r!" % val) - - def _validate_valign(self, val): - try: - assert val in ["t","m","b",None] - except AssertionError: - raise Exception("Alignment %s is invalid, use t, m, b or None!" % val) - - def _validate_nonnegative_int(self, name, val): - try: - assert int(val) >= 0 - except AssertionError: - raise Exception("Invalid value for %s: %s!" % (name, self._unicode(val))) - - def _validate_true_or_false(self, name, val): - try: - assert val in (True, False) - except AssertionError: - raise Exception("Invalid value for %s! Must be True or False." % name) - - def _validate_int_format(self, name, val): - if val == "": - return - try: - assert type(val) in (str, unicode) - assert val.isdigit() - except AssertionError: - raise Exception("Invalid value for %s! Must be an integer format string." % name) - - def _validate_float_format(self, name, val): - if val == "": - return - try: - assert type(val) in (str, unicode) - assert "." in val - bits = val.split(".") - assert len(bits) <= 2 - assert bits[0] == "" or bits[0].isdigit() - assert bits[1] == "" or bits[1].isdigit() - except AssertionError: - raise Exception("Invalid value for %s! Must be a float format string." % name) - - def _validate_function(self, name, val): - try: - assert hasattr(val, "__call__") - except AssertionError: - raise Exception("Invalid value for %s! Must be a function." % name) - - def _validate_hrules(self, name, val): - try: - assert val in (ALL, FRAME, HEADER, NONE) - except AssertionError: - raise Exception("Invalid value for %s! Must be ALL, FRAME, HEADER or NONE." % name) - - def _validate_vrules(self, name, val): - try: - assert val in (ALL, FRAME, NONE) - except AssertionError: - raise Exception("Invalid value for %s! Must be ALL, FRAME, or NONE." % name) - - def _validate_field_name(self, name, val): - try: - assert (val in self._field_names) or (val is None) - except AssertionError: - raise Exception("Invalid field name: %s!" % val) - - def _validate_all_field_names(self, name, val): - try: - for x in val: - self._validate_field_name(name, x) - except AssertionError: - raise Exception("fields must be a sequence of field names!") - - def _validate_single_char(self, name, val): - try: - assert _str_block_width(val) == 1 - except AssertionError: - raise Exception("Invalid value for %s! Must be a string of length 1." % name) - - def _validate_attributes(self, name, val): - try: - assert isinstance(val, dict) - except AssertionError: - raise Exception("attributes must be a dictionary of name/value pairs!") - - ############################## - # ATTRIBUTE MANAGEMENT # - ############################## - - def _get_field_names(self): - return self._field_names - """The names of the fields - - Arguments: - - fields - list or tuple of field names""" - def _set_field_names(self, val): - val = [self._unicode(x) for x in val] - self._validate_option("field_names", val) - if self._field_names: - old_names = self._field_names[:] - self._field_names = val - if self._align and old_names: - for old_name, new_name in zip(old_names, val): - self._align[new_name] = self._align[old_name] - for old_name in old_names: - if old_name not in self._align: - self._align.pop(old_name) - else: - for field in self._field_names: - self._align[field] = "c" - if self._valign and old_names: - for old_name, new_name in zip(old_names, val): - self._valign[new_name] = self._valign[old_name] - for old_name in old_names: - if old_name not in self._valign: - self._valign.pop(old_name) - else: - for field in self._field_names: - self._valign[field] = "t" - field_names = property(_get_field_names, _set_field_names) - - def _get_align(self): - return self._align - def _set_align(self, val): - self._validate_align(val) - for field in self._field_names: - self._align[field] = val - align = property(_get_align, _set_align) - - def _get_valign(self): - return self._valign - def _set_valign(self, val): - self._validate_valign(val) - for field in self._field_names: - self._valign[field] = val - valign = property(_get_valign, _set_valign) - - def _get_max_width(self): - return self._max_width - def _set_max_width(self, val): - self._validate_option("max_width", val) - for field in self._field_names: - self._max_width[field] = val - max_width = property(_get_max_width, _set_max_width) - - def _get_fields(self): - """List or tuple of field names to include in displays - - Arguments: - - fields - list or tuple of field names to include in displays""" - return self._fields - def _set_fields(self, val): - self._validate_option("fields", val) - self._fields = val - fields = property(_get_fields, _set_fields) - - def _get_start(self): - """Start index of the range of rows to print - - Arguments: - - start - index of first data row to include in output""" - return self._start - - def _set_start(self, val): - self._validate_option("start", val) - self._start = val - start = property(_get_start, _set_start) - - def _get_end(self): - """End index of the range of rows to print - - Arguments: - - end - index of last data row to include in output PLUS ONE (list slice style)""" - return self._end - def _set_end(self, val): - self._validate_option("end", val) - self._end = val - end = property(_get_end, _set_end) - - def _get_sortby(self): - """Name of field by which to sort rows - - Arguments: - - sortby - field name to sort by""" - return self._sortby - def _set_sortby(self, val): - self._validate_option("sortby", val) - self._sortby = val - sortby = property(_get_sortby, _set_sortby) - - def _get_reversesort(self): - """Controls direction of sorting (ascending vs descending) - - Arguments: - - reveresort - set to True to sort by descending order, or False to sort by ascending order""" - return self._reversesort - def _set_reversesort(self, val): - self._validate_option("reversesort", val) - self._reversesort = val - reversesort = property(_get_reversesort, _set_reversesort) - - def _get_sort_key(self): - """Sorting key function, applied to data points before sorting - - Arguments: - - sort_key - a function which takes one argument and returns something to be sorted""" - return self._sort_key - def _set_sort_key(self, val): - self._validate_option("sort_key", val) - self._sort_key = val - sort_key = property(_get_sort_key, _set_sort_key) - - def _get_header(self): - """Controls printing of table header with field names - - Arguments: - - header - print a header showing field names (True or False)""" - return self._header - def _set_header(self, val): - self._validate_option("header", val) - self._header = val - header = property(_get_header, _set_header) - - def _get_header_style(self): - """Controls stylisation applied to field names in header - - Arguments: - - header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None)""" - return self._header_style - def _set_header_style(self, val): - self._validate_header_style(val) - self._header_style = val - header_style = property(_get_header_style, _set_header_style) - - def _get_border(self): - """Controls printing of border around table - - Arguments: - - border - print a border around the table (True or False)""" - return self._border - def _set_border(self, val): - self._validate_option("border", val) - self._border = val - border = property(_get_border, _set_border) - - def _get_hrules(self): - """Controls printing of horizontal rules after rows - - Arguments: - - hrules - horizontal rules style. Allowed values: FRAME, ALL, HEADER, NONE""" - return self._hrules - def _set_hrules(self, val): - self._validate_option("hrules", val) - self._hrules = val - hrules = property(_get_hrules, _set_hrules) - - def _get_vrules(self): - """Controls printing of vertical rules between columns - - Arguments: - - vrules - vertical rules style. Allowed values: FRAME, ALL, NONE""" - return self._vrules - def _set_vrules(self, val): - self._validate_option("vrules", val) - self._vrules = val - vrules = property(_get_vrules, _set_vrules) - - def _get_int_format(self): - """Controls formatting of integer data - Arguments: - - int_format - integer format string""" - return self._int_format - def _set_int_format(self, val): -# self._validate_option("int_format", val) - for field in self._field_names: - self._int_format[field] = val - int_format = property(_get_int_format, _set_int_format) - - def _get_float_format(self): - """Controls formatting of floating point data - Arguments: - - float_format - floating point format string""" - return self._float_format - def _set_float_format(self, val): -# self._validate_option("float_format", val) - for field in self._field_names: - self._float_format[field] = val - float_format = property(_get_float_format, _set_float_format) - - def _get_padding_width(self): - """The number of empty spaces between a column's edge and its content - - Arguments: - - padding_width - number of spaces, must be a positive integer""" - return self._padding_width - def _set_padding_width(self, val): - self._validate_option("padding_width", val) - self._padding_width = val - padding_width = property(_get_padding_width, _set_padding_width) - - def _get_left_padding_width(self): - """The number of empty spaces between a column's left edge and its content - - Arguments: - - left_padding - number of spaces, must be a positive integer""" - return self._left_padding_width - def _set_left_padding_width(self, val): - self._validate_option("left_padding_width", val) - self._left_padding_width = val - left_padding_width = property(_get_left_padding_width, _set_left_padding_width) - - def _get_right_padding_width(self): - """The number of empty spaces between a column's right edge and its content - - Arguments: - - right_padding - number of spaces, must be a positive integer""" - return self._right_padding_width - def _set_right_padding_width(self, val): - self._validate_option("right_padding_width", val) - self._right_padding_width = val - right_padding_width = property(_get_right_padding_width, _set_right_padding_width) - - def _get_vertical_char(self): - """The charcter used when printing table borders to draw vertical lines - - Arguments: - - vertical_char - single character string used to draw vertical lines""" - return self._vertical_char - def _set_vertical_char(self, val): - val = self._unicode(val) - self._validate_option("vertical_char", val) - self._vertical_char = val - vertical_char = property(_get_vertical_char, _set_vertical_char) - - def _get_horizontal_char(self): - """The charcter used when printing table borders to draw horizontal lines - - Arguments: - - horizontal_char - single character string used to draw horizontal lines""" - return self._horizontal_char - def _set_horizontal_char(self, val): - val = self._unicode(val) - self._validate_option("horizontal_char", val) - self._horizontal_char = val - horizontal_char = property(_get_horizontal_char, _set_horizontal_char) - - def _get_junction_char(self): - """The charcter used when printing table borders to draw line junctions - - Arguments: - - junction_char - single character string used to draw line junctions""" - return self._junction_char - def _set_junction_char(self, val): - val = self._unicode(val) - self._validate_option("vertical_char", val) - self._junction_char = val - junction_char = property(_get_junction_char, _set_junction_char) - - def _get_format(self): - """Controls whether or not HTML tables are formatted to match styling options - - Arguments: - - format - True or False""" - return self._format - def _set_format(self, val): - self._validate_option("format", val) - self._format = val - format = property(_get_format, _set_format) - - def _get_print_empty(self): - """Controls whether or not empty tables produce a header and frame or just an empty string - - Arguments: - - print_empty - True or False""" - return self._print_empty - def _set_print_empty(self, val): - self._validate_option("print_empty", val) - self._print_empty = val - print_empty = property(_get_print_empty, _set_print_empty) - - def _get_attributes(self): - """A dictionary of HTML attribute name/value pairs to be included in the tag when printing HTML - - Arguments: - - attributes - dictionary of attributes""" - return self._attributes - def _set_attributes(self, val): - self._validate_option("attributes", val) - self._attributes = val - attributes = property(_get_attributes, _set_attributes) - - ############################## - # OPTION MIXER # - ############################## - - def _get_options(self, kwargs): - - options = {} - for option in self._options: - if option in kwargs: - self._validate_option(option, kwargs[option]) - options[option] = kwargs[option] - else: - options[option] = getattr(self, "_"+option) - return options - - ############################## - # PRESET STYLE LOGIC # - ############################## - - def set_style(self, style): - - if style == DEFAULT: - self._set_default_style() - elif style == MSWORD_FRIENDLY: - self._set_msword_style() - elif style == PLAIN_COLUMNS: - self._set_columns_style() - elif style == RANDOM: - self._set_random_style() - else: - raise Exception("Invalid pre-set style!") - - def _set_default_style(self): - - self.header = True - self.border = True - self._hrules = FRAME - self._vrules = ALL - self.padding_width = 1 - self.left_padding_width = 1 - self.right_padding_width = 1 - self.vertical_char = "|" - self.horizontal_char = "-" - self.junction_char = "+" - - def _set_msword_style(self): - - self.header = True - self.border = True - self._hrules = NONE - self.padding_width = 1 - self.left_padding_width = 1 - self.right_padding_width = 1 - self.vertical_char = "|" - - def _set_columns_style(self): - - self.header = True - self.border = False - self.padding_width = 1 - self.left_padding_width = 0 - self.right_padding_width = 8 - - def _set_random_style(self): - - # Just for fun! - self.header = random.choice((True, False)) - self.border = random.choice((True, False)) - self._hrules = random.choice((ALL, FRAME, HEADER, NONE)) - self._vrules = random.choice((ALL, FRAME, NONE)) - self.left_padding_width = random.randint(0,5) - self.right_padding_width = random.randint(0,5) - self.vertical_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") - self.horizontal_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") - self.junction_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") - - ############################## - # DATA INPUT METHODS # - ############################## - - def add_row(self, row): - - """Add a row to the table - - Arguments: - - row - row of data, should be a list with as many elements as the table - has fields""" - - if self._field_names and len(row) != len(self._field_names): - raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" %(len(row),len(self._field_names))) - if not self._field_names: - self.field_names = [("Field %d" % (n+1)) for n in range(0,len(row))] - self._rows.append(list(row)) - - def del_row(self, row_index): - - """Delete a row to the table - - Arguments: - - row_index - The index of the row you want to delete. Indexing starts at 0.""" - - if row_index > len(self._rows)-1: - raise Exception("Cant delete row at index %d, table only has %d rows!" % (row_index, len(self._rows))) - del self._rows[row_index] - - def add_column(self, fieldname, column, align="c", valign="t"): - - """Add a column to the table. - - Arguments: - - fieldname - name of the field to contain the new column of data - column - column of data, should be a list with as many elements as the - table has rows - align - desired alignment for this column - "l" for left, "c" for centre and "r" for right - valign - desired vertical alignment for new columns - "t" for top, "m" for middle and "b" for bottom""" - - if len(self._rows) in (0, len(column)): - self._validate_align(align) - self._validate_valign(valign) - self._field_names.append(fieldname) - self._align[fieldname] = align - self._valign[fieldname] = valign - for i in range(0, len(column)): - if len(self._rows) < i+1: - self._rows.append([]) - self._rows[i].append(column[i]) - else: - raise Exception("Column length %d does not match number of rows %d!" % (len(column), len(self._rows))) - - def clear_rows(self): - - """Delete all rows from the table but keep the current field names""" - - self._rows = [] - - def clear(self): - - """Delete all rows and field names from the table, maintaining nothing but styling options""" - - self._rows = [] - self._field_names = [] - self._widths = [] - - ############################## - # MISC PUBLIC METHODS # - ############################## - - def copy(self): - return copy.deepcopy(self) - - ############################## - # MISC PRIVATE METHODS # - ############################## - - def _format_value(self, field, value): - if isinstance(value, int) and field in self._int_format: - value = self._unicode(("%%%sd" % self._int_format[field]) % value) - elif isinstance(value, float) and field in self._float_format: - value = self._unicode(("%%%sf" % self._float_format[field]) % value) - return self._unicode(value) - - def _compute_widths(self, rows, options): - if options["header"]: - widths = [_get_size(field)[0] for field in self._field_names] - else: - widths = len(self.field_names) * [0] - for row in rows: - for index, value in enumerate(row): - fieldname = self.field_names[index] - if fieldname in self.max_width: - widths[index] = max(widths[index], min(_get_size(value)[0], self.max_width[fieldname])) - else: - widths[index] = max(widths[index], _get_size(value)[0]) - self._widths = widths - - def _get_padding_widths(self, options): - - if options["left_padding_width"] is not None: - lpad = options["left_padding_width"] - else: - lpad = options["padding_width"] - if options["right_padding_width"] is not None: - rpad = options["right_padding_width"] - else: - rpad = options["padding_width"] - return lpad, rpad - - def _get_rows(self, options): - """Return only those data rows that should be printed, based on slicing and sorting. - - Arguments: - - options - dictionary of option settings.""" - - # Make a copy of only those rows in the slice range - rows = copy.deepcopy(self._rows[options["start"]:options["end"]]) - # Sort if necessary - if options["sortby"]: - sortindex = self._field_names.index(options["sortby"]) - # Decorate - rows = [[row[sortindex]]+row for row in rows] - # Sort - rows.sort(reverse=options["reversesort"], key=options["sort_key"]) - # Undecorate - rows = [row[1:] for row in rows] - return rows - - def _format_row(self, row, options): - return [self._format_value(field, value) for (field, value) in zip(self._field_names, row)] - - def _format_rows(self, rows, options): - return [self._format_row(row, options) for row in rows] - - ############################## - # PLAIN TEXT STRING METHODS # - ############################## - - def get_string(self, **kwargs): - - """Return string representation of table in current state. - - Arguments: - - start - index of first data row to include in output - end - index of last data row to include in output PLUS ONE (list slice style) - fields - names of fields (columns) to include - header - print a header showing field names (True or False) - border - print a border around the table (True or False) - hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE - vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE - int_format - controls formatting of integer data - float_format - controls formatting of floating point data - padding_width - number of spaces on either side of column data (only used if left and right paddings are None) - left_padding_width - number of spaces on left hand side of column data - right_padding_width - number of spaces on right hand side of column data - vertical_char - single character string used to draw vertical lines - horizontal_char - single character string used to draw horizontal lines - junction_char - single character string used to draw line junctions - sortby - name of field to sort rows by - sort_key - sorting key function, applied to data points before sorting - reversesort - True or False to sort in descending or ascending order - print empty - if True, stringify just the header for an empty table, if False return an empty string """ - - options = self._get_options(kwargs) - - lines = [] - - # Don't think too hard about an empty table - # Is this the desired behaviour? Maybe we should still print the header? - if self.rowcount == 0 and (not options["print_empty"] or not options["border"]): - return "" - - # Get the rows we need to print, taking into account slicing, sorting, etc. - rows = self._get_rows(options) - - # Turn all data in all rows into Unicode, formatted as desired - formatted_rows = self._format_rows(rows, options) - - # Compute column widths - self._compute_widths(formatted_rows, options) - - # Add header or top of border - self._hrule = self._stringify_hrule(options) - if options["header"]: - lines.append(self._stringify_header(options)) - elif options["border"] and options["hrules"] in (ALL, FRAME): - lines.append(self._hrule) - - # Add rows - for row in formatted_rows: - lines.append(self._stringify_row(row, options)) - - # Add bottom of border - if options["border"] and options["hrules"] == FRAME: - lines.append(self._hrule) - - return self._unicode("\n").join(lines) - - def _stringify_hrule(self, options): - - if not options["border"]: - return "" - lpad, rpad = self._get_padding_widths(options) - if options['vrules'] in (ALL, FRAME): - bits = [options["junction_char"]] - else: - bits = [options["horizontal_char"]] - # For tables with no data or fieldnames - if not self._field_names: - bits.append(options["junction_char"]) - return "".join(bits) - for field, width in zip(self._field_names, self._widths): - if options["fields"] and field not in options["fields"]: - continue - bits.append((width+lpad+rpad)*options["horizontal_char"]) - if options['vrules'] == ALL: - bits.append(options["junction_char"]) - else: - bits.append(options["horizontal_char"]) - if options["vrules"] == FRAME: - bits.pop() - bits.append(options["junction_char"]) - return "".join(bits) - - def _stringify_header(self, options): - - bits = [] - lpad, rpad = self._get_padding_widths(options) - if options["border"]: - if options["hrules"] in (ALL, FRAME): - bits.append(self._hrule) - bits.append("\n") - if options["vrules"] in (ALL, FRAME): - bits.append(options["vertical_char"]) - else: - bits.append(" ") - # For tables with no data or field names - if not self._field_names: - if options["vrules"] in (ALL, FRAME): - bits.append(options["vertical_char"]) - else: - bits.append(" ") - for field, width, in zip(self._field_names, self._widths): - if options["fields"] and field not in options["fields"]: - continue - if self._header_style == "cap": - fieldname = field.capitalize() - elif self._header_style == "title": - fieldname = field.title() - elif self._header_style == "upper": - fieldname = field.upper() - elif self._header_style == "lower": - fieldname = field.lower() - else: - fieldname = field - bits.append(" " * lpad + self._justify(fieldname, width, self._align[field]) + " " * rpad) - if options["border"]: - if options["vrules"] == ALL: - bits.append(options["vertical_char"]) - else: - bits.append(" ") - # If vrules is FRAME, then we just appended a space at the end - # of the last field, when we really want a vertical character - if options["border"] and options["vrules"] == FRAME: - bits.pop() - bits.append(options["vertical_char"]) - if options["border"] and options["hrules"] != NONE: - bits.append("\n") - bits.append(self._hrule) - return "".join(bits) - - def _stringify_row(self, row, options): - - for index, field, value, width, in zip(range(0,len(row)), self._field_names, row, self._widths): - # Enforce max widths - lines = value.split("\n") - new_lines = [] - for line in lines: - if _str_block_width(line) > width: - line = textwrap.fill(line, width) - new_lines.append(line) - lines = new_lines - value = "\n".join(lines) - row[index] = value - - row_height = 0 - for c in row: - h = _get_size(c)[1] - if h > row_height: - row_height = h - - bits = [] - lpad, rpad = self._get_padding_widths(options) - for y in range(0, row_height): - bits.append([]) - if options["border"]: - if options["vrules"] in (ALL, FRAME): - bits[y].append(self.vertical_char) - else: - bits[y].append(" ") - - for field, value, width, in zip(self._field_names, row, self._widths): - - valign = self._valign[field] - lines = value.split("\n") - dHeight = row_height - len(lines) - if dHeight: - if valign == "m": - lines = [""] * int(dHeight / 2) + lines + [""] * (dHeight - int(dHeight / 2)) - elif valign == "b": - lines = [""] * dHeight + lines - else: - lines = lines + [""] * dHeight - - y = 0 - for l in lines: - if options["fields"] and field not in options["fields"]: - continue - - bits[y].append(" " * lpad + self._justify(l, width, self._align[field]) + " " * rpad) - if options["border"]: - if options["vrules"] == ALL: - bits[y].append(self.vertical_char) - else: - bits[y].append(" ") - y += 1 - - # If vrules is FRAME, then we just appended a space at the end - # of the last field, when we really want a vertical character - for y in range(0, row_height): - if options["border"] and options["vrules"] == FRAME: - bits[y].pop() - bits[y].append(options["vertical_char"]) - - if options["border"] and options["hrules"]== ALL: - bits[row_height-1].append("\n") - bits[row_height-1].append(self._hrule) - - for y in range(0, row_height): - bits[y] = "".join(bits[y]) - - return "\n".join(bits) - - ############################## - # HTML STRING METHODS # - ############################## - - def get_html_string(self, **kwargs): - - """Return string representation of HTML formatted version of table in current state. - - Arguments: - - start - index of first data row to include in output - end - index of last data row to include in output PLUS ONE (list slice style) - fields - names of fields (columns) to include - header - print a header showing field names (True or False) - border - print a border around the table (True or False) - hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE - vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE - int_format - controls formatting of integer data - float_format - controls formatting of floating point data - padding_width - number of spaces on either side of column data (only used if left and right paddings are None) - left_padding_width - number of spaces on left hand side of column data - right_padding_width - number of spaces on right hand side of column data - sortby - name of field to sort rows by - sort_key - sorting key function, applied to data points before sorting - attributes - dictionary of name/value pairs to include as HTML attributes in the
tag - xhtml - print
tags if True,
tags if false""" - - options = self._get_options(kwargs) - - if options["format"]: - string = self._get_formatted_html_string(options) - else: - string = self._get_simple_html_string(options) - - return string - - def _get_simple_html_string(self, options): - - lines = [] - if options["xhtml"]: - linebreak = "
" - else: - linebreak = "
" - - open_tag = [] - open_tag.append("") - lines.append("".join(open_tag)) - - # Headers - if options["header"]: - lines.append(" ") - for field in self._field_names: - if options["fields"] and field not in options["fields"]: - continue - lines.append(" " % escape(field).replace("\n", linebreak)) - lines.append(" ") - - # Data - rows = self._get_rows(options) - formatted_rows = self._format_rows(rows, options) - for row in formatted_rows: - lines.append(" ") - for field, datum in zip(self._field_names, row): - if options["fields"] and field not in options["fields"]: - continue - lines.append(" " % escape(datum).replace("\n", linebreak)) - lines.append(" ") - - lines.append("
%s
%s
") - - return self._unicode("\n").join(lines) - - def _get_formatted_html_string(self, options): - - lines = [] - lpad, rpad = self._get_padding_widths(options) - if options["xhtml"]: - linebreak = "
" - else: - linebreak = "
" - - open_tag = [] - open_tag.append("") - lines.append("".join(open_tag)) - - # Headers - if options["header"]: - lines.append(" ") - for field in self._field_names: - if options["fields"] and field not in options["fields"]: - continue - lines.append(" %s" % (lpad, rpad, escape(field).replace("\n", linebreak))) - lines.append(" ") - - # Data - rows = self._get_rows(options) - formatted_rows = self._format_rows(rows, options) - aligns = [] - valigns = [] - for field in self._field_names: - aligns.append({ "l" : "left", "r" : "right", "c" : "center" }[self._align[field]]) - valigns.append({"t" : "top", "m" : "middle", "b" : "bottom"}[self._valign[field]]) - for row in formatted_rows: - lines.append(" ") - for field, datum, align, valign in zip(self._field_names, row, aligns, valigns): - if options["fields"] and field not in options["fields"]: - continue - lines.append(" %s" % (lpad, rpad, align, valign, escape(datum).replace("\n", linebreak))) - lines.append(" ") - lines.append("") - - return self._unicode("\n").join(lines) - -############################## -# UNICODE WIDTH FUNCTIONS # -############################## - -def _char_block_width(char): - # Basic Latin, which is probably the most common case - #if char in xrange(0x0021, 0x007e): - #if char >= 0x0021 and char <= 0x007e: - if 0x0021 <= char <= 0x007e: - return 1 - # Chinese, Japanese, Korean (common) - if 0x4e00 <= char <= 0x9fff: - return 2 - # Hangul - if 0xac00 <= char <= 0xd7af: - return 2 - # Combining? - if unicodedata.combining(uni_chr(char)): - return 0 - # Hiragana and Katakana - if 0x3040 <= char <= 0x309f or 0x30a0 <= char <= 0x30ff: - return 2 - # Full-width Latin characters - if 0xff01 <= char <= 0xff60: - return 2 - # CJK punctuation - if 0x3000 <= char <= 0x303e: - return 2 - # Backspace and delete - if char in (0x0008, 0x007f): - return -1 - # Other control characters - elif char in (0x0000, 0x001f): - return 0 - # Take a guess - return 1 - -def _str_block_width(val): - - return sum(itermap(_char_block_width, itermap(ord, _re.sub("", val)))) - -############################## -# TABLE FACTORIES # -############################## - -def from_csv(fp, field_names = None, **kwargs): - - dialect = csv.Sniffer().sniff(fp.read(1024)) - fp.seek(0) - reader = csv.reader(fp, dialect) - - table = PrettyTable(**kwargs) - if field_names: - table.field_names = field_names - else: - if py3k: - table.field_names = [x.strip() for x in next(reader)] - else: - table.field_names = [x.strip() for x in reader.next()] - - for row in reader: - table.add_row([x.strip() for x in row]) - - return table - -def from_db_cursor(cursor, **kwargs): - - if cursor.description: - table = PrettyTable(**kwargs) - table.field_names = [col[0] for col in cursor.description] - for row in cursor.fetchall(): - table.add_row(row) - return table - -class TableHandler(HTMLParser): - - def __init__(self, **kwargs): - HTMLParser.__init__(self) - self.kwargs = kwargs - self.tables = [] - self.last_row = [] - self.rows = [] - self.max_row_width = 0 - self.active = None - self.last_content = "" - self.is_last_row_header = False - - def handle_starttag(self,tag, attrs): - self.active = tag - if tag == "th": - self.is_last_row_header = True - - def handle_endtag(self,tag): - if tag in ["th", "td"]: - stripped_content = self.last_content.strip() - self.last_row.append(stripped_content) - if tag == "tr": - self.rows.append( - (self.last_row, self.is_last_row_header)) - self.max_row_width = max(self.max_row_width, len(self.last_row)) - self.last_row = [] - self.is_last_row_header = False - if tag == "table": - table = self.generate_table(self.rows) - self.tables.append(table) - self.rows = [] - self.last_content = " " - self.active = None - - - def handle_data(self, data): - self.last_content += data - - def generate_table(self, rows): - """ - Generates from a list of rows a PrettyTable object. - """ - table = PrettyTable(**self.kwargs) - for row in self.rows: - if len(row[0]) < self.max_row_width: - appends = self.max_row_width - len(row[0]) - for i in range(1,appends): - row[0].append("-") - - if row[1] == True: - self.make_fields_unique(row[0]) - table.field_names = row[0] - else: - table.add_row(row[0]) - return table - - def make_fields_unique(self, fields): - """ - iterates over the row and make each field unique - """ - for i in range(0, len(fields)): - for j in range(i+1, len(fields)): - if fields[i] == fields[j]: - fields[j] += "'" - -def from_html(html_code, **kwargs): - """ - Generates a list of PrettyTables from a string of HTML code. Each in - the HTML becomes one PrettyTable object. - """ - - parser = TableHandler(**kwargs) - parser.feed(html_code) - return parser.tables - -def from_html_one(html_code, **kwargs): - """ - Generates a PrettyTables from a string of HTML code which contains only a - single
- """ - - tables = from_html(html_code, **kwargs) - try: - assert len(tables) == 1 - except AssertionError: - raise Exception("More than one
in provided HTML code! Use from_html instead.") - return tables[0] - -############################## -# MAIN (TEST FUNCTION) # -############################## - -def main(): - - x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"]) - x.sortby = "Population" - x.reversesort = True - x.int_format["Area"] = "04d" - x.float_format = "6.1f" - x.align["City name"] = "l" # Left align city names - x.add_row(["Adelaide", 1295, 1158259, 600.5]) - x.add_row(["Brisbane", 5905, 1857594, 1146.4]) - x.add_row(["Darwin", 112, 120900, 1714.7]) - x.add_row(["Hobart", 1357, 205556, 619.5]) - x.add_row(["Sydney", 2058, 4336374, 1214.8]) - x.add_row(["Melbourne", 1566, 3806092, 646.9]) - x.add_row(["Perth", 5386, 1554769, 869.4]) - print(x) - -if __name__ == "__main__": - main() diff --git a/lib/prettytable/prettytable.pyc b/lib/prettytable/prettytable.pyc deleted file mode 100644 index d4d1f946e0e817b331c2ef509fd64c462e835cb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53141 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

    d`&OHd&+O3g+ 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 3 -try: - import sqlite3 - _have_sqlite = True -except ImportError: - _have_sqlite = False -if py3k: - import io as StringIO -else: - import StringIO -from math import pi, e, sqrt -import unittest - -class BuildEquivelanceTest(unittest.TestCase): - - """Make sure that building a table row-by-row and column-by-column yield the same results""" - - def setUp(self): - - # Row by row... - self.row = PrettyTable() - self.row.field_names = ["City name", "Area", "Population", "Annual Rainfall"] - self.row.add_row(["Adelaide",1295, 1158259, 600.5]) - self.row.add_row(["Brisbane",5905, 1857594, 1146.4]) - self.row.add_row(["Darwin", 112, 120900, 1714.7]) - self.row.add_row(["Hobart", 1357, 205556, 619.5]) - self.row.add_row(["Sydney", 2058, 4336374, 1214.8]) - self.row.add_row(["Melbourne", 1566, 3806092, 646.9]) - self.row.add_row(["Perth", 5386, 1554769, 869.4]) - - # Column by column... - self.col = PrettyTable() - self.col.add_column("City name",["Adelaide","Brisbane","Darwin","Hobart","Sydney","Melbourne","Perth"]) - self.col.add_column("Area", [1295, 5905, 112, 1357, 2058, 1566, 5386]) - self.col.add_column("Population", [1158259, 1857594, 120900, 205556, 4336374, 3806092, 1554769]) - self.col.add_column("Annual Rainfall",[600.5, 1146.4, 1714.7, 619.5, 1214.8, 646.9, 869.4]) - - # A mix of both! - self.mix = PrettyTable() - self.mix.field_names = ["City name", "Area"] - self.mix.add_row(["Adelaide",1295]) - self.mix.add_row(["Brisbane",5905]) - self.mix.add_row(["Darwin", 112]) - self.mix.add_row(["Hobart", 1357]) - self.mix.add_row(["Sydney", 2058]) - self.mix.add_row(["Melbourne", 1566]) - self.mix.add_row(["Perth", 5386]) - self.mix.add_column("Population", [1158259, 1857594, 120900, 205556, 4336374, 3806092, 1554769]) - self.mix.add_column("Annual Rainfall",[600.5, 1146.4, 1714.7, 619.5, 1214.8, 646.9, 869.4]) - - def testRowColEquivalenceASCII(self): - - self.assertEqual(self.row.get_string(), self.col.get_string()) - - def testRowMixEquivalenceASCII(self): - - self.assertEqual(self.row.get_string(), self.mix.get_string()) - - def testRowColEquivalenceHTML(self): - - self.assertEqual(self.row.get_html_string(), self.col.get_html_string()) - - def testRowMixEquivalenceHTML(self): - - self.assertEqual(self.row.get_html_string(), self.mix.get_html_string()) - -#class FieldnamelessTableTest(unittest.TestCase): -# -# """Make sure that building and stringing a table with no fieldnames works fine""" -# -# def setUp(self): -# self.x = PrettyTable() -# self.x.add_row(["Adelaide",1295, 1158259, 600.5]) -# self.x.add_row(["Brisbane",5905, 1857594, 1146.4]) -# self.x.add_row(["Darwin", 112, 120900, 1714.7]) -# self.x.add_row(["Hobart", 1357, 205556, 619.5]) -# self.x.add_row(["Sydney", 2058, 4336374, 1214.8]) -# self.x.add_row(["Melbourne", 1566, 3806092, 646.9]) -# self.x.add_row(["Perth", 5386, 1554769, 869.4]) -# -# def testCanStringASCII(self): -# self.x.get_string() -# -# def testCanStringHTML(self): -# self.x.get_html_string() -# -# def testAddFieldnamesLater(self): -# self.x.field_names = ["City name", "Area", "Population", "Annual Rainfall"] -# self.x.get_string() - -class CityDataTest(unittest.TestCase): - - """Just build the Australian capital city data example table.""" - - def setUp(self): - - self.x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"]) - self.x.add_row(["Adelaide",1295, 1158259, 600.5]) - self.x.add_row(["Brisbane",5905, 1857594, 1146.4]) - self.x.add_row(["Darwin", 112, 120900, 1714.7]) - self.x.add_row(["Hobart", 1357, 205556, 619.5]) - self.x.add_row(["Sydney", 2058, 4336374, 1214.8]) - self.x.add_row(["Melbourne", 1566, 3806092, 646.9]) - self.x.add_row(["Perth", 5386, 1554769, 869.4]) - -class OptionOverrideTests(CityDataTest): - - """Make sure all options are properly overwritten by get_string.""" - - def testBorder(self): - default = self.x.get_string() - override = self.x.get_string(border=False) - self.assertTrue(default != override) - - def testHeader(self): - default = self.x.get_string() - override = self.x.get_string(header=False) - self.assertTrue(default != override) - - def testHrulesAll(self): - default = self.x.get_string() - override = self.x.get_string(hrules=ALL) - self.assertTrue(default != override) - - def testHrulesNone(self): - - default = self.x.get_string() - override = self.x.get_string(hrules=NONE) - self.assertTrue(default != override) - -class OptionAttributeTests(CityDataTest): - - """Make sure all options which have an attribute interface work as they should. - Also make sure option settings are copied correctly when a table is cloned by - slicing.""" - - def testSetForAllColumns(self): - self.x.field_names = sorted(self.x.field_names) - self.x.align = "l" - self.x.max_width = 10 - self.x.start = 2 - self.x.end = 4 - self.x.sortby = "Area" - self.x.reversesort = True - self.x.header = True - self.x.border = False - self.x.hrule = True - self.x.int_format = "4" - self.x.float_format = "2.2" - self.x.padding_width = 2 - self.x.left_padding_width = 2 - self.x.right_padding_width = 2 - self.x.vertical_char = "!" - self.x.horizontal_char = "~" - self.x.junction_char = "*" - self.x.format = True - self.x.attributes = {"class" : "prettytable"} - assert self.x.get_string() == self.x[:].get_string() - - def testSetForOneColumn(self): - self.x.align["Rainfall"] = "l" - self.x.max_width["Name"] = 10 - self.x.int_format["Population"] = "4" - self.x.float_format["Area"] = "2.2" - assert self.x.get_string() == self.x[:].get_string() - -class BasicTests(CityDataTest): - - """Some very basic tests.""" - - def testNoBlankLines(self): - - """No table should ever have blank lines in it.""" - - string = self.x.get_string() - lines = string.split("\n") - self.assertTrue("" not in lines) - - def testAllLengthsEqual(self): - - """All lines in a table should be of the same length.""" - - string = self.x.get_string() - lines = string.split("\n") - lengths = [len(line) for line in lines] - lengths = set(lengths) - self.assertEqual(len(lengths),1) - -class NoBorderBasicTests(BasicTests): - - """Run the basic tests with border = False""" - - def setUp(self): - BasicTests.setUp(self) - self.x.border = False - -class NoHeaderBasicTests(BasicTests): - - """Run the basic tests with header = False""" - - def setUp(self): - BasicTests.setUp(self) - self.x.header = False - -class HrulesNoneBasicTests(BasicTests): - - """Run the basic tests with hrules = NONE""" - - def setUp(self): - BasicTests.setUp(self) - self.x.hrules = NONE - -class HrulesAllBasicTests(BasicTests): - - """Run the basic tests with hrules = ALL""" - - def setUp(self): - BasicTests.setUp(self) - self.x.hrules = ALL - -class EmptyTableTests(CityDataTest): - - """Make sure the print_empty option works""" - - def setUp(self): - CityDataTest.setUp(self) - self.y = PrettyTable() - self.y.field_names = ["City name", "Area", "Population", "Annual Rainfall"] - - def testPrintEmptyTrue(self): - assert self.y.get_string(print_empty=True) != "" - assert self.x.get_string(print_empty=True) != self.y.get_string(print_empty=True) - - def testPrintEmptyFalse(self): - assert self.y.get_string(print_empty=False) == "" - assert self.y.get_string(print_empty=False) != self.x.get_string(print_empty=False) - - def testInteractionWithBorder(self): - assert self.y.get_string(border=False, print_empty=True) == "" -class PresetBasicTests(BasicTests): - - """Run the basic tests after using set_style""" - - def setUp(self): - BasicTests.setUp(self) - self.x.set_style(MSWORD_FRIENDLY) - -class SlicingTests(CityDataTest): - - def setUp(self): - CityDataTest.setUp(self) - - def testSliceAll(self): - y = self.x[:] - assert self.x.get_string() == y.get_string() - - def testSliceFirstTwoRows(self): - y = self.x[0:2] - string = y.get_string() - assert len(string.split("\n")) == 6 - assert "Adelaide" in string - assert "Brisbane" in string - assert "Melbourne" not in string - assert "Perth" not in string - - def testSliceLastTwoRows(self): - y = self.x[-2:] - string = y.get_string() - assert len(string.split("\n")) == 6 - assert "Adelaide" not in string - assert "Brisbane" not in string - assert "Melbourne" in string - assert "Perth" in string - -class SortingTests(CityDataTest): - - def setUp(self): - CityDataTest.setUp(self) - - def testSortBy(self): - self.x.sortby = self.x.field_names[0] - old = self.x.get_string() - for field in self.x.field_names[1:]: - self.x.sortby = field - new = self.x.get_string() - assert new != old - - def testReverseSort(self): - for field in self.x.field_names: - self.x.sortby = field - self.x.reversesort = False - forward = self.x.get_string() - self.x.reversesort = True - backward = self.x.get_string() - forward_lines = forward.split("\n")[2:] # Discard header lines - backward_lines = backward.split("\n")[2:] - backward_lines.reverse() - assert forward_lines == backward_lines - - def testSortKey(self): - # Test sorting by length of city name - def key(vals): - vals[0] = len(vals[0]) - return vals - self.x.sortby = "City name" - self.x.sort_key = key - assert self.x.get_string().strip() == """+-----------+------+------------+-----------------+ -| City name | Area | Population | Annual Rainfall | -+-----------+------+------------+-----------------+ -| Perth | 5386 | 1554769 | 869.4 | -| Darwin | 112 | 120900 | 1714.7 | -| Hobart | 1357 | 205556 | 619.5 | -| Sydney | 2058 | 4336374 | 1214.8 | -| Adelaide | 1295 | 1158259 | 600.5 | -| Brisbane | 5905 | 1857594 | 1146.4 | -| Melbourne | 1566 | 3806092 | 646.9 | -+-----------+------+------------+-----------------+ -""".strip() - -class IntegerFormatBasicTests(BasicTests): - - """Run the basic tests after setting an integer format string""" - - def setUp(self): - BasicTests.setUp(self) - self.x.int_format = "04" - -class FloatFormatBasicTests(BasicTests): - - """Run the basic tests after setting a float format string""" - - def setUp(self): - BasicTests.setUp(self) - self.x.float_format = "6.2f" - -class FloatFormatTests(unittest.TestCase): - - def setUp(self): - self.x = PrettyTable(["Constant", "Value"]) - self.x.add_row(["Pi", pi]) - self.x.add_row(["e", e]) - self.x.add_row(["sqrt(2)", sqrt(2)]) - - def testNoDecimals(self): - self.x.float_format = ".0f" - self.x.caching = False - assert "." not in self.x.get_string() - - def testRoundTo5DP(self): - self.x.float_format = ".5f" - string = self.x.get_string() - assert "3.14159" in string - assert "3.141592" not in string - assert "2.71828" in string - assert "2.718281" not in string - assert "2.718282" not in string - assert "1.41421" in string - assert "1.414213" not in string - - def testPadWith2Zeroes(self): - self.x.float_format = "06.2f" - string = self.x.get_string() - assert "003.14" in string - assert "002.72" in string - assert "001.41" in string - -class BreakLineTests(unittest.TestCase): - def testAsciiBreakLine(self): - t = PrettyTable(['Field 1', 'Field 2']) - t.add_row(['value 1', 'value2\nsecond line']) - t.add_row(['value 3', 'value4']) - result = t.get_string(hrules=ALL) - assert result.strip() == """ -+---------+-------------+ -| Field 1 | Field 2 | -+---------+-------------+ -| value 1 | value2 | -| | second line | -+---------+-------------+ -| value 3 | value4 | -+---------+-------------+ -""".strip() - - t = PrettyTable(['Field 1', 'Field 2']) - t.add_row(['value 1', 'value2\nsecond line']) - t.add_row(['value 3\n\nother line', 'value4\n\n\nvalue5']) - result = t.get_string(hrules=ALL) - assert result.strip() == """ -+------------+-------------+ -| Field 1 | Field 2 | -+------------+-------------+ -| value 1 | value2 | -| | second line | -+------------+-------------+ -| value 3 | value4 | -| | | -| other line | | -| | value5 | -+------------+-------------+ -""".strip() - - t = PrettyTable(['Field 1', 'Field 2']) - t.add_row(['value 1', 'value2\nsecond line']) - t.add_row(['value 3\n\nother line', 'value4\n\n\nvalue5']) - result = t.get_string() - assert result.strip() == """ -+------------+-------------+ -| Field 1 | Field 2 | -+------------+-------------+ -| value 1 | value2 | -| | second line | -| value 3 | value4 | -| | | -| other line | | -| | value5 | -+------------+-------------+ -""".strip() - - def testHtmlBreakLine(self): - t = PrettyTable(['Field 1', 'Field 2']) - t.add_row(['value 1', 'value2\nsecond line']) - t.add_row(['value 3', 'value4']) - result = t.get_html_string(hrules=ALL) - assert result.strip() == """ -
- - - - - - - - - - - - -
Field 1Field 2
value 1value2
second line
value 3value4
-""".strip() - -class HtmlOutputTests(unittest.TestCase): - - def testHtmlOutput(self): - t = PrettyTable(['Field 1', 'Field 2', 'Field 3']) - t.add_row(['value 1', 'value2', 'value3']) - t.add_row(['value 4', 'value5', 'value6']) - t.add_row(['value 7', 'value8', 'value9']) - result = t.get_html_string() - assert result.strip() == """ - - - - - - - - - - - - - - - - - - - - - -
Field 1Field 2Field 3
value 1value2value3
value 4value5value6
value 7value8value9
-""".strip() - - def testHtmlOutputFormated(self): - t = PrettyTable(['Field 1', 'Field 2', 'Field 3']) - t.add_row(['value 1', 'value2', 'value3']) - t.add_row(['value 4', 'value5', 'value6']) - t.add_row(['value 7', 'value8', 'value9']) - result = t.get_html_string(format=True) - assert result.strip() == """ - - - - - - - - - - - - - - - - - - - - - -
Field 1Field 2Field 3
value 1value2value3
value 4value5value6
value 7value8value9
-""".strip() - -class CsvConstructorTest(BasicTests): - - def setUp(self): - - csv_string = """City name, Area , Population , Annual Rainfall - Sydney, 2058 , 4336374 , 1214.8 - Melbourne, 1566 , 3806092 , 646.9 - Brisbane, 5905 , 1857594 , 1146.4 - Perth, 5386 , 1554769 , 869.4 - Adelaide, 1295 , 1158259 , 600.5 - Hobart, 1357 , 205556 , 619.5 - Darwin, 0112 , 120900 , 1714.7""" - csv_fp = StringIO.StringIO(csv_string) - self.x = from_csv(csv_fp) - -if _have_sqlite: - class DatabaseConstructorTest(BasicTests): - - def setUp(self): - self.conn = sqlite3.connect(":memory:") - self.cur = self.conn.cursor() - self.cur.execute("CREATE TABLE cities (name TEXT, area INTEGER, population INTEGER, rainfall REAL)") - self.cur.execute("INSERT INTO cities VALUES (\"Adelaide\", 1295, 1158259, 600.5)") - self.cur.execute("INSERT INTO cities VALUES (\"Brisbane\", 5905, 1857594, 1146.4)") - self.cur.execute("INSERT INTO cities VALUES (\"Darwin\", 112, 120900, 1714.7)") - self.cur.execute("INSERT INTO cities VALUES (\"Hobart\", 1357, 205556, 619.5)") - self.cur.execute("INSERT INTO cities VALUES (\"Sydney\", 2058, 4336374, 1214.8)") - self.cur.execute("INSERT INTO cities VALUES (\"Melbourne\", 1566, 3806092, 646.9)") - self.cur.execute("INSERT INTO cities VALUES (\"Perth\", 5386, 1554769, 869.4)") - self.cur.execute("SELECT * FROM cities") - self.x = from_db_cursor(self.cur) - - def testNonSelectCurosr(self): - self.cur.execute("INSERT INTO cities VALUES (\"Adelaide\", 1295, 1158259, 600.5)") - assert from_db_cursor(self.cur) is None - -class HtmlConstructorTest(CityDataTest): - - def testHtmlAndBack(self): - html_string = self.x.get_html_string() - new_table = from_html(html_string)[0] - assert new_table.get_string() == self.x.get_string() - - def testHtmlOneAndBack(self): - html_string = self.x.get_html_string() - new_table = from_html_one(html_string) - assert new_table.get_string() == self.x.get_string() - - def testHtmlOneFailOnMany(self): - html_string = self.x.get_html_string() - html_string += self.x.get_html_string() - self.assertRaises(Exception, from_html_one, html_string) - -class PrintEnglishTest(CityDataTest): - - def testPrint(self): - print() - print(self.x) - -class PrintJapanestTest(unittest.TestCase): - - def setUp(self): - - self.x = PrettyTable(["Kanji", "Hiragana", "English"]) - self.x.add_row(["神戸", "こうべ", "Kobe"]) - self.x.add_row(["京都", "きょうと", "Kyoto"]) - self.x.add_row(["長崎", "ながさき", "Nagasaki"]) - self.x.add_row(["名古屋", "なごや", "Nagoya"]) - self.x.add_row(["大阪", "おおさか", "Osaka"]) - self.x.add_row(["札幌", "さっぽろ", "Sapporo"]) - self.x.add_row(["東京", "とうきょう", "Tokyo"]) - self.x.add_row(["横浜", "よこはま", "Yokohama"]) - - def testPrint(self): - print() - print(self.x) - -if __name__ == "__main__": - unittest.main() diff --git a/lib/simpy/__init__.py b/lib/simpy/__init__.py deleted file mode 100644 index 2d88e1b..0000000 --- a/lib/simpy/__init__.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -The ``simpy`` module provides SimPy's end-user API. It aggregates Simpy's most -important classes and methods. This is purely for your convenience. You can of -course also access everything (and more!) via their actual submodules. - - -Core classes and functions --------------------------- - -.. currentmodule:: simpy.core - -- :class:`Environment`: SimPy's central class. It contains - the simulation's state and lets the PEMs interact with it (i.e., - schedule events). - -.. currentmodule:: simpy.events - -- :class:`Interrupt`: This exception is thrown into a process if it gets - interrupted by another one. - - -Resources ---------- - -.. currentmodule:: simpy.resources.resource - -- :class:`Resource`: Can be used by a limited number of processes at a - time (e.g., a gas station with a limited number of fuel pumps). - -- :class:`PriorityResource`: Like :class:`Resource`, but waiting - processes are sorted by priority. - -- :class:`PreemptiveResource`: Version of :class:`Resource` with - preemption. - -.. currentmodule:: simpy.resources.container - -- :class:`Container`: Models the production and consumption of a - homogeneous, undifferentiated bulk. It may either be continuous (like - water) or discrete (like apples). - -.. currentmodule:: simpy.resources.store - -- :class:`Store`: Allows the production and consumption of discrete - Python objects. - -- :class:`FilterStore`: Like :class:`Store`, but items taken out of it - can be filtered with a user-defined function. - - -Monitoring ----------- - -.. currentmodule:: simpy.monitoring - -*[Not yet implemented]* - - -Other ------ - -.. currentmodule:: simpy - -.. autofunction:: test - -.. - :func:`test`: Run the test suite on the installed copy of Simpy. - -""" -from pkgutil import extend_path - -from simpy.core import Environment -from simpy.events import Interrupt -from simpy.resources.resource import ( - Resource, PriorityResource, PreemptiveResource) -from simpy.resources.container import Container -from simpy.resources.store import Store, FilterStore - - -__path__ = extend_path(__path__, __name__) -__all__ = [ - 'test', - 'Environment', - 'Interrupt', - 'Resource', 'PriorityResource', 'PreemptiveResource', - 'Container', - 'Store', 'FilterStore', -] -__version__ = '3.0.4' - - -def test(): - """Runs SimPy's test suite via `py.test `_.""" - import os.path - try: - import pytest - except ImportError: - print('You need pytest to run the tests. Try "pip install pytest".') - else: - pytest.main([os.path.dirname(__file__)]) diff --git a/lib/simpy/__init__.pyc b/lib/simpy/__init__.pyc deleted file mode 100644 index a30b1f569529ede27cdaa03fa0b21377f32fe8ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3029 zcmcIm-EI^&6h5c@VHHRn~F8+uQlT}%Gasbp#DZPUZ?yF6`R!Gq+*NuTU3P9 z51Y9S%Fj}&c`A0Oze9m+k=!MDfnMXpUCK5o+oCL_>?~#5l%1pO zJY_qS?a~;^FH&}a`j;rXNWTH_Wy&tmVdpX%@%si*_;@14aA;LgO@~8K=&a7AsEj^Q znY7|i6$jHVtdM1Py|&VbI|mOU@xX~>JT`KixMoeoYYJ^$2tC@kq;w+96KgS@l$j`` zo9N6&;_*aTfxoIYGM|c(He#x4BT`+S$WqBNm67;G*CI_yp+{kgNh?K?TP>0_l@?G= zq?x*jD#xPZb!#I(yDCmpBHTnOgLSS>a$)PE#u^)iVYrV~Z$KK=W$KhJZFv2?zc7p< zk=Dk@(lxreUE!&UQs@iA>!Rz;><;ghC(7uuz{+8FL0gKU0T#c=^9Dm*qHwBUw5+Vo z6UG4otplH)uUtCo=MU~Z1Yf0-23CpZ%1wlFqN5@i?Lmc2C(=6%q?FEHg{ALB+$>qe z{DAeEx?;rMG4e$!D+a?ZaucJUqgOgm&k^C-S0e^620k3`fw2OaiK8iyYuIFjE+tdm zlNR2?t|h~#z%%pJ(Y&_`b*>9Y_YoOKQXm?eLq)C%#R$r}IO5YsbFdtIh*YLp6;gC$ zG>-OQ%Q$JcWavN{ZMBzZ-NmPWVOFEK1i|Ef*&6*fW$mcbfL$xl)AQiqAoKv8cDu93V{u8 z)%mf%1Vu6x60r<#!IKO@l{NN=P7W%e=Q-A!s{+{}Q#Lgc*TPSUq^fdxdYmt9v+k`+ zddLFUbSKaC^Z#Pg{B%G%K+a+AkDkfYaY|V_>syrr?p*mB(a;E+P(Y9tI5atyP=n9_ zXUah{r#jFu@>)RCEF?MIUeDx6m5|M|72m6ex>PWPqiUJCPPtjda(#I1=_BpLR62n~ zol8zvGW+!!wEn>EXcWF*m!C;$r{{Q(4^aY~mxB-%cZVzi%e}hvIgE$M#xNh5!Ap?; zT$1OoE7jGMi9nsHrZ7(ZgI^~=b#iCX$QKTkC$17VSqfKDZ6oBWyvSvpHgDcUGrt>d z7lXI2t-I)jZ{M<|!o?lvv{>Of8t+VxYsKSkji+H^k5Tt{agQPNSl;7pJ(OAMo#BRI zS(bejeHqmv~-nc!CBk!a~_G@V4Qe8?ovRNrmE6j@9x#F9t_`FgTiiLOjY~|-shZ%9) zXdAJI0h*}M<4JztpAz?sL8@f<1?r~n_{F?2Q>IMtwqRQ07#B#!apNv#RofoNPzPVr z*dBWO-N6&wEIYV^1Y8X6Ydyw;=dMAj9Xv(}PgSY3=t-vrkEK-uTa^R<3VI~nAXi5N zU;hVjjCC%KqiWjY_rv+e2)vDPoar=`>1jsE~Lv8H}Aa_a_*ydtmQll3Z?M@=@?pb$XtnyEGNiisLqIG zz5%^qnF*iS5y!a1=rhZ4jAt2Fr#SX}ELr!x|A~5!=~nYJyM31p`V`GZu-n>&3qD4B SrM=T?!y)Zp3*UBYqx~=ajey?( diff --git a/lib/simpy/_compat.py b/lib/simpy/_compat.py deleted file mode 100644 index 2a78443..0000000 --- a/lib/simpy/_compat.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Compatibility helpers for older Python versions. - -""" -import sys - - -PY2 = sys.version_info[0] == 2 - - -if PY2: - # Python 2.x does not report exception chains. To emulate the behaviour of - # Python 3 the functions format_chain and print_chain are added. The latter - # function is used to override the exception hook of Python 2.x. - from traceback import format_exception - - def format_chain(exc_type, exc_value, exc_traceback): - if hasattr(exc_value, '__cause__') and exc_value.__cause__: - cause = exc_value.__cause__ - if hasattr(exc_value, '__traceback__'): - traceback = exc_value.__traceback__ - else: - traceback = None - lines = format_chain(type(cause), cause, traceback) - lines += ('\nThe above exception was the direct cause of the ' - 'following exception:\n\n') - else: - lines = [] - - return lines + format_exception(exc_type, exc_value, exc_traceback) - - def print_chain(exc_type, exc_value, exc_traceback): - sys.stderr.write(''.join(format_chain(exc_type, exc_value, - exc_traceback))) - sys.stderr.flush() - - sys.excepthook = print_chain diff --git a/lib/simpy/_compat.pyc b/lib/simpy/_compat.pyc deleted file mode 100644 index 795c7fad839a2d4474bd76b154e58f538a9b2211..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1251 zcmcIj!EVz)5S_IXw=oSZ^Z?v^^rZ=E5f!LHNIh_iDu+uWp_OBA5;u<5@@`rpr3V!L zm=AzA+oTnDi0tv~+u7N9GcW$HZunt77-qEndH8j<_sph)QdW` z#keR7yNYJ2tW{&8iEbiY=BkOVR(7VV=oV<9E7J?Z;x~SS0*9&Kl4Aj5k6>_Wky@#~ zXR5Y{&U}Eu_PxZ{{D84UYtRz3@u-=yelST%x(Cwc_MF2*=oFz2vH`8#m1|`5>D4f9p^Aa+z)G2=OQWQ;UAMALnyGP`&80M7 zfP`OXDoV%tRz;7jqPx^Y7I;ar@2eVmPRBp$vYL#GvV(z2mxuForup_{W+q7INN5F0ED4<0|t~eL@ zNX_A8Q7IE6fShq;`%BaG%VONG-ONvW=Y3OHbyA}r=~Nl47WL}ntoOF}?xbE}zo(|t z2edCUC!}7zO8DLvc+E@BFJ6m|I1~q70I%x@o>Rq68BP`N5e8L21}tz`XrT4i&@gB` zVjI+^4e~+@!%g*k?#xH<+z-BaW~%Tq<{|D`LLc$wbOrXHq858DL>-|9`TmCfahnSJ$TrXLI6k ugpy+p4q+aoI@5aoMB|^2*=g}&q4Q;_KCy@m7aNEJ|JV;&M_%W!)BXbm9Tb!R diff --git a/lib/simpy/core.py b/lib/simpy/core.py deleted file mode 100644 index f03a11b..0000000 --- a/lib/simpy/core.py +++ /dev/null @@ -1,219 +0,0 @@ -""" -This module contains the implementation of SimPy's core classes. The most -important ones are directly importable via :mod:`simpy`. - -""" -import types -from heapq import heappush, heappop -from itertools import count - -from simpy.events import (AllOf, AnyOf, Event, Process, Timeout, URGENT, - NORMAL) - - -Infinity = float('inf') #: Convenience alias for infinity - - -class BoundClass(object): - """Allows classes to behave like methods. - - The ``__get__()`` descriptor is basically identical to - ``function.__get__()`` and binds the first argument of the ``cls`` to the - descriptor instance. - - """ - def __init__(self, cls): - self.cls = cls - - def __get__(self, obj, type=None): - if obj is None: - return self.cls - return types.MethodType(self.cls, obj) - - @staticmethod - def bind_early(instance): - """Bind all :class:`BoundClass` attributes of the *instance's* class - to the instance itself to increase performance. - - """ - cls = type(instance) - for name, obj in cls.__dict__.items(): - if type(obj) is BoundClass: - bound_class = getattr(instance, name) - setattr(instance, name, bound_class) - - -class EmptySchedule(Exception): - """Thrown by the :class:`Environment` if there are no further events to be - processed.""" - pass - - -class BaseEnvironment(object): - """The abstract definition of an environment. - - An implementation must at least provide the means to access the current - time of the environment (see :attr:`now`) and to schedule (see - :meth:`schedule()`) as well as execute (see :meth:`step()` and - :meth:`run()`) events. - - The class is meant to be subclassed for different execution environments. - For example, SimPy defines a :class:`Environment` for simulations with - a virtual time and and a :class:`~simpy.rt.RealtimeEnvironment` that - schedules and executes events in real (e.g., wallclock) time. - - """ - @property - def now(self): - """The current time of the environment.""" - raise NotImplementedError(self) - - @property - def active_process(self): - """The currently active process of the environment.""" - raise NotImplementedError(self) - - def schedule(self, event, priority=NORMAL, delay=0): - """Schedule an *event* with a given *priority* and a *delay*. - - There are two default priority values, :data:`~simpy.events.URGENT` and - :data:`~simpy.events.NORMAL`.""" - raise NotImplementedError(self) - - def step(self): - """Process the next event.""" - raise NotImplementedError(self) - - def run(self, until=None): - """Executes :meth:`step()` until the given criterion *until* is met. - - - If it is ``None`` (which is the default) this method will return if - there are no further events to be processed. - - - If it is an :class:`~simpy.events.Event` the method will continue - stepping until this event has been triggered and will return its - value. - - - If it can be converted to a number the method will continue stepping - until the environment's time reaches *until*. - - """ - if until is None: - until = Event(self) - elif not isinstance(until, Event): - at = float(until) - - if at <= self.now: - raise ValueError('until(=%s) should be > the current ' - 'simulation time.' % at) - - # Schedule the event with before all regular timeouts. - until = Event(self) - until.ok = True - until._value = None - self.schedule(until, URGENT, at - self.now) - - until.callbacks.append(_stop_simulate) - - try: - while True: - self.step() - except EmptySchedule: - pass - - if not until.triggered: - return None - - if not until.ok: - raise until.value - - return until.value - - -class Environment(BaseEnvironment): - """Inherits :class:`BaseEnvironment` and implements a simulation - environment which simulates the passing of time by stepping from event to - event. - - You can provide an *initial_time* for the environment. By defaults, it - starts at ``0``. - - This class also provides aliases for common event types, for example - :attr:`process`, :attr:`timeout` and :attr:`event`. - - """ - def __init__(self, initial_time=0): - self._now = initial_time - self._queue = [] # Thelist of all currently scheduled events. - self._eid = count() # Counter for event IDs - self._active_proc = None - - # Bind all BoundClass instances to "self" to improve performance. - BoundClass.bind_early(self) - - @property - def now(self): - """The current simulation time.""" - return self._now - - @property - def active_process(self): - """The currently active process of the environment.""" - return self._active_proc - - process = BoundClass(Process) - timeout = BoundClass(Timeout) - event = BoundClass(Event) - all_of = BoundClass(AllOf) - any_of = BoundClass(AnyOf) - - def exit(self, value=None): - """Convenience function provided for Python versions prior to 3.3. Stop - the current process, optionally providing a ``value``. - - .. note:: - - From Python 3.3, you can use ``return value`` instead. - - """ - raise StopIteration(value) - - def schedule(self, event, priority=NORMAL, delay=0): - """Schedule an *event* with a given *priority* and a *delay*.""" - heappush(self._queue, - (self._now + delay, priority, next(self._eid), event)) - - def peek(self): - """Get the time of the next scheduled event. Return :data:`Infinity` - if there is no further event.""" - try: - return self._queue[0][0] - except IndexError: - return Infinity - - def step(self): - """Process the next event. - - Raise an :exc:`EmptySchedule` if no further events are available. - - """ - try: - self._now, _, _, event = heappop(self._queue) - except IndexError: - raise EmptySchedule() - - # Process callbacks of the event. - for callback in event.callbacks: - callback(event) - event.callbacks = None - - if not event.ok and not hasattr(event, 'defused'): - # The event has failed, check if it is defused. - # Raise the value if not. - raise event._value - - -def _stop_simulate(event): - """Used as callback in :meth:`BaseEnvironment.simulate()` to stop the - simulation when the *until* event occurred.""" - raise EmptySchedule() diff --git a/lib/simpy/core.pyc b/lib/simpy/core.pyc deleted file mode 100644 index ce71042db5890c22757b36b912226c1cc43731ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7785 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 L*tq!a#jXDWnB?42 diff --git a/lib/simpy/events.py b/lib/simpy/events.py deleted file mode 100644 index a46c46e..0000000 --- a/lib/simpy/events.py +++ /dev/null @@ -1,533 +0,0 @@ -""" -This *events* module contains the various event type used by the SimPy core. - -The base class for all events is :class:`Event`. Though it can be directly -used, there are several specialized subclasses of it: - -- :class:`Timeout`: is scheduled with a certain delay and lets processes hold - their state for a certain amount of time. - -- :class:`Initialize`: Initializes a new :class:`Process`. - -- :class:`Process`: Processes are also modeled as an event so other processes - can wait until another one finishes. - -- :class:`Condition`: Events can be concatenated with ``|`` an ``&`` to either - wait until one or both of the events are triggered. - -- :class:`AllOf`: Special case of :class:`Condition`; wait until a list of - events has been triggered. - -- :class:`AnyOf`: Special case of :class:`Condition`; wait until one of a list - of events has been triggered. - -This module also defines the :exc:`Interrupt` exception. - -""" -from inspect import isgenerator -from collections import OrderedDict - -from simpy._compat import PY2 - -if PY2: - import sys - - -PENDING = object() -"""Unique object to identify pending values of events.""" - -URGENT = 0 -"""Priority of interrupts and process initialization events.""" -NORMAL = 1 -"""Default priority used by events.""" - - -class Event(object): - """Base class for all events. - - Every event is bound to an environment *env* (see - :class:`~simpy.core.BaseEnvironment`) and has an optional *value*. - - An event has a list of :attr:`callbacks`. A callback can be any callable - that accepts a single argument which is the event instances the callback - belongs to. This list is not exclusively for SimPy internals---you can also - append custom callbacks. All callbacks are executed in the order that they - were added when the event is processed. - - This class also implements ``__and__()`` (``&``) and ``__or__()`` (``|``). - If you concatenate two events using one of these operators, - a :class:`Condition` event is generated that lets you wait for both or one - of them. - - """ - def __init__(self, env): - self.env = env - """The :class:`~simpy.core.Environment` the event lives in.""" - self.callbacks = [] - """List of functions that are called when the event is processed.""" - self._value = PENDING - - def __repr__(self): - """Return the description of the event (see :meth:`_desc`) with the id - of the event.""" - return '<%s object at 0x%x>' % (self._desc(), id(self)) - - def _desc(self): - """Return a string *Event()*.""" - return '%s()' % self.__class__.__name__ - - @property - def triggered(self): - """Becomes ``True`` if the event has been triggered and its callbacks - are about to be invoked.""" - return self._value is not PENDING - - @property - def processed(self): - """Becomes ``True`` if the event has been processed (e.g., its - callbacks have been invoked).""" - return self.callbacks is None - - @property - def value(self): - """The value of the event if it is available. - - The value is available when the event has been triggered. - - Raise a :exc:`AttributeError` if the value is not yet available. - - """ - if self._value is PENDING: - raise AttributeError('Value of %s is not yet available' % self) - return self._value - - def trigger(self, event): - """Triggers the event with the state and value of the provided *event*. - - This method can be used directly as a callback function. - - """ - self.ok = event.ok - self._value = event._value - self.env.schedule(self) - - def succeed(self, value=None): - """Schedule the event and mark it as successful. Return the event - instance. - - You can optionally pass an arbitrary ``value`` that will be sent into - processes waiting for that event. - - Raise a :exc:`RuntimeError` if this event has already been scheduled. - - """ - if self._value is not PENDING: - raise RuntimeError('%s has already been triggered' % self) - - self.ok = True - self._value = value - self.env.schedule(self) - return self - - def fail(self, exception): - """Schedule the event and mark it as failed. Return the event instance. - - The ``exception`` will be thrown into processes waiting for that event. - - Raise a :exc:`ValueError` if ``exception`` is not an :exc:`Exception`. - - Raise a :exc:`RuntimeError` if this event has already been scheduled. - - """ - if self._value is not PENDING: - raise RuntimeError('%s has already been triggered' % self) - if not isinstance(exception, BaseException): - raise ValueError('%s is not an exception.' % exception) - self.ok = False - self._value = exception - self.env.schedule(self) - return self - - def __and__(self, other): - """Return ``True`` if this event and *other* are triggered.""" - return Condition(self.env, Condition.all_events, [self, other]) - - def __or__(self, other): - """Return ``True`` if this event or *other is triggered, or both.""" - return Condition(self.env, Condition.any_events, [self, other]) - - -class Timeout(Event): - """An :class:`Event` that is scheduled with a certain *delay* after its - creation. - - This event can be used by processes to wait (or hold their state) for - *delay* time steps. It is immediately scheduled at ``env.now + delay`` and - has thus (in contrast to :class:`Event`) no *success()* or *fail()* method. - - """ - def __init__(self, env, delay, value=None): - if delay < 0: - raise ValueError('Negative delay %s' % delay) - # NOTE: The following initialization code is inlined from - # Event.__init__() for performance reasons. - self.env = env - self.callbacks = [] - self._value = value - self._delay = delay - self.ok = True - env.schedule(self, NORMAL, delay) - - def _desc(self): - """Return a string *Timeout(delay[, value=value])*.""" - return '%s(%s%s)' % (self.__class__.__name__, self._delay, - '' if self._value is None else - (', value=%s' % self._value)) - - -class Initialize(Event): - """Initializes a process. Only used internally by :class:`Process`.""" - def __init__(self, env, process): - # NOTE: The following initialization code is inlined from - # Event.__init__() for performance reasons. - self.env = env - self.callbacks = [process._resume] - self._value = None - - # The initialization events needs to be scheduled as urgent so that it - # will be handled before interrupts. Otherwise a process whose - # generator has not yet been started could be interrupted. - self.ok = True - env.schedule(self, URGENT) - - -class Process(Event): - """A *Process* is a wrapper for the process *generator* (that is returned - by a *process function*) during its execution. - - It also contains internal and external status information and is used for - process interaction, e.g., for interrupts. - - ``Process`` inherits :class:`Event`. You can thus wait for the termination - of a process by simply yielding it from your process function. - - An instance of this class is returned by - :meth:`simpy.core.Environment.process()`. - - """ - def __init__(self, env, generator): - if not isgenerator(generator): - raise ValueError('%s is not a generator.' % generator) - - # NOTE: The following initialization code is inlined from - # Event.__init__() for performance reasons. - self.env = env - self.callbacks = [] - self._value = PENDING - - self._generator = generator - - # Schedule the start of the execution of the process. - self._target = Initialize(env, self) - - def _desc(self): - """Return a string *Process(process_func_name)*.""" - return '%s(%s)' % (self.__class__.__name__, self._generator.__name__) - - @property - def target(self): - """The event that the process is currently waiting for. - - May be ``None`` if the process was just started or interrupted and did - not yet yield a new event. - - """ - return self._target - - @property - def is_alive(self): - """``True`` until the process generator exits.""" - return self._value is PENDING - - def interrupt(self, cause=None): - """Interupt this process optionally providing a *cause*. - - A process cannot be interrupted if it already terminated. A process - can also not interrupt itself. Raise a :exc:`RuntimeError` in these - cases. - - """ - if self._value is not PENDING: - raise RuntimeError('%s has terminated and cannot be interrupted.' % - self) - - if self is self.env.active_process: - raise RuntimeError('A process is not allowed to interrupt itself.') - - # Create an event for this interrupt and schedule it as being urgent. - event = self.env.event() - event._value = Interrupt(cause) - event.ok = False - # Interrupts do not cause the simulation to crash. - event.defused = True - event.callbacks.append(self._resume) - self.env.schedule(event, URGENT) - - def _resume(self, event): - """Resume the execution of the process. - - Send the result of the event the process was waiting for into the - process generator and retrieve a new event from it. Register this - method as callback for that event. - - If the process generator exits or raises an exception, terminate this - process. Also schedule this process to notify all registered callbacks, - that the process terminated. - - """ - # Ignore dead processes. Multiple concurrently scheduled interrupts - # cause this situation. If the process dies while handling the first - # one, the remaining interrupts must be discarded. - if self._value is not PENDING: - return - - # If the current target (e.g. an interrupt) isn't the one the process - # expects, remove it from the original events joiners list. - if self._target is not event: - self._target.callbacks.remove(self._resume) - - # Mark the current process as active. - self.env._active_proc = self - - while True: - # Get next event from process - try: - if event.ok: - event = self._generator.send(event._value) - else: - # The process has no choice but to handle the failed event - # (or fail itself). - event.defused = True - event = self._generator.throw(event._value) - except StopIteration as e: - # Process has terminated. - event = None - self.ok = True - self._value = e.args[0] if len(e.args) else None - self.env.schedule(self) - break - except BaseException as e: - # Process has failed. - event = None - self.ok = False - self._value = type(e)(*e.args) - self._value.__cause__ = e - if PY2: - self._value.__traceback__ = sys.exc_info()[2] - self.env.schedule(self) - break - - # Process returned another event to wait upon. - try: - # Be optimistic and blindly access the callbacks attribute. - if event.callbacks is not None: - # The event has not yet been triggered. Register callback - # to resume the process if that happens. - event.callbacks.append(self._resume) - break - except AttributeError: - # Our optimism didn't work out, figure out what went wrong and - # inform the user. - if not hasattr(event, 'callbacks'): - msg = 'Invalid yield value "%s"' % event - - descr = _describe_frame(self._generator.gi_frame) - error = RuntimeError('\n%s%s' % (descr, msg)) - # Drop the AttributeError as the cause for this exception. - error.__cause__ = None - raise error - - self._target = event - self.env._active_proc = None - - -class Condition(Event): - """A *Condition* :class:`Event` groups several *events* and is triggered if - a given condition (implemented by the *evaluate* function) becomes true. - - The value of the condition is a dictionary that maps the input events to - their respective values. It only contains entries for those events that - occurred until the condition was met. - - If one of the events fails, the condition also fails and forwards the - exception of the failing event. - - The ``evaluate`` function receives the list of target events and the - number of processed events in this list. If it returns ``True``, the - condition is scheduled. The :func:`Condition.all_events()` and - :func:`Condition.any_events()` functions are used to implement *and* - (``&``) and *or* (``|``) for events. - - Conditions events can be nested. - - """ - def __init__(self, env, evaluate, events): - super(Condition, self).__init__(env) - self._evaluate = evaluate - self._events = [] - self._count = 0 - - for event in events: - self._add_event(event) - - # Register a callback which will update the value of this - # condition once it is being processed. - self.callbacks.append(self._collect_values) - - if (self._value is PENDING and - self._evaluate(self._events, self._count)): - # Immediately trigger the condition if it is already met. - self.succeed() - - def _desc(self): - """Return a string *Condition(and_or_or, [events])*.""" - return '%s(%s, %s)' % (self.__class__.__name__, - self._evaluate.__name__, self._events) - - def _get_values(self): - """Recursively collect the current values of all nested conditions into - a flat dictionary.""" - values = OrderedDict() - - for event in self._events: - if isinstance(event, Condition): - values.update(event._get_values()) - elif event.callbacks is None: - values[event] = event._value - - return values - - def _collect_values(self, event): - """Update the final value of this condition.""" - if event.ok: - self._value = OrderedDict() - self._value.update(self._get_values()) - - def _add_event(self, event): - """Add another *event* to the condition. - - Raise a :exc:`ValueError` if *event* belongs to a different - environment. Raise a :exc:`RuntimeError` if either this condition has - already been processed.""" - - if self.env != event.env: - raise ValueError('It is not allowed to mix events from different ' - 'environments') - if self.callbacks is None: - raise RuntimeError('%s has already been processed' % self) - - self._events.append(event) - - if event.callbacks is None: - self._check(event) - else: - event.callbacks.append(self._check) - - return self - - def _check(self, event): - """Check if the condition was already met and schedule the *event* if - so.""" - if self._value is not PENDING: - return - - self._count += 1 - - if not event.ok: - # Abort if the event has failed. - event.defused = True - self.fail(event._value) - elif self._evaluate(self._events, self._count): - # The condition has been met. The _collect_values callback will - # populate set the value once this condition gets processed. - self.succeed() - - def __iand__(self, other): - if self._evaluate is not Condition.all_events: - # Use self.__and__ - return NotImplemented - - return self._add_event(other) - - def __ior__(self, other): - if self._evaluate is not Condition.any_events: - # Use self.__or__ - return NotImplemented - - return self._add_event(other) - - @staticmethod - def all_events(events, count): - """A condition function that returns ``True`` if all *events* have - been triggered.""" - return len(events) == count - - @staticmethod - def any_events(events, count): - """A condition function that returns ``True`` if at least one of - *events* has been triggered.""" - return count > 0 or len(events) == 0 - - -class AllOf(Condition): - """A :class:`Condition` event that waits for all *events*.""" - def __init__(self, env, events): - super(AllOf, self).__init__(env, Condition.all_events, events) - - -class AnyOf(Condition): - """A :class:`Condition` event that waits until the first of *events* is - triggered.""" - def __init__(self, env, events): - super(AnyOf, self).__init__(env, Condition.any_events, events) - - -class Interrupt(Exception): - """This exceptions is sent into a process if it was interrupted by another - process (see :func:`Process.interrupt()`). - - *cause* may be none if no cause was explicitly passed to - :func:`Process.interrupt()`. - - An interrupt has a higher priority as a normal event. Thus, if a process - has a normal event and an interrupt scheduled at the same time, the - interrupt will always be thrown into the process first. - - If a process is interrupted multiple times at the same time, all interrupts - will be thrown into the process in the same order as they occurred. - - """ - def __init__(self, cause): - super(Interrupt, self).__init__(cause) - - def __str__(self): - return '%s(%r)' % (self.__class__.__name__, self.cause) - - @property - def cause(self): - """The cause of the interrupt or ``None`` if no cause was provided.""" - return self.args[0] - - -def _describe_frame(frame): - """Print filename, line number and function name of a stack frame.""" - filename, name = frame.f_code.co_filename, frame.f_code.co_name - lineno = frame.f_lineno - - with open(filename) as f: - for no, line in enumerate(f): - if no + 1 == lineno: - break - - return ' File "%s", line %d, in %s\n %s\n' % (filename, lineno, name, - line.strip()) diff --git a/lib/simpy/events.pyc b/lib/simpy/events.pyc deleted file mode 100644 index aa3d3a55ddf621e63a60551f9b45dcd2ea92c537..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20674 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 AkN^Mx diff --git a/lib/simpy/resources/base.py b/lib/simpy/resources/base.py deleted file mode 100644 index 5ea37c0..0000000 --- a/lib/simpy/resources/base.py +++ /dev/null @@ -1,201 +0,0 @@ -""" -This module contains the base classes for Simpy's resource system. - -:class:`BaseResource` defines the abstract base resource. The request for -putting something into or getting something out of a resource is modeled as an -event that has to be yielded by the requesting process. :class:`Put` and -:class:`Get` are the base event types for this. - -""" -from simpy.core import BoundClass -from simpy.events import Event - - -class Put(Event): - """The base class for all put events. - - It receives the *resource* that created the event. - - This event (and all of its subclasses) can act as context manager and can - be used with the :keyword:`with` statement to automatically cancel a put - request if an exception or an :class:`simpy.events.Interrupt` occurs: - - .. code-block:: python - - with res.put(item) as request: - yield request - - It is not used directly by any resource, but rather sub-classed for each - type. - - """ - def __init__(self, resource): - super(Put, self).__init__(resource._env) - self.resource = resource - self.proc = self.env.active_process - - resource.put_queue.append(self) - self.callbacks.append(resource._trigger_get) - resource._trigger_put(None) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - # If the request has been interrupted, remove it from the queue: - if not self.triggered: - self.resource.put_queue.remove(self) - - cancel = __exit__ - """Cancel the current put request. - - This method has to be called if a process received an - :class:`~simpy.events.Interrupt` or an exception while yielding this event - and is not going to yield this event again. - - If the event was created in a :keyword:`with` statement, this method is - called automatically. - - """ - - -class Get(Event): - """The base class for all get events. - - It receives the *resource* that created the event. - - This event (and all of its subclasses) can act as context manager and can - be used with the :keyword:`with` statement to automatically cancel a get - request if an exception or an :class:`simpy.events.Interrupt` occurs: - - .. code-block:: python - - with res.get() as request: - yield request - - It is not used directly by any resource, but rather sub-classed for each - type. - - """ - def __init__(self, resource): - super(Get, self).__init__(resource._env) - self.resource = resource - self.proc = self.env.active_process - - resource.get_queue.append(self) - self.callbacks.append(resource._trigger_put) - resource._trigger_get(None) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - # If the request has been interrupted, remove it from the queue: - if not self.triggered: - self.resource.get_queue.remove(self) - - cancel = __exit__ - """Cancel the current get request. - - This method has to be called if a process received an - :class:`~simpy.events.Interrupt` or an exception while yielding this event - and is not going to yield this event again. - - If the event was created in a :keyword:`with` statement, this method is - called automatically. - - """ - - -class BaseResource(object): - """This is the abstract base class for all SimPy resources. - - All resources are bound to a specific :class:`~simpy.core.Environment` - *env*. - - You can :meth:`put()` something into the resources or :meth:`get()` - something out of it. Both methods return an event that the requesting - process has to ``yield``. - - If a put or get operation can be performed immediately (because the - resource is not full (put) or not empty (get)), that event is triggered - immediately. - - If a resources is too full or too empty to perform a put or get request, - the event is pushed to the *put_queue* or *get_queue*. An event is popped - from one of these queues and triggered as soon as the corresponding - operation is possible. - - :meth:`put()` and :meth:`get()` only provide the user API and the general - framework and should not be overridden in subclasses. The actual behavior - for what happens when a put/get succeeds should rather be implemented in - :meth:`_do_put()` and :meth:`_do_get()`. - - """ - - PutQueue = list - """The type to be used for the :attr:`put_queue`. This can either be - a plain :class:`list` (default) or a subclass of it.""" - - GetQueue = list - """The type to be used for the :attr:`get_queue`. This can either be - a plain :class:`list` (default) or a subclass of it.""" - - def __init__(self, env): - self._env = env - self.put_queue = self.PutQueue() - """Queue/list of events waiting to get something out of the resource. - """ - self.get_queue = self.GetQueue() - """Queue/list of events waiting to put something into the resource.""" - - # Bind event constructors as methods - BoundClass.bind_early(self) - - put = BoundClass(Put) - """Create a new :class:`Put` event.""" - - get = BoundClass(Get) - """Create a new :class:`Get` event.""" - - def _do_put(self, event): - """Actually perform the *put* operation. - - This methods needs to be implemented by subclasses. It receives the - *put_event* that is created at each request and doesn't need to return - anything. - - """ - raise NotImplementedError(self) - - def _trigger_put(self, get_event): - """Trigger pending put events after a get event has been executed.""" - if get_event is not None: - self.get_queue.remove(get_event) - - for put_event in self.put_queue: - if not put_event.triggered: - self._do_put(put_event) - if not put_event.triggered: - break - - def _do_get(self, event): - """Actually perform the *get* operation. - - This methods needs to be implemented by subclasses. It receives the - *get_event* that is created at each request and doesn't need to return - anything. - - """ - raise NotImplementedError(self) - - def _trigger_get(self, put_event): - """Trigger pending get events after a put event has been executed.""" - if put_event is not None: - self.put_queue.remove(put_event) - - for get_event in self.get_queue: - if not get_event.triggered: - self._do_get(get_event) - if not get_event.triggered: - break diff --git a/lib/simpy/resources/base.pyc b/lib/simpy/resources/base.pyc deleted file mode 100644 index 7af78d7e11c1dd64b103962d906e9015f61642eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7162 zcmeHLU2hyo6|J5bPdt-klO+&#`Pvc-WNanl2#^*mw2*{ohRe>-q)Hv44OH4uvp~%|>ae2%K6llutL8m**u!&AnVvdA?}qxl zQXeVx>~KS+{igTIs<*GwO=Yggw}Vyhrb>tE$>=Jj{wh#v`1B;RdS0Z}%;==ZU7Y1s zyAz|Qu|>};w$@mERFwKjHeW37SX~-hRApkcU0P@6#^?$`~#x=d%1*+hT6q3GcGp6&liy&8u&U%IV@r$E`ws zC1z$)9a|meLvwC&2L)oMPtb#zrbaI_GfVNwbSbLU;wfuU7KyQTteXWLRBnQK($xyz zGwdi$YoS`ia#33eT3eVT`zL-Q7+AR>nukS|r;lh%-?xFj$5hS{0}zwopZqxFAuhXv z#sCv=HQEl50f!%Ui4WfN2n^nAD6^ps`zm}g>T%#dFa_<}I#Apf&t@9t)uJ#})cEZ= zST8ZzdF`j|#x>jCYDsBg2V2pHGO!*esPRi5!9JRk!xx#e+E&vVHCsA~b4{GV9#<6~3Os(dY4z$ul5loy#h5mCEmX8EEh)7=TXCfYh^F&AGJI<8zXk6o5P=#q0J zW(E{sZ8BK{rwq`@wRxGCh0BVZ7{YVoSxYdFYg_C=wX&=h@NJPKRcUvLwUx~tIg2UYlJ7~!M`U0?)}kg zlt+l$YGKNdy0}d=QIzGGiy}vyMkYU(-Uf0K3hd-?pa!6f>WGv9ymteA=D=8WJX1V$8XQ3&m%f`mHp z;-e_}Hg1RweDapu03Lu?x4}&=TU)aR4wcDk=a^JX9x_RaJ4-V!&W&U;S*Ab)Q5+HB zGLjGH@eJ-K7`Uw%I(@zN@bEP+3E#UAYq=2B83dy%RKmZ`-}Ib7<304{howg*A>=!^XasP+3#*u`!54K@tbklT?@HH^oQ0UJvGU<2Fv z9VO(!IAmrH87kp*HaF17HX#Y=Q`#a9AoO#+MMxTZMlCPCo|Q#T#xRkDyp40^c6~m6R#ZYvc1baJCp^1vfnr={oYc6L4en5X zEm_vj*^a20Xy(THVF3!miHnp>z*Qv}5NJqCME%S{dBHhFyZV&daMZ~}*urE|E4)XN z3pHyRtP32f!GFMMaCRW0#WTqB91JR(qs>5;z#~Relf>Xvw0^xS)x^D~9Y+-mJi^3V zoS8(#%oh#=vB<5hyBG)F>@}wk{b|~yGS>w`7qx6n#t}uKMFwW)y`LT4tM$FSs+u<4 zmFa1umE=WbPsl+F!O=Dp_Q`siYHzP{_x4ynXeu*huFGiR#Fr2Q@o)(oeV(PhdH~Oq z`oY1TR|4{mO%A`#Wc^1aj*?)&XY$sb6xA#h55e0w9+zd7rY1*m^YFSQmu={-!R(1c4F+GMyweL2FtVo8NJoVi~kP3$CU^Z38v3{P=>l z`@Sf~-G0AtdoAW3mt|45&XSsfXtGw1dB*1P{YN^0UQnCjvM%m;J;G%_Km#blQ)dHJ zUSPlIBxFH-U%pd=`sW|qRlfnkL5+LLZb|P3ANgV3`@TAO{=Ry?#lK&+{{Jb&JoQ-$ z?w52V+ksdDN$dzDUB|4D);W{4&Z$9&o0lf3P`?}R%bqMJ+4f{50ppKc-+_n`*X22P zb0!uELbI84*Kc__bpHuzSf0LB=UQ+JrIA5!9oHulF)-4n5HXPAt+n2W3BL^^wc!!I z%|{wenl 0.' % amount) - #: The amount to be put into the container. - self.amount = amount - """The amount to be put into the container.""" - - super(ContainerPut, self).__init__(container) - - -class ContainerGet(base.Get): - """An event that gets *amount* from the *container*. The event is triggered - as soon as there's enough content available in the *container*. - - Raise a :exc:`ValueError` if ``amount <= 0``. - - """ - def __init__(self, resource, amount): - if amount <= 0: - raise ValueError('amount(=%s) must be > 0.' % amount) - self.amount = amount - """The amount to be taken out of the container.""" - - super(ContainerGet, self).__init__(resource) - - -class Container(base.BaseResource): - """Models the production and consumption of a homogeneous, undifferentiated - bulk. It may either be continuous (like water) or discrete (like apples). - - The *env* parameter is the :class:`~simpy.core.Environment` instance the - container is bound to. - - The *capacity* defines the size of the container and must be a positive - number (> 0). By default, a container is of unlimited size. You can - specify the initial level of the container via *init*. It must be >= - 0 and is 0 by default. - - Raise a :exc:`ValueError` if ``capacity <= 0``, ``init < 0`` or - ``init > capacity``. - - """ - def __init__(self, env, capacity=float('inf'), init=0): - super(Container, self).__init__(env) - if capacity <= 0: - raise ValueError('"capacity" must be > 0.') - if init < 0: - raise ValueError('"init" must be >= 0.') - if init > capacity: - raise ValueError('"init" must be <= "capacity".') - - self._capacity = capacity - self._level = init - - @property - def capacity(self): - """The maximum capacity of the container.""" - return self._capacity - - @property - def level(self): - """The current level of the container (a number between ``0`` and - ``capacity``). - - """ - return self._level - - put = BoundClass(ContainerPut) - """Creates a new :class:`ContainerPut` event.""" - - get = BoundClass(ContainerGet) - """Creates a new :class:`ContainerGet` event.""" - - def _do_put(self, event): - if self._capacity - self._level >= event.amount: - self._level += event.amount - event.succeed() - - def _do_get(self, event): - if self._level >= event.amount: - self._level -= event.amount - event.succeed() diff --git a/lib/simpy/resources/container.pyc b/lib/simpy/resources/container.pyc deleted file mode 100644 index 2bdf54bc2a36ba5aefe7852c3c2fb608a21023ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4769 zcmds5ZEqVz5Z<#Lw{g>^FSN8D5UWa1Cu*IvfLfxofCY)iy znHk}%NX{~0kqL`4LPsPWvD-aI>|X^!be`-RE3+c0QZ3^mcgo~esx*}wajLA{=x_T) zUG`;a4zw(_Evhosw%6&*XuO3^rQ6qXTw=G_nIe}ePiRwHWn-QfL#gC`krgAI>!Px^ zJ5`>T;ZT=4cSbp#$U&7J^yF7gW@;j}!G@(Ac+pH=;TzdyF`nb?azz%UOpJ|7?R3Wz z)Oeg~y8`LH08zhC8Roaaccg5Qnp{ik6iZ`W0cB`icE{S4MYl&Gv~(&zustcC z;8&NnV{$N2mUej>H4~ zY(WPN0!~WbvQpZZkP7lG3bQ7j?VM$KK#J0$gHxC-0zWG?{ybI)Nvq9Fc7`)@4Yx zc4yH=k;#pVB4**}B&|+|ZV`i-fkRsEqWRA5VzgG-axFE3weiI57x}&3{WWWxz5|;c zz;!HU*?4m2quzS&<2&OC=si6e`PH>qfUeDW^v08rBqjAM2_}N2;7Vh$*(K&vQFE2) zC}IVp$Y%sh&)|h9N{Tp&c#FBDJbG*5k_|7RS(32bZU@Kx|4aQQ{{JiXI`01?eboPl zWs&_y{!g~2C{u^Zq-ucdI!y%spI8VfaM*oA^8Jt!l=l%Pkms;#)ed=@p1uO+b59SZ zo*q)6w75n z4Hrs|3nXlF==tPTHh`R6pvX#3ZcS)6Ri%!qlKBRZ0h?BNYBJ>dL=qbmAWw@5MMlm7 z+p&(#aKfAkS&T|$3TRGGigRd`T%~sa;8ac3K=zO~T4!;=aa|5(;!HLF8)&X=B0V_Y z23ivvxk*rsdW`pKqs47B`y)EQxC?5i2A+GUo&)_| zLUIHFTz(4j92tn0@@pnFkCE`w&0qUU686$Z;*COcA-YT$Y;EXfj?vF z192T8`wUUHAj$_sXkpDS-EW{2ry)WVj)DT6V~!kS$9p*)cSw42wZP@4t>Reggs(6@ z3MfYTM)LOE!56?u5#gq!z}8X(h~P$WIXG#EPe7U)0y+k4fxF%UAn?+I2E4@^=1 zqI2cLYnaZp4k;a-qQWSRhu}C>_%amkE|Yx6YbbV+VOl81iHs8y{sF)>$W1~DrSK|s zXu{u%NJrR`t3?I_@CoT+65c>J*N;bUEss&=y@zJW-`dOV;Of%y!t&Xr=2CNUVTb=8 z;S!OrL-T@y^>AbczgCyFHY68jb#ODED~fB1SCp#1E!^V6>Axo-32;iv@|kS<8{H!k S#8L&^4lV{4n>Q}qxcCS3dY5?s diff --git a/lib/simpy/resources/resource.py b/lib/simpy/resources/resource.py deleted file mode 100644 index 23c3566..0000000 --- a/lib/simpy/resources/resource.py +++ /dev/null @@ -1,220 +0,0 @@ -""" -This module contains all :class:`Resource` like resources. - -These resources can be used by a limited number of processes at a time (e.g., -a gas station with a limited number of fuel pumps). Processes *request* these -resources to become a user (or to own them) and have to *release* them once -they are done (e.g., vehicles arrive at the gas station, use a fuel-pump, if -one is available, and leave when they are done). - -Requesting a resources is modeled as "putting a process' token into the -resources" and releasing a resources correspondingly as "getting a process' -token out of the resource". Thus, calling ``request()``/``release()`` is -equivalent to calling ``put()``/``get()``. Note, that releasing a resource will -always succeed immediately, no matter if a process is actually using a resource -or not. - -Beside :class:`Resource`, there are a :class:`PriorityResource`, were processes -can define a request priority, and a :class:`PreemptiveResource` whose resource -users can be preempted by other processes with a higher priority. - -""" -from simpy.core import BoundClass -from simpy.resources import base - - -class Preempted(object): - def __init__(self, by, usage_since): - self.by = by - """The preempting :class:`simpy.events.Process`.""" - self.usage_since = usage_since - """The simulation time at which the preempted process started to use - the resource.""" - - -class Request(base.Put): - """Request access on the *resource*. The event is triggered once access is - granted. - - If the maximum capacity of users is not reached, the requesting process - obtains the resource immediately. If the maximum capacity is reached, the - requesting process waits until another process releases the resource. - - The request is automatically released when the request was created within - a :keyword:`with` statement. - - """ - def __exit__(self, exc_type, value, traceback): - super(Request, self).__exit__(exc_type, value, traceback) - self.resource.release(self) - - -class Release(base.Get): - """Releases the access privilege to *resource* granted by *request*. This - event is triggered immediately. - - If there's another process waiting for the *resource*, resume it. - - If the request was made in a :keyword:`with` statement (e.g., ``with - res.request() as req:``), this method is automatically called when the - ``with`` block is left. - - """ - def __init__(self, resource, request): - self.request = request - """The request (:class:`Request`) that is to be released.""" - super(Release, self).__init__(resource) - - -class PriorityRequest(Request): - """Request the *resource* with a given *priority*. If the *resource* - supports preemption and *preempted* is true other processes with access to - the *resource* may be preempted (see :class:`PreemptiveResource` for - details). - - This event type inherits :class:`Request` and adds some additional - attributes needed by :class:`PriorityResource` and - :class:`PreemptiveResource` - - """ - def __init__(self, resource, priority=0, preempt=True): - self.priority = priority - """The priority of this request. A smaller number means higher - priority.""" - - self.preempt = preempt - """Indicates whether the request should preempt a resource user or not - (this flag is not taken into account by :class:`PriorityResource`).""" - - self.time = resource._env.now - """The time at which the request was made.""" - - self.key = (self.priority, self.time, not self.preempt) - """Key for sorting events. Consists of the priority (lower value is - more important), the time at witch the request was made (earlier - requests are more important) and finally the preemption flag (preempt - requests are more important). """ - - super(PriorityRequest, self).__init__(resource) - - -class SortedQueue(list): - """Queue that sorts events by their :attr:`~PriorityRequest.key` - attribute.""" - def __init__(self, maxlen=None): - super(SortedQueue, self).__init__() - self.maxlen = maxlen - """Maximum length of the queue.""" - - def append(self, item): - """Append *item* to the queue and keep the queue sorted. - - Raise a :exc:`RuntimeError` if the queue is full. - - """ - if self.maxlen is not None and len(self) >= self.maxlen: - raise RuntimeError('Cannot append event. Queue is full.') - - super(SortedQueue, self).append(item) - super(SortedQueue, self).sort(key=lambda e: e.key) - - -class Resource(base.BaseResource): - """A resource has a limited number of slots that can be requested by - a process. - - If all slots are taken, requesters are put into a queue. If a process - releases a slot, the next process is popped from the queue and gets one - slot. - - The *env* parameter is the :class:`~simpy.core.Environment` instance the - resource is bound to. - - The *capacity* defines the number of slots and must be a positive integer. - - """ - - def __init__(self, env, capacity=1): - super(Resource, self).__init__(env) - self._capacity = capacity - self.users = [] - """List of :class:`Request` events for the processes that are currently - using the resource.""" - self.queue = self.put_queue - """Queue/list of pending :class:`Request` events that represent - processes waiting to use the resource.""" - - @property - def capacity(self): - """Maximum capacity of the resource.""" - return self._capacity - - @property - def count(self): - """Number of users currently using the resource.""" - return len(self.users) - - request = BoundClass(Request) - """Create a new :class:`Request` event.""" - - release = BoundClass(Release) - """Create a new :class:`Release` event.""" - - def _do_put(self, event): - if len(self.users) < self.capacity: - self.users.append(event) - event.succeed() - - def _do_get(self, event): - try: - self.users.remove(event.request) - except ValueError: - pass - event.succeed() - - -class PriorityResource(Resource): - """This class works like :class:`Resource`, but requests are sorted by - priority. - - The :attr:`~Resource.queue` is kept sorted by priority in ascending order - (a lower value for *priority* results in a higher priority), so more - important request will get the resource earlier. - - """ - PutQueue = SortedQueue - """The type to be used for the - :attr:`~simpy.resources.base.BaseResource.put_queue`.""" - GetQueue = list - """The type to be used for the - :attr:`~simpy.resources.base.BaseResource.get_queue`.""" - - def __init__(self, env, capacity=1): - super(PriorityResource, self).__init__(env, capacity) - - request = BoundClass(PriorityRequest) - """Create a new :class:`PriorityRequest` event.""" - - -class PreemptiveResource(PriorityResource): - """This resource mostly works like :class:`Resource`, but users of the - resource can be *preempted* by higher prioritized requests. - - Furthermore, the queue of requests is also sorted by *priority*. - - If a less important request is preempted, the process of that request will - receive an :class:`~simpy.events.Interrupt` with a :class:`Preempted` - instance as cause. - - """ - def _do_put(self, event): - if len(self.users) >= self.capacity and event.preempt: - # Check if we can preempt another process - preempt = sorted(self.users, key=lambda e: e.key)[-1] - - if preempt.key > event.key: - self.users.remove(preempt) - preempt.proc.interrupt(Preempted(by=event.proc, - usage_since=preempt.time)) - - return super(PreemptiveResource, self)._do_put(event) diff --git a/lib/simpy/resources/resource.pyc b/lib/simpy/resources/resource.pyc deleted file mode 100644 index 84640039576c724ba35cb87aa9756dde96b1a889..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10143 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 diff --git a/lib/simpy/resources/store.py b/lib/simpy/resources/store.py deleted file mode 100644 index c3d292e..0000000 --- a/lib/simpy/resources/store.py +++ /dev/null @@ -1,160 +0,0 @@ -""" -This module contains all :class:`Store` like resources. - -Stores model the production and consumption of concrete objects. The object -type is, by default, not restricted. A single Store can even contain multiple -types of objects. - -Beside :class:`Store`, there is a :class:`FilterStore` that lets you use -a custom function to filter the objects you get out of the store. - -""" -from simpy.core import BoundClass -from simpy.resources import base - - -class StorePut(base.Put): - """Put *item* into the store if possible or wait until it is.""" - def __init__(self, resource, item): - self.item = item - """The item to put into the store.""" - super(StorePut, self).__init__(resource) - - -class StoreGet(base.Get): - """Get an item from the store or wait until one is available.""" - pass - - -class FilterStoreGet(StoreGet): - """Get an item from the store for which *filter* returns ``True``. This - event is triggered once such an event is available. - - The default *filter* function returns ``True`` for all items, and thus this - event exactly behaves like :class:`StoreGet`. - - """ - def __init__(self, resource, filter=lambda item: True): - self.filter = filter - """The filter function to use.""" - super(FilterStoreGet, self).__init__(resource) - - -class FilterQueue(list): - """The queue inherits :class:`list` and modifies :meth:`__getitem__()` and - :meth:`__bool__` to appears to only contain events for which the - *store*\ 's item queue contains proper - item. - - """ - def __init__(self): - super(FilterQueue, self).__init__() - self.store = None - - def __getitem__(self, key): - """Get the *key*\ th event from all events that have an item available - in the corresponding store's item queue. - - """ - filtered_events = [evt for evt in self - if any(evt.filter(item) - for item in self.store.items)] - return filtered_events[key] - - def __bool__(self): - """Return ``True`` if the queue contains an event for which an item is - available in the corresponding store's item queue. - - """ - for evt in self: - for item in self.store.items: - if evt.filter(item): - return True - return False - - #: Provided for backwards compatability: :meth:`__bool__()` is only - #: used from Python 3 onwards. - __nonzero__ = __bool__ - - -class Store(base.BaseResource): - """Models the production and consumption of concrete Python objects. - - Items put into the store can be of any type. By default, they are put and - retrieved from the store in a first-in first-out order. - - The *env* parameter is the :class:`~simpy.core.Environment` instance the - container is bound to. - - The *capacity* defines the size of the Store and must be a positive number - (> 0). By default, a Store is of unlimited size. A :exc:`ValueError` is - raised if the value is negative. - - """ - def __init__(self, env, capacity=float('inf')): - super(Store, self).__init__(env) - if capacity <= 0: - raise ValueError('"capacity" must be > 0.') - self._capacity = capacity - self.items = [] - """List of the items within the store.""" - - @property - def capacity(self): - """The maximum capacity of the store.""" - return self._capacity - - put = BoundClass(StorePut) - """Create a new :class:`StorePut` event.""" - - get = BoundClass(StoreGet) - """Create a new :class:`StoreGet` event.""" - - def _do_put(self, event): - if len(self.items) < self._capacity: - self.items.append(event.item) - event.succeed() - - def _do_get(self, event): - if self.items: - event.succeed(self.items.pop(0)) - - -class FilterStore(Store): - """The *FilterStore* subclasses :class:`Store` and allows you to only get - items that match a user-defined criteria. - - This criteria is defined via a filter function that is passed to - :meth:`get()`. :meth:`get()` only considers items for which this function - returns ``True``. - - .. note:: - - In contrast to :class:`Store`, processes trying to get an item from - :class:`FilterStore` won't necessarily be processed in the same order - that they made the request. - - *Example:* The store is empty. *Process 1* tries to get an item of type - *a*, *Process 2* an item of type *b*. Another process puts one item of - type *b* into the store. Though *Process 2* made his request after - *Process 1*, it will receive that new item because *Process 1* doesn't - want it. - - """ - GetQueue = FilterQueue - """The type to be used for the - :attr:`~simpy.resources.base.BaseResource.get_queue`.""" - - def __init__(self, env, capacity=float('inf')): - super(FilterStore, self).__init__(env, capacity) - self.get_queue.store = self - - get = BoundClass(FilterStoreGet) - """Create a new :class:`FilterStoreGet` event.""" - - def _do_get(self, event): - for item in self.items: - if event.filter(item): - self.items.remove(item) - event.succeed(item) - break diff --git a/lib/simpy/resources/store.pyc b/lib/simpy/resources/store.pyc deleted file mode 100644 index e5aace9980b22ec8efc8aa838561c6cdbf90e784..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7873 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 diff --git a/lib/simpy/rt.py b/lib/simpy/rt.py deleted file mode 100644 index 142d473..0000000 --- a/lib/simpy/rt.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Provides an environment whose time passes according to the (scaled) -real-time (aka *wallclock time*).""" - -try: - # Python >= 3.3 - from time import monotonic as time, sleep -except ImportError: - # Python < 3.3 - from time import time, sleep - -from simpy.core import Environment, EmptySchedule, Infinity - - -class RealtimeEnvironment(Environment): - """An :class:`~simpy.core.Environment` which uses the real (e.g. wallclock) - time. - - A time step will take *factor* seconds of real time (one second by - default); e.g., if you step from ``0`` until ``3`` with ``factor=0.5``, the - :meth:`simpy.core.BaseEnvironment.run()` call will take at least 1.5 - seconds. - - If the processing of the events for a time step takes too long, - a :exc:`RuntimeError` is raised in :meth:`step()`. You can disable this - behavior by setting *strict* to ``False``. - - """ - def __init__(self, initial_time=0, factor=1.0, strict=True): - Environment.__init__(self, initial_time) - - self.env_start = initial_time - self.real_start = time() - self.factor = factor - """Scaling factor of the real-time.""" - self.strict = strict - """Running mode of the environment. :meth:`step()` will raise a - :exc:`RuntimeError` if this is set to ``True`` and the processing of - events takes too long.""" - - def step(self): - """Waits until enough real-time has passed for the next event to - happen. - - The delay is scaled according to the real-time :attr:`factor`. If the - events of a time step are processed too slowly for the given - :attr:`factor` and if :attr:`strict` is enabled, a :exc:`RuntimeError` - is raised. - - """ - evt_time = self.peek() - - if evt_time is Infinity: - raise EmptySchedule() - - real_time = self.real_start + (evt_time - self.env_start) * self.factor - - if self.strict and time() - real_time > self.factor: - # Events scheduled for time *t* may take just up to *t+1* - # for their computation, before an error is raised. - raise RuntimeError( - 'Simulation too slow for real time (%.3fs).' % ( - time() - real_time)) - - # Sleep in a loop to fix inaccuracies of windows (see - # http://stackoverflow.com/a/15967564 for details) and to ignore - # interrupts. - while True: - delta = real_time - time() - if delta <= 0: - break - sleep(delta) - - return Environment.step(self) diff --git a/lib/simpy/rt.pyc b/lib/simpy/rt.pyc deleted file mode 100644 index 0ef7f90a9ce8dec08bbd01e71a3644dd8718dbf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2546 zcmcIm-HsbI6h59wvb!N$6{u)cD&Y$dv(fHsOQ{eke=P!OFQ}?XrPADFyfaSX&CGZh zJG&dPMTqt}cmbY(w?g6p;2b;62Ek>CWbg6VKIi!J_Z@He=Wh4Y`J-V5t6v-ceud5a zj9m^e0$ARJoIxHy!JrIagnbC35ZKe|Lukns%)8JW0b`7og>D(!SXMFko@QE5w4v<4 zr~~B|jJBZMhS4^#ybZDg6I{`OmjEvSo{e@O--2PZO_@mS9yj__<CneTNQDT)KPG!k>c`)8skvO;Hbx4*v^ ztH1CU1sKlmU{~r&TV1Kl5-6sSvi6fLT7e-GhDH?9=nd??xy;M%(zvYca+u9zz9^)n zqa0NeRVll)Z{r}rbjZubw)HJggooJ79(H(s#GLLTSY3n+3Fnu13oU{bK^wIp8AdM8 z4j$k^#Rpk|fDF=~j4JD8jG)MPBUFmescgm$$XsiNRk%q8znL}qo<=SF6_$_>{O@6Lm*E4iBC$4loXmlLrl?B0D&@%Fiz@TFdG z=XTQQlBenIH06uRssio1XrC!NL(@n4{C0dVP4_8p=WkF-I~$}MCw(Z)Ca`g{sG_}; zBNK{^#D(RB6vpy9@jd5gb%51rk0uV8y3v_5hA4#xBdp{pa?kLIZn)S$gup;BwdRGc zru)vL-~)M{4bp@TgO}Yjx=Fb*yb;RCoGZMkRrUxf<{0y52t|fG&y^9!1+sgljPrFY zXW~?0(BmZnYAt2dH?~2B_KEFj`mHF8Ow(2V3dsXg>Hz-uG9yw^Bf@@-&D_V%0-P|o z0JvcAA^?Vea4-(QhNMKYTW}FLqYW3KGdje^VMO$Q6m4OjkWWYP@t7Ds9y_;&p9LA}KJSWc(@>tSQ#dYRBcZecSN6xh{Z zhxOP_uoGrzRjcO+83W5;z75U$U;_f~7Y3DIgOE8)Pg-z;_O#*J z_2&-IlMXcJV7DC5!K>MM%YEG@tzXW)^Mb8>p5LMJ)ci)b;^%KX#~4@=0+=&seuc*v z*>>p@Zel1v&m%g9w%Uk*Qr?@kOpHn9+Y5TE z7rYz17ltfk*V;R57ZuiGSAvLY!K!{#qLf%aNa*8nsp;{=A&GN5&UH2(Cs)ySYE51R z_#dhPWpo9bA)~X~VQ=*wMXvf>r|?wJ5^Ui932^WKQCaK8x(e^IzkH6|wfKv5S4=K1 f(RS8rDq;@9&pc{hP>!ycb%HRs$##RA!L9Ib>uz=^ diff --git a/lib/simpy/test/__init__.py b/lib/simpy/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/simpy/test/__init__.pyc b/lib/simpy/test/__init__.pyc deleted file mode 100644 index 1bd94196bad6146edb928f5a0ccd64f80a21d7df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmZSn%*$n&?H8QP00oRd+5w1*S%5?e14FO|NW@PANHCxg#lAo>{nFwh{hZ7s{esGp zjQl(!J#+oy%#u{yg2d$P#Prl+ATPI|QrB3|K+i zJzme+yE6_x&PKP(c#_g~_}Cw?oIr-@QAsr2#~w{RGDNRFWj?(Vt-%C4CfG3pc{m9N zC{lc4#QO#ws7EX(xtwrGm^cK`FL2M`bmv-K7IWS7{8HOXb)#LuCTeEsa+Vv1Q`Pj* zmAX*ZQPU%@OrC$UnvG+f)^;I*LWKYo%Q`>8=EN%be}#=eGaP!f{*fn_c?j6F<5NUW zc2%^st&HWv2sd^meYe6xQN!W{zF;qrf8=ApqIl!3H3>pW#PR>jJ3*Jbs?NI7+=~t; Ly2k!EI3Ioi9g~Fk diff --git a/lib/simpy/test/test_condition.py b/lib/simpy/test/test_condition.py deleted file mode 100644 index 2b3df98..0000000 --- a/lib/simpy/test/test_condition.py +++ /dev/null @@ -1,272 +0,0 @@ -import pytest - - -def test_operator_and(env): - def process(env): - timeout = [env.timeout(delay, value=delay) for delay in range(3)] - results = yield timeout[0] & timeout[1] & timeout[2] - - assert results == { - timeout[0]: 0, - timeout[1]: 1, - timeout[2]: 2, - } - - env.process(process(env)) - env.run() - - -def test_operator_or(env): - def process(env): - timeout = [env.timeout(delay, value=delay) for delay in range(3)] - results = yield timeout[0] | timeout[1] | timeout[2] - - assert results == { - timeout[0]: 0, - } - - env.process(process(env)) - env.run() - - -def test_operator_nested_and(env): - def process(env): - timeout = [env.timeout(delay, value=delay) for delay in range(3)] - results = yield (timeout[0] & timeout[2]) | timeout[1] - - assert results == { - timeout[0]: 0, - timeout[1]: 1, - } - assert env.now == 1 - - env.process(process(env)) - env.run() - - -def test_operator_nested_or(env): - def process(env): - timeout = [env.timeout(delay, value=delay) for delay in range(3)] - results = yield (timeout[0] | timeout[1]) & timeout[2] - - assert results == { - timeout[0]: 0, - timeout[1]: 1, - timeout[2]: 2, - } - assert env.now == 2 - - env.process(process(env)) - env.run() - - -def test_nested_cond_with_error(env): - def explode(env): - yield env.timeout(1) - raise ValueError('Onoes!') - - def process(env): - try: - yield env.process(explode(env)) & env.timeout(1) - pytest.fail('The condition should have raised a ValueError') - except ValueError as err: - assert err.args == ('Onoes!',) - - env.process(process(env)) - env.run() - - -def test_cond_with_error(env): - def explode(env, delay): - yield env.timeout(delay) - raise ValueError('Onoes, failed after %d!' % delay) - - def process(env): - try: - yield env.process(explode(env, 0)) | env.timeout(1) - pytest.fail('The condition should have raised a ValueError') - except ValueError as err: - assert err.args == ('Onoes, failed after 0!',) - - env.process(process(env)) - env.run() - - -def test_cond_with_nested_error(env): - def explode(env, delay): - yield env.timeout(delay) - raise ValueError('Onoes, failed after %d!' % delay) - - def process(env): - try: - yield (env.process(explode(env, 0)) & env.timeout(1) | - env.timeout(1)) - pytest.fail('The condition should have raised a ValueError') - except ValueError as err: - assert err.args == ('Onoes, failed after 0!',) - - env.process(process(env)) - env.run() - - -def test_cond_with_uncaught_error(env): - """Errors that happen after the condition has been triggered will not be - handled by the condition and cause the simulation to crash.""" - def explode(env, delay): - yield env.timeout(delay) - raise ValueError('Onoes, failed after %d!' % delay) - - def process(env): - yield env.timeout(1) | env.process(explode(env, 2)) - - env.process(process(env)) - try: - env.run() - assert False, 'There should have been an exception.' - except ValueError: - pass - assert env.now == 2 - - -def test_iand_with_and_cond(env): - def process(env): - cond = env.timeout(1, value=1) & env.timeout(2, value=2) - orig = cond - - cond &= env.timeout(0, value=0) - assert cond is orig - - results = yield cond - assert sorted(results.values()) == [0, 1, 2] - - env.process(process(env)) - env.run() - - -def test_iand_with_or_cond(env): - def process(env): - cond = env.timeout(1, value=1) | env.timeout(2, value=2) - orig = cond - - cond &= env.timeout(0, value=0) - assert cond is not orig - - results = yield cond - assert sorted(results.values()) == [0, 1] - - env.process(process(env)) - env.run() - - -def test_ior_with_or_cond(env): - def process(env): - cond = env.timeout(1, value=1) | env.timeout(2, value=2) - orig = cond - - cond |= env.timeout(0, value=0) - assert cond is orig - - results = yield cond - assert sorted(results.values()) == [0] - - env.process(process(env)) - env.run() - - -def test_ior_with_and_cond(env): - def process(env): - cond = env.timeout(1, value=1) & env.timeout(2, value=2) - orig = cond - - cond |= env.timeout(0, value=0) - assert cond is not orig - - results = yield cond - assert sorted(results.values()) == [0] - - env.process(process(env)) - env.run() - - -def test_immutable_results(env): - """Results of conditions should not change after they have been - triggered.""" - def process(env): - timeout = [env.timeout(delay, value=delay) for delay in range(3)] - # The or condition in this expression will trigger immediately. The and - # condition will trigger later on. - condition = timeout[0] | (timeout[1] & timeout[2]) - - results = yield condition - assert results == {timeout[0]: 0} - - # Make sure that the results of condition were frozen. The results of - # the nested and condition do not become visible afterwards. - yield env.timeout(2) - assert results == {timeout[0]: 0} - - env.process(process(env)) - env.run() - - -def test_shared_and_condition(env): - timeout = [env.timeout(delay, value=delay) for delay in range(3)] - c1 = timeout[0] & timeout[1] - c2 = c1 & timeout[2] - - def p1(env, condition): - results = yield condition - assert results == {timeout[0]: 0, timeout[1]: 1} - - def p2(env, condition): - results = yield condition - assert results == {timeout[0]: 0, timeout[1]: 1, timeout[2]: 2} - - env.process(p1(env, c1)) - env.process(p2(env, c2)) - env.run() - - -def test_shared_or_condition(env): - timeout = [env.timeout(delay, value=delay) for delay in range(3)] - c1 = timeout[0] | timeout[1] - c2 = c1 | timeout[2] - - def p1(env, condition): - results = yield condition - assert results == {timeout[0]: 0} - - def p2(env, condition): - results = yield condition - assert results == {timeout[0]: 0} - - env.process(p1(env, c1)) - env.process(p2(env, c2)) - env.run() - - -def test_result_order(env): - """The order of a conditions result is based on the order in which the - events have been specified.""" - timeouts = list(reversed([env.timeout(delay) for delay in range(3)])) - - def p(env, timeouts): - results = yield env.all_of(timeouts) - assert list(results.keys()) == timeouts - - env.process(p(env, timeouts)) - env.run() - - -def test_nested_result_order(env): - """The order of a conditions result is based on the order in which the - events have been specified (even if nested).""" - timeouts = list(reversed([env.timeout(delay) for delay in range(3)])) - condition = (timeouts[0] | timeouts[1]) & timeouts[2] - - def p(env, timeouts): - results = yield env.all_of(timeouts) - assert list(results.keys()) == timeouts - - env.process(p(env, timeouts)) - env.run() diff --git a/lib/simpy/test/test_condition.pyc b/lib/simpy/test/test_condition.pyc deleted file mode 100644 index d3964257f9b23e88d4f49f1350b7077972d3f451..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13066 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 cTXDANolzEz6MdI)8ius!F1I?XorTWEKPz?}', str(env.event())) - - assert re.match(r'', str(env.timeout(1))) - assert re.match(r'', - str(env.timeout(1, value=2))) - - assert re.match(r', ' - r'\]\) object at 0x.*>', - str(env.event() & env.event())) - - assert re.match(r'', - str(env.process(pem()))) - - -def test_value(env): - """After an event has been triggered, its value becomes accessible.""" - event = env.timeout(0, 'I am the value') - - env.run() - - assert event.value == 'I am the value' - - -def test_unavailable_value(env): - """If an event has not yet been triggered, its value is not availabe and - trying to access it will result in a AttributeError.""" - event = env.event() - - try: - event.value - assert False, 'Expected an exception' - except AttributeError as e: - assert e.args[0].endswith('is not yet available') - - -def test_triggered(env): - def pem(env, event): - value = yield event - env.exit(value) - - event = env.event() - event.succeed('i was already done') - - result = env.run(env.process(pem(env, event))) - - assert result == 'i was already done' diff --git a/lib/simpy/test/test_event.pyc b/lib/simpy/test/test_event.pyc deleted file mode 100644 index 88dd926287595cd0bc956b688be0ff8ad7e1e3d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4484 zcmc&&ZEqVz5Z*mIiEYw`rX?bX!Xg!LQR?&sgesM)p$e!%GU!tJk{X=L-NwG;d}rOR zlUQo`K&!U=Abt-Y_zC;~cxLuuJB{)M$za9nx!t+jxtZsgnN8`hGqsz0&5ckL9}mBG z@HLMxL_%z0lp^v(BE=>ICwpSk6H!ULfI>+`WfsaJnqgr^M6)c+ifE37IT2M@sL-P} z>Q&m|5{BA+ZLCo{S+2IWOx)`a8+u=-)-=}XXKSlbtHpog(+nll$+}YUH8&x^9@y}k z5U+&TVcT8`@l1-RLhK6hl!fO~;7`&NBq1aW->8>yx4DLaEMNm{SF81OKhCqXhrOL4 z^~16NY)MFcm_h9o@viwSTjcFwBLH3XSpTaG&|sX_(G@qmhND8v9uo~16{|I;K6(k zX?0-o)g<0t?GJ4?ORqP+Ts5)PSNcJ?7j(426?}f>M&oMZ^DF%!)*HIhakHxyp5dX* zn>6qY+A-|LNpuE+nZ+RFoLrQn=+N8TptUs&E(&mY5fLd~^CW!r+#3ajJ%$K9keJ^E zoZQmbMeG?L&l4OqpZ2hG82Wh@YGYWoaZhIh%d#1Sq1F*Q(2$~?JQOr}yiV!+W}O>+U`MmHNIkxDbGI@%%&IW#BLlr^PqFb_<$#0_=DV%9N-AA~49m z=0*YjLZ@`3!|Wg(r*_1VLT%=l8Y(?%{BtP_!*`jnh$BCA6#i})!~4X zI(3395Gh2ixu@s_Hte#)jLltY;8aV520$H|l6beHtLz~GXnYnZ{dQ40iYa-)N`2L%f>yhi;13KQy7(U#u9 z?Q1(0O=plQ?!8nuFlt-tRM|Z4bWn?;6&0g%=E!3u%wVGm(DxV!H}Ol-3v_T$d(X%!wC^QzXa8yKL3h*vqw(-lA9YLJB^4liW(G6^ly8)y%Pb}{uVIdhwVV)Fz=h|O}?`bph3x<0; z#~n{Szz{va=>dohHwwW%5_PbxaeGu#_}F|HryXUpA~cAZI*5}*<=PAqtKw7zYRw|3 z+XL&Gg@$rNHN zw({R5HG+apBXbbj?wf51#bMfKiX}3FTGBx}dY3k%;ybLkR;4uWm2l8lLbJlyAb^gr z@fa5f{)^E$gA0H=j8Ts`dDI{bl?XZz&?35H2+;5hlYI0bM*cWiYzT(Uka87CoJ#R) z-o=2&@R^Tm4n6It;UTxVs^g!B_=Z=RaxQ~maXOtIB}qEM8}byL(KvU->Hv8pNOB!S zLltGIX3#s#P`AOIj_b zJpNw-pZePEC=1(dpXlS$`P8Q_*QcEAQ?&eL8mRsC6^60#EKQ|^RItyjUKR}!eVYPd Wh;Xysyf^46`F{W&xxq;Q diff --git a/lib/simpy/test/test_exceptions.py b/lib/simpy/test/test_exceptions.py deleted file mode 100644 index cc7bf17..0000000 --- a/lib/simpy/test/test_exceptions.py +++ /dev/null @@ -1,161 +0,0 @@ -""" -Tests for forwarding exceptions from child to parent processes. - -""" -import pytest - -from simpy import _compat - - -def test_error_forwarding(env): - """Exceptions are forwarded from child to parent processes if there - are any. - - """ - def child(env): - raise ValueError('Onoes!') - yield env.timeout(1) - - def parent(env): - try: - yield env.process(child(env)) - pytest.fail('We should not have gotten here ...') - except ValueError as err: - assert err.args[0] == 'Onoes!' - - env.process(parent(env)) - env.run() - - -def test_no_parent_process(env): - """Exceptions should be normally raised if there are no processes waiting - for the one that raises something. - - """ - def child(env): - raise ValueError('Onoes!') - yield env.timeout(1) - - def parent(env): - try: - env.process(child(env)) - yield env.timeout(1) - except Exception as err: - pytest.fail('There should be no error (%s).' % err) - - env.process(parent(env)) - pytest.raises(ValueError, env.run) - - -def test_crashing_child_traceback(env): - def panic(env): - yield env.timeout(1) - raise RuntimeError('Oh noes, roflcopter incoming... BOOM!') - - def root(env): - try: - yield env.process(panic(env)) - pytest.fail("Hey, where's the roflcopter?") - except RuntimeError: - if not _compat.PY2: - import traceback - stacktrace = traceback.format_exc() - else: - import sys - stacktrace = ''.join(_compat.format_chain(*sys.exc_info())) - - # The current frame must be visible in the stacktrace. - assert 'yield env.process(panic(env))' in stacktrace - assert 'raise RuntimeError(\'Oh noes,' in stacktrace - - env.process(root(env)) - env.run() - - -@pytest.mark.skipif('sys.version_info[0] < 3') -def test_exception_chaining(env): - """Unhandled exceptions pass through the entire event stack. This must be - visible in the stacktrace of the exception. - - """ - def child(env): - yield env.timeout(1) - raise RuntimeError('foo') - - def parent(env): - child_proc = env.process(child(env)) - yield child_proc - - def grandparent(env): - parent_proc = env.process(parent(env)) - yield parent_proc - - env.process(grandparent(env)) - try: - env.run() - pytest.fail('There should have been an exception') - except RuntimeError: - import traceback - trace = traceback.format_exc() - assert 'raise RuntimeError(\'foo\')' in trace - assert 'yield child_proc' in trace - assert 'yield parent_proc' in trace - - -def test_invalid_event(env): - """Invalid yield values will cause the simulation to fail.""" - - def root(env): - yield None - - env.process(root(env)) - try: - env.run() - pytest.fail('Hey, this is not allowed!') - except RuntimeError as err: - assert err.args[0].endswith('Invalid yield value "None"') - - -def test_exception_handling(env): - """If failed events are not defused (which is the default) the simulation - crashes.""" - - event = env.event() - event.fail(RuntimeError()) - try: - env.run(until=1) - assert False, 'There must be a RuntimeError!' - except RuntimeError: - pass - - -def test_callback_exception_handling(env): - """Callbacks of events may handle exception by setting the ``defused`` - attribute of ``event`` to ``True``.""" - def callback(event): - event.defused = True - - event = env.event() - event.callbacks.append(callback) - event.fail(RuntimeError()) - assert not hasattr(event, 'defused'), 'Event has been defused immediately' - env.run(until=1) - assert event.defused, 'Event has not been defused' - - -def test_process_exception_handling(env): - """Processes can't ignore failed events and auto-handle execeptions.""" - def pem(env, event): - try: - yield event - assert False, 'Hey, the event should fail!' - except RuntimeError: - pass - - event = env.event() - env.process(pem(env, event)) - event.fail(RuntimeError()) - - assert not hasattr(event, 'defused'), 'Event has been defuseed immediately' - env.run(until=1) - assert event.defused, 'Event has not been defused' diff --git a/lib/simpy/test/test_exceptions.pyc b/lib/simpy/test/test_exceptions.pyc deleted file mode 100644 index e9cc37c7bf04cd111c8c927f5e5654d0c00fc7fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7710 zcmcgxYj51f6&>#CwQI?iA93M2bw_p`uL67J+696DL0czq(IDvBpxBMRkDJmN<^ii-z(WGN-BhrgTmz|CDr=mA@>V6)yRxwL(eWM$>#8 zn9S&{B;~d5rG6N1>)^wFFv`Lt#*;J|>i!^%e4QnFm{T0*C(nrdH>Ktfo2X3nN#ok9S_WZL-to+=F?h zG-1(!=*8u^Lv|c~nu;b|zA|GCh`0iq^H|K#Hk4C$E;whMGfu|}Mm30F={T-Lbx&c~ z8VUC&!p_c8UZD&))GiJuNxKyrP-3%$M$I?o&Mcm;J26Aw~aQ* zFvtd&H5(2Pi*yMwYiK+st4eeTk=fu~Y~&hOpk*Hq^@n(HEYx$#wMc&osQ(C1uL;!O zvrvc1EYyMdi==}R;@n?HC{VcbvA}s+cUsI!x2~J@c8m7zN+W#Pz-_5=mw<8UNv4oM z8$2n*3hZuR!4j+IopXZKN^Bt|He!p$YJ2&x%qNQ8i&$IG8z)`sq`NsD=*qqYzTe$5 zmyS?8%GX?|b?b#e0rv0`FdO&=zkJgg&2>omU;vB<=B7@Qt*D=jvLMxAjBtV|ZMXH? z4<7t#%^HQ64bR&h$8@chgk zq9O^jK=4BkUhBiYOfnNAVZlxk#@Hv~1N*!`@Gw9(hMCtVVr*I89LIg#F^bvJ9jX_5|4wpVfDOYc$&~pdoM^0A~Vs#uW$cGzoqXAe-yYO zgw4Ou92--7;i%6X$sX$J)Blt`OfE}sVG{^Au^r$8k%mODUEpj^+}b~gGh|rEt)h8C z3J}>@Aa01KMZ$=P`2zz`lGai&@|R+yQH=aE9}x^KAU_aG0s*Qg@xY7y2oVb`uSly# zo*|ynWV}5PTp_Ir5djB#%&!bBu&o~tLZgRclj%*Hw(Nx_+>8RSqP!#1= zH%2x8H7yIGwvwbGU4i7uhcxfgA`WpZJpk=QIF>%nZtC-?)P;FvnXVBLHcJ(fsbUKu z5o@~StrLwho->reH2%_HjM~7I|actm3KlIhFPdJ%$~e-aBdE`WA^9jyM<+ze~P7>JipB zCT@Y=T!3cjmBuHbw#16FJ4+I#&B+Sp0Lj9Sfo;lp{7PK@*Zoqw$}G4y3Kge&DZ#oFiI0<~a0KN`JM}`etk8 zQya&`zS$46fwfxd*~T%H$d!Z%oms~$42#4$@hNYMJBKc6^;?Dl5;HYpfkhx^BA^~$OUY( z&xJ*ybIZ;um}b?v2&NHqQbu0yaL#Yh9ATIPNmj@fQb>eLre=9xXSRaJyvO!2#Qd

*~C)xxV%SEE>UN5(ZUe6Yavn&la#~BJpTe{biFZFt)r(W-IIu3fhilV5oM-+h7 zb>u}5yPIdamZmsCXo_!2O*3E2Qr1c7nTOQR&j)q*g1X$B>|aH5UxLtqXApP7GZz>M zhr_@Ry)1|(F6$%K?&*W77sz(gFLhevYnNM-+^tyGNEz1~je^*(fFNgrbv>NApg=Ml z5lrgsmf&s`U*uP3O>i1Z*fb|Uv8uq{Ezt1^=9F|te5^XJ;_6fDLglav;*X=?rxeg!K(pX1*H-I|3;1{G@|DKv#!BP#SCi<_ AR{#J2 diff --git a/lib/simpy/test/test_interrupts.py b/lib/simpy/test/test_interrupts.py deleted file mode 100644 index f6c075e..0000000 --- a/lib/simpy/test/test_interrupts.py +++ /dev/null @@ -1,204 +0,0 @@ -""" -Test asynchronous interrupts. - -""" -import re - -import pytest - -import simpy - - -def test_interruption(env): - """With asynchronous interrupts, the victim expects an interrupt - while waiting for an event, but will process this even if no - interrupt occurred. - - """ - def interruptee(env): - try: - yield env.timeout(10) - pytest.fail('Expected an interrupt') - except simpy.Interrupt as interrupt: - assert interrupt.cause == 'interrupt!' - - def interruptor(env): - child_process = env.process(interruptee(env)) - yield env.timeout(5) - child_process.interrupt('interrupt!') - - env.process(interruptor(env)) - env.run() - - -def test_concurrent_interrupts(env, log): - """Concurrent interrupts are scheduled in the order in which they - occurred. - - """ - def fox(env, log): - while True: - try: - yield env.timeout(10) - except simpy.Interrupt as interrupt: - log.append((env.now, interrupt.cause)) - - def farmer(env, name, fox): - fox.interrupt(name) - yield env.timeout(1) - - fantastic_mr_fox = env.process(fox(env, log)) - for name in ('boggis', 'bunce', 'beans'): - env.process(farmer(env, name, fantastic_mr_fox)) - - env.run(20) - assert log == [(0, 'boggis'), (0, 'bunce'), (0, 'beans')] - - -def test_init_interrupt(env): - """An interrupt should always be executed after an INIT event at the - same time.""" - def child(env): - try: - yield env.timeout(10) - pytest.fail('Should have been interrupted.') - except simpy.Interrupt: - assert env.now == 0 - - def root(env): - child_proc = env.process(child(env)) - child_proc.interrupt() - - yield env.timeout(1) - - env.process(root(env)) - env.run() - - -def test_interrupt_terminated_process(env): - """A process that has no event scheduled cannot be interrupted.""" - def child(env): - yield env.timeout(1) - - def parent(env): - child_proc = env.process(child(env)) - - yield env.timeout(2) - ei = pytest.raises(RuntimeError, child_proc.interrupt) - assert re.match(r' has terminated ' - r'and cannot be interrupted.', ei.value.args[0]) - - yield env.timeout(1) - - env.process(parent(env)) - env.run() - - -def test_multiple_interrupts(env): - """Interrupts on dead processes are discarded. If there are multiple - concurrent interrupts on a process and the latter dies after - handling the first interrupt, the remaining ones are silently - ignored. - - """ - def child(env): - try: - yield env.timeout(1) - except simpy.Interrupt as i: - env.exit(i.cause) - - def parent(env): - c = env.process(child(env)) - yield env.timeout(0) - c.interrupt(1) - c.interrupt(2) - result = yield c - assert result == 1 - - env.process(parent(env)) - env.run() - - -def test_interrupt_self(env): - """A processs should not be able to interrupt itself.""" - def pem(env): - pytest.raises(RuntimeError, env.active_process.interrupt) - yield env.timeout(0) - - env.process(pem(env)) - env.run() - - -def test_immediate_interrupt(env, log): - """Test should be interruptable immediatly after a suspend.""" - def child(env, log): - try: - yield env.event() - except simpy.Interrupt: - log.append(env.now) - - def resumer(env, other): - other.interrupt() - yield env.exit() - - c = env.process(child(env, log)) - env.process(resumer(env, c)) - env.run() - - # Confirm that child has been interrupted immediately at timestep 0. - assert log == [0] - - -def test_interrupt_suspend(env): - """A process should be interruptable during a suspend.""" - def child(env): - try: - yield env.event() - except simpy.Interrupt: - assert env.now == 5 - - def parent(env): - child_proc = env.process(child(env)) - yield env.timeout(5) - child_proc.interrupt() - - env.process(parent(env)) - env.run() - - -def test_interrupt_event(env): - """A process should be interruptable while waiting for an Event.""" - def child(env): - try: - yield env.event() - except simpy.Interrupt: - assert env.now == 5 - - def parent(env): - child_proc = env.process(child(env)) - yield env.timeout(5) - child_proc.interrupt() - - env.process(parent(env)) - env.run() - - -def test_concurrent_behaviour(env): - def proc_a(env): - timeouts = [env.timeout(0) for i in range(2)] - while timeouts: - try: - yield timeouts.pop(0) - assert False, 'Expected an interrupt' - except simpy.Interrupt: - pass - - def proc_b(env, proc_a): - for i in range(2): - proc_a.interrupt() - yield env.exit() - - proc_a = env.process(proc_a(env)) - env.process(proc_b(env, proc_a)) - - env.run() diff --git a/lib/simpy/test/test_interrupts.pyc b/lib/simpy/test/test_interrupts.pyc deleted file mode 100644 index ef2dbf8c68fe5d4d869392f0164a5c2f7a99aa0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9663 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', 'exec') - eval(code, globs, locs) - child = locs['child'] - - def parent(env): - result1 = yield env.process(child(env)) - result2 = yield env.process(child(env)) - - assert [result1, result2] == [1, 2] - - env.process(parent(env)) - env.run() - - -def test_child_exception(env): - """A child catches an exception and sends it to its parent.""" - def child(env): - try: - yield env.timeout(1) - raise RuntimeError('Onoes!') - except RuntimeError as err: - env.exit(err) - - def parent(env): - result = yield env.process(child(env)) - assert isinstance(result, Exception) - - env.process(parent(env)) - env.run() - - -def test_interrupted_join(env): - """Tests that interrupts are raised while the victim is waiting for - another process. The victim should get unregistered from the other - process. - - """ - def interruptor(env, process): - yield env.timeout(1) - process.interrupt() - - def child(env): - yield env.timeout(2) - - def parent(env): - child_proc = env.process(child(env)) - try: - yield child_proc - pytest.fail('Did not receive an interrupt.') - except Interrupt: - assert env.now == 1 - assert child_proc.is_alive - - # We should not get resumed when child terminates. - yield env.timeout(5) - assert env.now == 6 - - parent_proc = env.process(parent(env)) - env.process(interruptor(env, parent_proc)) - env.run() - - -def test_interrupted_join_and_rejoin(env): - """Tests that interrupts are raised while the victim is waiting for - another process. The victim tries to join again. - - """ - def interruptor(env, process): - yield env.timeout(1) - process.interrupt() - - def child(env): - yield env.timeout(2) - - def parent(env): - child_proc = env.process(child(env)) - try: - yield child_proc - pytest.fail('Did not receive an interrupt.') - except Interrupt: - assert env.now == 1 - assert child_proc.is_alive - - yield child_proc - assert env.now == 2 - - parent_proc = env.process(parent(env)) - env.process(interruptor(env, parent_proc)) - env.run() - - -def test_unregister_after_interrupt(env): - """If a process is interrupted while waiting for another one, it - should be unregistered from that process. - - """ - def interruptor(env, process): - yield env.timeout(1) - process.interrupt() - - def child(env): - yield env.timeout(2) - - def parent(env): - child_proc = env.process(child(env)) - try: - yield child_proc - pytest.fail('Did not receive an interrupt.') - except Interrupt: - assert env.now == 1 - assert child_proc.is_alive - - yield env.timeout(2) - assert env.now == 3 - assert not child_proc.is_alive - - parent_proc = env.process(parent(env)) - env.process(interruptor(env, parent_proc)) - env.run() - - -def test_error_and_interrupted_join(env): - def child_a(env, process): - process.interrupt() - env.exit() - yield # Dummy yield - - def child_b(env): - raise AttributeError('spam') - yield # Dummy yield - - def parent(env): - env.process(child_a(env, env.active_process)) - b = env.process(child_b(env)) - - try: - yield b - # This interrupt unregisters me from b so I won't receive its - # AttributeError - except Interrupt: - pass - - yield env.timeout(0) - - env.process(parent(env)) - pytest.raises(AttributeError, env.run) diff --git a/lib/simpy/test/test_process.pyc b/lib/simpy/test/test_process.pyc deleted file mode 100644 index 491f6a44ef94a808fda30c4ed7b6c28b06262bd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10487 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 BwA26q diff --git a/lib/simpy/test/test_resources.py b/lib/simpy/test/test_resources.py deleted file mode 100644 index 25a1444..0000000 --- a/lib/simpy/test/test_resources.py +++ /dev/null @@ -1,454 +0,0 @@ -""" -Theses test cases demonstrate the API for shared resources. - -""" -# Pytest gets the parameters "env" and "log" from the *conftest.py* file -import pytest - -import simpy - - -# -# Tests for Resource -# - - -def test_resource(env, log): - """A *resource* is something with a limited numer of slots that need - to be requested before and released after the usage (e.g., gas pumps - at a gas station). - - """ - def pem(env, name, resource, log): - req = resource.request() - yield req - assert resource.count == 1 - - yield env.timeout(1) - resource.release(req) - - log.append((name, env.now)) - - resource = simpy.Resource(env, capacity=1) - assert resource.capacity == 1 - assert resource.count == 0 - env.process(pem(env, 'a', resource, log)) - env.process(pem(env, 'b', resource, log)) - env.run() - - assert log == [('a', 1), ('b', 2)] - - -def test_resource_context_manager(env, log): - """The event that ``Resource.request()`` returns can be used as - Context Manager.""" - def pem(env, name, resource, log): - with resource.request() as request: - yield request - yield env.timeout(1) - - log.append((name, env.now)) - - resource = simpy.Resource(env, capacity=1) - env.process(pem(env, 'a', resource, log)) - env.process(pem(env, 'b', resource, log)) - env.run() - - assert log == [('a', 1), ('b', 2)] - - -def test_resource_slots(env, log): - def pem(env, name, resource, log): - with resource.request() as req: - yield req - log.append((name, env.now)) - yield env.timeout(1) - - resource = simpy.Resource(env, capacity=3) - for i in range(9): - env.process(pem(env, str(i), resource, log)) - env.run() - - assert log == [('0', 0), ('1', 0), ('2', 0), ('3', 1), ('4', 1), ('5', 1), - ('6', 2), ('7', 2), ('8', 2)] - - -def test_resource_continue_after_interrupt(env): - """A process may be interrupted while waiting for a resource but - should be able to continue waiting afterwards.""" - def pem(env, res): - with res.request() as req: - yield req - yield env.timeout(1) - - def victim(env, res): - try: - evt = res.request() - yield evt - pytest.fail('Should not have gotten the resource.') - except simpy.Interrupt: - yield evt - res.release(evt) - assert env.now == 1 - - def interruptor(env, proc): - proc.interrupt() - yield env.exit(0) - - res = simpy.Resource(env, 1) - env.process(pem(env, res)) - proc = env.process(victim(env, res)) - env.process(interruptor(env, proc)) - env.run() - - -def test_resource_release_after_interrupt(env): - """A process needs to release a resource, even it it was interrupted - and does not continue to wait for it.""" - def blocker(env, res): - with res.request() as req: - yield req - yield env.timeout(1) - - def victim(env, res): - try: - evt = res.request() - yield evt - pytest.fail('Should not have gotten the resource.') - except simpy.Interrupt: - # Dont wait for the resource - res.release(evt) - assert env.now == 0 - env.exit() - - def interruptor(env, proc): - proc.interrupt() - yield env.exit(0) - - res = simpy.Resource(env, 1) - env.process(blocker(env, res)) - victim_proc = env.process(victim(env, res)) - env.process(interruptor(env, victim_proc)) - env.run() - - -def test_resource_immediate_requests(env): - """A process must not acquire a resource if it releases it and immediately - requests it again while there are already other requesting processes.""" - def child(env, res): - result = [] - for i in range(3): - with res.request() as req: - yield req - result.append(env.now) - yield env.timeout(1) - env.exit(result) - - def parent(env): - res = simpy.Resource(env, 1) - child_a = env.process(child(env, res)) - child_b = env.process(child(env, res)) - - a_acquire_times = yield child_a - b_acquire_times = yield child_b - - assert a_acquire_times == [0, 2, 4] - assert b_acquire_times == [1, 3, 5] - - env.process(parent(env)) - env.run() - - -def test_resource_cm_exception(env, log): - """Resource with context manager receives an exception.""" - def process(env, resource, log, raise_): - try: - with resource.request() as req: - yield req - yield env.timeout(1) - log.append(env.now) - if raise_: - raise ValueError('Foo') - except ValueError as err: - assert err.args == ('Foo',) - - resource = simpy.Resource(env, 1) - env.process(process(env, resource, log, True)) - # The second process is used to check if it was able to access the - # resource: - env.process(process(env, resource, log, False)) - env.run() - - assert log == [1, 2] - - -def test_resource_with_condition(env): - def process(env, resource): - with resource.request() as res_event: - result = yield res_event | env.timeout(1) - assert res_event in result - - resource = simpy.Resource(env, 1) - env.process(process(env, resource)) - env.run() - - -def test_resource_with_priority_queue(env): - def process(env, delay, resource, priority, res_time): - yield env.timeout(delay) - req = resource.request(priority=priority) - yield req - assert env.now == res_time - yield env.timeout(5) - resource.release(req) - - resource = simpy.PriorityResource(env, capacity=1) - env.process(process(env, 0, resource, 2, 0)) - env.process(process(env, 2, resource, 3, 10)) - env.process(process(env, 2, resource, 3, 15)) # Test equal priority - env.process(process(env, 4, resource, 1, 5)) - env.run() - - -def test_sorted_queue_maxlen(env): - """Requests must fail if more than *maxlen* requests happen - concurrently.""" - resource = simpy.PriorityResource(env, capacity=10) - resource.put_queue.maxlen = 1 - - def process(env, resource): - resource.request(priority=1) - try: - resource.request(priority=1) - pytest.fail('Expected a RuntimeError') - except RuntimeError as e: - assert e.args[0] == 'Cannot append event. Queue is full.' - yield env.timeout(0) - - env.process(process(env, resource)) - env.run() - - -def test_get_users(env): - def process(env, resource): - with resource.request() as req: - yield req - yield env.timeout(1) - - resource = simpy.Resource(env, 1) - procs = [env.process(process(env, resource)) for i in range(3)] - env.run(until=1) - assert [evt.proc for evt in resource.users] == procs[0:1] - assert [evt.proc for evt in resource.queue] == procs[1:] - - env.run(until=2) - assert [evt.proc for evt in resource.users] == procs[1:2] - assert [evt.proc for evt in resource.queue] == procs[2:] - - -# -# Tests for PreemptiveResource -# - - -def test_preemptive_resource(env, log): - def process(id, env, res, delay, prio, log): - yield env.timeout(delay) - with res.request(priority=prio) as req: - try: - yield req - yield env.timeout(5) - log.append((env.now, id)) - except simpy.Interrupt as ir: - log.append((env.now, id, (ir.cause.by, ir.cause.usage_since))) - - res = simpy.PreemptiveResource(env, capacity=2) - env.process(process(0, env, res, 0, 1, log)) - env.process(process(1, env, res, 0, 1, log)) - p2 = env.process(process(2, env, res, 1, 0, log)) - env.process(process(3, env, res, 2, 2, log)) - - env.run() - - assert log == [(1, 1, (p2, 0)), (5, 0), (6, 2), (10, 3)] - - -def test_preemptive_resource_timeout_0(env): - def proc_a(env, resource, prio): - with resource.request(priority=prio) as req: - try: - yield req - yield env.timeout(1) - pytest.fail('Should have received an interrupt/preemption.') - except simpy.Interrupt: - pass - yield env.event() - - def proc_b(env, resource, prio): - with resource.request(priority=prio) as req: - yield req - - resource = simpy.PreemptiveResource(env, 1) - env.process(proc_a(env, resource, 1)) - env.process(proc_b(env, resource, 0)) - - env.run() - - -def test_mixed_preemption(env, log): - def process(id, env, res, delay, prio, preempt, log): - yield env.timeout(delay) - with res.request(priority=prio, preempt=preempt) as req: - try: - yield req - yield env.timeout(5) - log.append((env.now, id)) - except simpy.Interrupt as ir: - log.append((env.now, id, (ir.cause.by, ir.cause.usage_since))) - - res = simpy.PreemptiveResource(env, 2) - env.process(process(0, env, res, 0, 1, True, log)) - env.process(process(1, env, res, 0, 1, True, log)) - env.process(process(2, env, res, 1, 0, False, log)) - p3 = env.process(process(3, env, res, 1, 0, True, log)) - env.process(process(4, env, res, 2, 2, True, log)) - - env.run() - - assert log == [(1, 1, (p3, 0)), (5, 0), (6, 3), (10, 2), (11, 4)] - -# -# Tests for Container -# - - -def test_container(env, log): - """A *container* is a resource (of optinally limited capacity) where - you can put in our take out a discrete or continuous amount of - things (e.g., a box of lump sugar or a can of milk). The *put* and - *get* operations block if the buffer is to full or to empty. If they - return, the process nows that the *put* or *get* operation was - successfull. - - """ - def putter(env, buf, log): - yield env.timeout(1) - while True: - yield buf.put(2) - log.append(('p', env.now)) - yield env.timeout(1) - - def getter(env, buf, log): - yield buf.get(1) - log.append(('g', env.now)) - - yield env.timeout(1) - yield buf.get(1) - log.append(('g', env.now)) - - buf = simpy.Container(env, init=0, capacity=2) - env.process(putter(env, buf, log)) - env.process(getter(env, buf, log)) - env.run(until=5) - - assert log == [('p', 1), ('g', 1), ('g', 2), ('p', 2)] - - -def test_container_get_queued(env): - def proc(env, wait, container, what): - yield env.timeout(wait) - with getattr(container, what)(1) as req: - yield req - - container = simpy.Container(env, 1) - p0 = env.process(proc(env, 0, container, 'get')) - env.process(proc(env, 1, container, 'put')) - env.process(proc(env, 1, container, 'put')) - p3 = env.process(proc(env, 1, container, 'put')) - - env.run(until=1) - assert [ev.proc for ev in container.put_queue] == [] - assert [ev.proc for ev in container.get_queue] == [p0] - - env.run(until=2) - assert [ev.proc for ev in container.put_queue] == [p3] - assert [ev.proc for ev in container.get_queue] == [] - - -def test_initial_container_capacity(env): - container = simpy.Container(env) - assert container.capacity == float('inf') - - -@pytest.mark.parametrize(('error', 'args'), [ - (None, [2, 1]), # normal case - (None, [1, 1]), # init == capacity should be valid - (None, [1, 0]), # init == 0 should be valid - (ValueError, [1, 2]), # init > capcity - (ValueError, [0]), # capacity == 0 - (ValueError, [-1]), # capacity < 0 - (ValueError, [1, -1]), # init < 0 -]) -def test_container_init_capacity(env, error, args): - args.insert(0, env) - if error: - pytest.raises(error, simpy.Container, *args) - else: - simpy.Container(*args) - - -# -# Tests fore Store -# - - -def test_store(env): - """A store models the production and consumption of concrete python - objects (in contrast to containers, where you only now if the *put* - or *get* operations were successfull but don't get concrete - objects). - - """ - def putter(env, store, item): - yield store.put(item) - - def getter(env, store, orig_item): - item = yield store.get() - assert item is orig_item - - store = simpy.Store(env, capacity=2) - item = object() - - # NOTE: Does the start order matter? Need to test this. - env.process(putter(env, store, item)) - env.process(getter(env, store, item)) - env.run() - - -@pytest.mark.parametrize('Store', [ - simpy.Store, - simpy.FilterStore, -]) -def test_initial_store_capacity(env, Store): - store = Store(env) - assert store.capacity == float('inf') - - -def test_store_capacity(env): - simpy.Store(env, 1) - pytest.raises(ValueError, simpy.Store, env, 0) - pytest.raises(ValueError, simpy.Store, env, -1) - - -def test_filter_store(env): - def pem(env): - store = simpy.FilterStore(env, capacity=2) - - get_event = store.get(lambda item: item == 'b') - yield store.put('a') - assert not get_event.triggered - yield store.put('b') - assert get_event.triggered - - env.process(pem(env)) - env.run() diff --git a/lib/simpy/test/test_resources.pyc b/lib/simpy/test/test_resources.pyc deleted file mode 100644 index cc85e5c9889bb862a6fc11792b857b1d5d52f5af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19817 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 3.3 - from time import monotonic -except ImportError: - # Python < 3.3 - from time import time as monotonic - -import pytest - -from simpy.rt import RealtimeEnvironment - - -def process(env, log, sleep, timeout=1): - """Test process.""" - while True: - time.sleep(sleep) - yield env.timeout(timeout) - log.append(env.now) - - -def check_duration(real, expected): - return expected <= real < (expected + 0.02) - - -@pytest.mark.parametrize('factor', [0.1, 0.05, 0.15]) -def test_rt(log, factor): - """Basic tests for run().""" - env = RealtimeEnvironment(factor=factor) - env.process(process(env, log, 0.01, 1)) - env.process(process(env, log, 0.02, 1)) - - start = monotonic() - env.run(2) - duration = monotonic() - start - - assert check_duration(duration, 2 * factor) - assert log == [1, 1] - - -def test_rt_multiple_call(log): - """Test multiple calls to run().""" - env = RealtimeEnvironment(factor=0.05) - start = monotonic() - - env.process(process(env, log, 0.01, 2)) - env.process(process(env, log, 0.01, 3)) - - env.run(5) - duration = monotonic() - start - - # assert almost_equal(duration, 0.2) - assert check_duration(duration, 5 * 0.05) - assert log == [2, 3, 4] - - env.run(12) - duration = monotonic() - start - - assert check_duration(duration, 12 * 0.05) - assert log == [2, 3, 4, 6, 6, 8, 9, 10] - - -def test_rt_slow_sim_default_behavior(log): - """By default, SimPy should raise an error if a simulation is too - slow for the selected real-time factor.""" - env = RealtimeEnvironment(factor=0.05) - env.process(process(env, log, 0.1, 1)) - - err = pytest.raises(RuntimeError, env.run, 3) - assert 'Simulation too slow for real time' in err.value.args[0] - assert log == [] - - -def test_rt_slow_sim_no_error(log): - """Test ignoring slow simulations.""" - env = RealtimeEnvironment(factor=0.05, strict=False) - env.process(process(env, log, 0.1, 1)) - - start = monotonic() - env.run(2) - duration = monotonic() - start - - assert check_duration(duration, 2 * 0.1) - assert log == [1] - - -def test_rt_illegal_until(): - """Test illegal value for *until*.""" - env = RealtimeEnvironment() - err = pytest.raises(ValueError, env.run, -1) - assert err.value.args[0] == ('until(=-1.0) should be > the current ' - 'simulation time.') diff --git a/lib/simpy/test/test_rt.pyc b/lib/simpy/test/test_rt.pyc deleted file mode 100644 index 018db82868fb410896ffa47b9b1727be9eb57369..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3721 zcmc&%&vO(-6n-=NBiWEmKm#U%I-FQ2vq6EPtO8b`(4xvpS+f*a@vt?_bS9IXomr-P z1HqtGgg?QHc<}7SyJt^$@?Wt06F7L(@4cSgjZof9ImN#8boadOe&6@L*Hm6M=RWGM zcS9%gTAmz;aqX{xFU_2;O#O^)O6{HjdA zCzy~q^hN2TN*h&iP@}X)gF0>0$%*O|&CoMUo}p-#o?+2hQdQb+(Docf4T@loP&7|* zh4x*FnmlJ8^U=8=^XDmAltz=HqtaNQ=$JGXdGp=Y8+@h~%-kKNjrKP4!s{l3;qDpj z6)H$qO)^m4eYF+rBze)En@j$}v)*#>iD|$L@+>!bmV~ye%KiAnzP*VzA6~fUR<@HA zc{Wg)N%-rKr}EF`7yR@Um`9h$5EYlnIJE83p34Vv_K3DCw1;Clbl2I#P?fl^CZi83 zbPs{-w%C(y!thzVVUdSQ>-Ku9jxNJX-6=-Om#s6NI#p_DW_S?$%SXoW#9%m7S!60` zX8FUG9Ee|3wj-Ts9{b$svkQ)#HTpBO)<(KmOOyL+!(FqLXCJmNuj$07)nO3!gIH<2 zA+)O(+UMIBSBJY8Z>u=Ay=w-W7Sk(C8xDA5)7C|$6qb|^y%-9zwOwKvj+(bLv45g^=PO(9N}N=QXl=_m;FfQZi^AsCtz*_Ite`n+-6)c%35dJJ_n{m=vhaS zWP(@1(XmYSx#`cb!HyQ&s%?j579$otU670{R$+ z!vtN@3dHPzo7KvbZH7(-|o)aT9)u%rkT%s6KS-> zVt4*AHmp>KNu%TN&v^qH^~NFTaY$A%pqF9DU33~Q)2it-E61Ia{MS8h$;APgNX7^+ zuaJv5WhLWvPJwAan>m6>HIao1^AJp8p{Ua~>huh)^y{R1gv7FPfY>r;9+W*`OSi?5 z*V`~RcD+b#2Eg`xF1w%adU`7#rIA+ziB?{ad5Uw`OE$g0L-81;qL*I649Mp^JUY!E zO4&AB%F`+p`94s)t(KPJGU3TsT191qC;Bw@gFTFwfd8p)t`8np$pn+2Lwta#4vA)? zVgVOv%I39Uy7?oNdlY{@h0pvE>DdX=k&?m_6tT8!VrZsp5>><550guL;4zmtiai9l zSHjU7UwLj~lIA24v2vFoSQD9=FzG3KlHy~FjHrqWv3gabB>hVr!V&i;LWY4-kfe7k zNGLE?V8Eb+5ZtoF)F^g@R$rH$AYMtU_pldHj3mzTBFSQl;*>o46)p&5+7wA>CKcDx zLTXOg_o5@^OF$P4KM7K;re#RL{h!5Qg<&YgVFGBD_aukbF{rsb62xF!b07=S12efv zUB?T+WNgr|g#>p2TsHv{$_0|%0$byx2jD9xJ)GiK33oOC1d;Db)zwoh(p1GrAtAnG z>p4!0^jur+(~?#C5*C);*45Py+UL)Xwcx(;u6dl@;ixEZeLg4zoLso!iU7;KfCbX$ zJBDG>-sMD_CZEp&>2sKD&dC?@E#JTnGuIC4c4C9kle2z_g*7J0&LNPP0{!R_I0yD(ElvX>!-bPyDM!(R@AA{Z#p{IT-o|0PT4 z^iQ(k3yJT+Od}wz|Nc(TyS*~Vqfx4^@$Tf>UU%l5io1j=wdBsbM=SHQjp~`j#)-y} GM)Pkb`YJjA diff --git a/lib/simpy/test/test_timeout.py b/lib/simpy/test/test_timeout.py deleted file mode 100644 index 8adebce..0000000 --- a/lib/simpy/test/test_timeout.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Tests for ``simpy.events.Timeout``. - -""" -# Pytest gets the parameters "env" and "log" from the *conftest.py* file -import pytest - - -def test_discrete_time_steps(env, log): - """envple envulation with discrete time steps.""" - def pem(env, log): - while True: - log.append(env.now) - yield env.timeout(delay=1) - - env.process(pem(env, log)) - env.run(until=3) - - assert log == [0, 1, 2] - - -def test_negative_timeout(env): - """Don't allow negative timeout times.""" - def pem(env): - yield env.timeout(-1) - - env.process(pem(env)) - pytest.raises(ValueError, env.run) - - -def test_timeout_value(env): - """You can pass an additional *value* to *timeout* which will be - directly yielded back into the PEM. This is useful to implement some - kinds of resources or other additions. - - See :class:`envpy.resources.Store` for an example. - - """ - def pem(env): - val = yield env.timeout(1, 'ohai') - assert val == 'ohai' - - env.process(pem(env)) - env.run() - - -def test_shared_timeout(env, log): - def child(env, timeout, id, log): - yield timeout - log.append((id, env.now)) - - timeout = env.timeout(1) - for i in range(3): - env.process(child(env, timeout, i, log)) - - env.run() - assert log == [(0, 1), (1, 1), (2, 1)] - - -def test_triggered_timeout(env): - def process(env): - def child(env, event): - value = yield event - env.exit(value) - - event = env.timeout(1, 'i was already done') - # Start the child after the timeout has already happened. - yield env.timeout(2) - value = yield env.process(child(env, event)) - assert value == 'i was already done' - - env.run(env.process(process(env))) diff --git a/lib/simpy/test/test_timeout.pyc b/lib/simpy/test/test_timeout.pyc deleted file mode 100644 index 17be4a89c257561565a3f3b225928ebbbba59192..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3610 zcmc&$U2hvj6rEi=A9kA(wW8unVgzdIrixRwNQkObptdhaSg?swsDzx&?!+Fl*>!iu zNh~EVg#XI}KLF0TW5+2-yt!E3+1dF#bMLw5PUFwD_E(3!?MzL7G2Y+canDiYO1(xI zsXSIiq+X-aP`{%wR(Vq@4V5oRrK$2|sVu2{MJmgjwcT0eVs}xrcZ~B+A5^v8+jI74 zH0hdSQ+n6ku}7vF`@OwxyKVo%ua^-<15XnVk+>cT0Kw+xu{u-gSEbIRau%tJSY1Ra zVyi#}$O{!k=*a;KfR@#up$=o^zEeI{zoC6)hU@{>HRbWBFdDCMk$PK|`o#L7&aKO8 zBqme0d&(URd;#W8zSgS)6(gED^E~-HXZAuFFBWhb?S8o+| ze`_@H!>WAL{c_7$Z#GA1c9;%~!x!AJ`MCSA`^DyHg8r@<3_|Ochr6WcPj~EM6#^NV zqic9M;!{y8T94!$y0a)4!roX8SnPYx^CM|G7M=VX~06KW}A)P{vMqRWi#a}e1BMueyI^s$iQTPlB!Ol{xQ|nM~qiZ)U#zx$q z@KdNAIPgx~|31;t{WIIo7=Ifbf#z|maVruZyyf`~4EQh42T8hsA?6PM19N_^#yU$& zJxZO^c&B-883JjcH;!qH4ecwvF%7&8eKNG!5UNxZdf&7)ez~np=8K7*SX1OC*ZYWR zZA(n^L!*Cq`F&UK46W1nkDWOf7hDnHUYH|@u6ET?Sm@A}xzp8wu8ph4HNsOvp(`v> zU&3+_W^TG|jDC_80R3c-qB-eaPU>#^sy2Hf?>LY-O}W){-dlzf^4^CLF3AhqASQOt z5i_EcRO&klQA4G^6iX2lQO9bS+Q1Q#JE0iODI?kf8@>~!-^139AXIbmeP8Iz1GQ7>e!8HZ%&Rn1FQfJNH1G&D2pCg$ zqqD|5%Ok5Egp`_-5LC9Qxy9z3EZH-t2G>KPM-s{0YD^hD*Ugcwv6ZE0(^up zKmB_V5SXNm=a)s?1hEuIb!B;w7(>_$s)KEqLh0F=T@uMOT6TUNrDtSMrZ|%H>SYuQ z1)N0K6OM{pnN#cM*Bar9TzsV_F}i3kC1k}jVyDqmv=XT4s2EnCq{u-6BKjUQd`hPeib`C aJ*x6?VV+4O5zcaaz0q20wOZ?~wZ8#a5D?D* diff --git a/lib/simpy/test/test_util.py b/lib/simpy/test/test_util.py deleted file mode 100644 index 6da0367..0000000 --- a/lib/simpy/test/test_util.py +++ /dev/null @@ -1,310 +0,0 @@ -""" -Tests for the utility functions from :mod:`simpy.util`. - -""" -import re - -import pytest - -from simpy import Interrupt -from simpy.util import start_delayed, subscribe_at - - -def test_start_delayed(env): - def pem(env): - assert env.now == 5 - yield env.timeout(1) - - start_delayed(env, pem(env), delay=5) - env.run() - - -def test_start_delayed_error(env): - """Check if delayed() raises an error if you pass a negative dt.""" - def pem(env): - yield env.timeout(1) - - pytest.raises(ValueError, start_delayed, env, pem(env), delay=-1) - - -def test_subscribe(env): - """Check async. interrupt if a process terminates.""" - def child(env): - yield env.timeout(3) - env.exit('ohai') - - def parent(env): - child_proc = env.process(child(env)) - subscribe_at(child_proc) - - try: - yield env.event() - except Interrupt as interrupt: - assert interrupt.cause[0] is child_proc - assert interrupt.cause[1] == 'ohai' - assert env.now == 3 - - env.process(parent(env)) - env.run() - - -def test_subscribe_terminated_proc(env): - """subscribe() proc should send a singal immediatly if - "other" has already terminated. - - """ - def child(env): - yield env.timeout(1) - - def parent(env): - child_proc = env.process(child(env)) - yield env.timeout(2) - pytest.raises(RuntimeError, subscribe_at, child_proc) - - env.process(parent(env)) - env.run() - - -def test_subscribe_with_join(env): - """Test that subscribe() works if a process waits for another one.""" - def child(env, i): - yield env.timeout(i) - - def parent(env): - child_proc1 = env.process(child(env, 1)) - child_proc2 = env.process(child(env, 2)) - try: - subscribe_at(child_proc1) - yield child_proc2 - except Interrupt as interrupt: - assert env.now == 1 - assert interrupt.cause[0] is child_proc1 - assert child_proc2.is_alive - - env.process(parent(env)) - env.run() - - -def test_subscribe_at_timeout(env): - """You should be able to subscribe at arbitrary events.""" - def pem(env): - to = env.timeout(2) - subscribe_at(to) - try: - yield env.timeout(10) - except Interrupt as interrupt: - assert interrupt.cause == (to, None) - assert env.now == 2 - - env.process(pem(env)) - env.run() - - -def test_subscribe_at_timeout_with_value(env): - """An event's value should be accessible via the interrupt cause.""" - def pem(env): - val = 'ohai' - to = env.timeout(2, value=val) - subscribe_at(to) - try: - yield env.timeout(10) - except Interrupt as interrupt: - assert interrupt.cause == (to, val) - assert env.now == 2 - - env.process(pem(env)) - env.run() - - -def test_all_of(env): - """Wait for all events to be triggered.""" - def parent(env): - # Start 10 events. - events = [env.timeout(i, value=i) for i in range(10)] - results = yield env.all_of(events) - - assert results == {events[i]: i for i in range(10)} - assert env.now == 9 - - env.process(parent(env)) - env.run() - - -def test_wait_for_all_with_errors(env): - """On default AllOf should fail immediately if one of its events - fails.""" - def child_with_error(env, value): - yield env.timeout(value) - raise RuntimeError('crashing') - - def parent(env): - events = [env.timeout(1, value=1), - env.process(child_with_error(env, 2)), - env.timeout(3, value=3)] - - try: - condition = env.all_of(events) - yield condition - assert False, 'There should have been an exception' - except RuntimeError as e: - assert e.args[0] == 'crashing' - - # Although the condition has failed, interim values are available. - assert condition._events[0].value == 1 - assert condition._events[1].value.args[0] == 'crashing' - # The last child has not terminated yet. - assert not events[2].processed - - env.process(parent(env)) - env.run() - - -def test_all_of_chaining(env): - """If a wait_for_all condition A is chained to a wait_for_all condition B, - B will be merged into A.""" - def parent(env): - condition_A = env.all_of([env.timeout(i, value=i) for i in range(2)]) - condition_B = env.all_of([env.timeout(i, value=i) for i in range(2)]) - - condition_A &= condition_B - - results = yield condition_A - assert list(results.values()) == [0, 1, 0, 1] - - env.process(parent(env)) - env.run() - - -def test_all_of_chaining_intermediate_results(env): - """If a wait_for_all condition A with intermediate results is merged into - another wait_for_all condition B, the results are copied into condition - A.""" - def parent(env): - condition_A = env.all_of([env.timeout(i, value=i) for i in range(2)]) - condition_B = env.all_of([env.timeout(i, value=i) for i in range(2)]) - - yield env.timeout(0) - - condition = condition_A & condition_B - assert sorted(condition._get_values().values()) == [0, 0] - - results = yield condition - assert sorted(results.values()) == [0, 0, 1, 1] - - env.process(parent(env)) - env.run() - - -def test_all_of_with_triggered_events(env): - """Processed events can be added to a condition. Confirm this with - all_of.""" - def parent(env): - events = [env.timeout(0, value='spam'), env.timeout(1, value='eggs')] - yield env.timeout(2) - - values = list((yield env.all_of(events)).values()) - assert values == ['spam', 'eggs'] - - env.process(parent(env)) - env.run() - - -def test_any_of(env): - """Wait for any event to be triggered.""" - def parent(env): - # Start 10 events. - events = [env.timeout(i, value=i) for i in range(10)] - results = yield env.any_of(events) - - assert results == {events[0]: 0} - assert env.now == 0 - - env.process(parent(env)) - env.run() - - -def test_any_of_with_errors(env): - """On default any_of should fail if the event has failed too.""" - def child_with_error(env, value): - yield env.timeout(value) - raise RuntimeError('crashing') - - def parent(env): - events = [env.process(child_with_error(env, 1)), - env.timeout(2, value=2)] - - try: - condition = env.any_of(events) - yield condition - assert False, 'There should have been an exception' - except RuntimeError as e: - assert e.args[0] == 'crashing' - - assert condition._events[0].value.args[0] == 'crashing' - # The last event has not terminated yet. - assert not events[1].processed - - env.process(parent(env)) - env.run() - - -def test_any_of_chaining(env): - """If a any_of condition A is chained to a any_of condition B, - B will be merged into A.""" - def parent(env): - condition_A = env.any_of([env.timeout(2, value='a')]) - condition_B = env.any_of([env.timeout(1, value='b')]) - - condition_A |= condition_B - - results = yield condition_A - assert sorted(results.values()) == ['b'] - - env.process(parent(env)) - env.run() - - -def test_any_of_with_triggered_events(env): - """Processed events can be added to a condition. Confirm this with - all_of.""" - def parent(env): - events = [env.timeout(0, value='spam'), env.timeout(1, value='eggs')] - yield env.timeout(2) - - values = list((yield env.any_of(events)).values()) - assert values == ['spam', 'eggs'] - - env.process(parent(env)) - env.run() - - -def test_empty_any_of(env): - """AnyOf will triggered immediately if there are no events.""" - def parent(env): - results = yield env.any_of([]) - assert results == {} - - env.process(parent(env)) - env.run() - - -def test_empty_all_of(env): - """AllOf will triggered immediately if there are no events.""" - def parent(env): - results = yield env.all_of([]) - assert results == {} - - env.process(parent(env)) - env.run() - - -def test_all_of_expansion(env): - """The result of AllOf is an OrderedDict, which allows to expand its values - directly into variables.""" - def p(env): - timeouts = [env.timeout(d, d) for d in [3, 2, 1]] - a, b, c = (yield env.all_of(timeouts)).values() - assert a == 3 and b == 2 and c == 1 - - env.process(p(env)) - env.run() diff --git a/lib/simpy/test/test_util.pyc b/lib/simpy/test/test_util.pyc deleted file mode 100644 index d886764b4c5b7984e5c2028b36f44a02f060026e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14746 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 IakWzYFUvy-QUCw| diff --git a/lib/simpy/util.py b/lib/simpy/util.py deleted file mode 100644 index 6d7e771..0000000 --- a/lib/simpy/util.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -This modules contains various utility functions: - -- :func:`start_delayed()`: Start a process with a given delay. -- :func:`subscribe_at()`: Receive an interrupt if an event occurs. - -""" - - -def start_delayed(env, generator, delay): - """Return a helper process that starts another process for *generator* - after a certain *delay*. - - :meth:`~simpy.core.Environment.process()` starts a process at the current - simulation time. This helper allows you to start a process after a delay of - *delay* simulation time units:: - - >>> from simpy import Environment - >>> from simpy.util import start_delayed - >>> def my_process(env, x): - ... print('%s, %s' % (env.now, x)) - ... yield env.timeout(1) - ... - >>> env = Environment() - >>> proc = start_delayed(env, my_process(env, 3), 5) - >>> env.run() - 5, 3 - - Raise a :exc:`ValueError` if ``delay <= 0``. - - """ - if delay <= 0: - raise ValueError('delay(=%s) must be > 0.' % delay) - - def starter(): - yield env.timeout(delay) - proc = env.process(generator) - env.exit(proc) - - return env.process(starter()) - - -def subscribe_at(event): - """Register at the *event* to receive an interrupt when it occurs. - - The most common use case for this is to pass - a :class:`~simpy.events.Process` to get notified when it terminates. - - Raise a :exc:`RuntimeError` if ``event`` has already occurred. - - """ - env = event.env - subscriber = env.active_process - - def signaller(signaller, receiver): - result = yield signaller - if receiver.is_alive: - receiver.interrupt((signaller, result)) - - if event.callbacks is not None: - env.process(signaller(event, subscriber)) - else: - raise RuntimeError('%s has already terminated.' % event) diff --git a/lib/simpy/util.pyc b/lib/simpy/util.pyc deleted file mode 100644 index e4122c6e1d4497076bdbc0aa243067861115c092..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2569 zcmcImU2hvj6rHt`wzm2up%#IVxCn(hCfNi+Rbe#|Qt4}iHdT;#u_wD@d&qjnnwd>v zDdB-4nqR~VKZ`%WxwC7>rtlUl@7T9{flt_4ww4`A`{{# z$VmK#he%|N@X!!hQyg{HSnCgn_M5SGG%;D3D@Um*ywruGGikLc9hF|^+RtcM7OB^! zaJ_bWn|eI#4V;(O_cN8tnaVnwgB~668iS>tv*qG z>BH(or3#uP3(|#G)|OLGddOo11D;Hpme$4XcG`sN90T9^a^FK-hj33LVLuff&kcU_ z^lc=5jYJ{DJNzT@t|5j^PQvd(Ts6gYBz}&>WmCKr0-AMZ{%i=5C|}xwB#l)*Rdx}R zA4^XmbO+CjA1}=fjiv5L70OC)Y`0DL$st04nN(TMEpU|Po}dt zHCDy1i!*IaF+s#}ok*l-vE{-MxPi?HDX~xu)?lT~B^Qdko~W2Yb?X3Ro||(=GgFc` zl{vUIFV)=5;2oJ^uvhy^|FBDCp}p&&7dQm|9UL6c(3%NrPG8F(inHth$)}LoX6=z#?vDsVb#Bm(lm|8SW=h=qaq7Cy#3aDp6_jL(eu^C?1L?fC6mv$Xs^m| zBDDi3sHZLfx?`D_>b14T4j3DQK~=Dq`?Nb4)J>%U2H`%02$P-t4Yx^?(s?>jbU?dt z$_mUD=yS1$%f%3kg@7Ue;W(<0aou28Tvd||aT!n&Wdcay^@p7%q`%9eN?~}e3*{N2 z>O%XV8%8`+ty#xhaL=;|B*3?DUx25BK?Uob(%GF{pX^L$er$@b;}<(ld$m24>8Ttk zhY@;idoSLNzuBJ7FdwVYsCv7@{S{*&dl*Wi?C1P=hs{S1qeoGPbKu($H>piHwm^~z zyP2?d!oehLC%6`ZsNOA90t?-WyAQC@?jD4AdO!N0@nk(^7uhAgUg2^-LZCh< z;T0Gy@Y)sVJ{$mGI-Uybf7%lERZv)C;=}t(6gkv6Djo~J&diCoHFMsa7i-uuQ6udF zp;heD4b0qSMzQ}!-{)g6zh0tl#_1c-*2JJ4Q!|->t4j2DDj}FZJs(K;!{Ssr7chwC zPjd_wL>YE<@ekEGGhpSB@&r28L#;BJZwpT+x{zM+sli*Xf+;BrZrf!z1fEs^t8Jv*G2FC#Ilj6O$k;LR&50MneohvE;{}OIic^kq}ugmj)N}% zU~%$QC}B)EJt{!nI6qjsPP6sglvcSi_fjD=XnnDGndERWf76E!_MDPM1`~idkqHg}p zV1tAIL*T)e#Wwgf`4V0@c2KlhQLE8fe-wSxSmM2YKQn2+pKzs;ClEJIg{Rvig?ljm Rviejz2z. -## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. - - -""" -Basic statistics module. - -This module provides functions for calculating statistics of data, including -averages, variance, and standard deviation. - -Calculating averages --------------------- - -================== ============================================= -Function Description -================== ============================================= -mean Arithmetic mean (average) of data. -median Median (middle value) of data. -median_low Low median of data. -median_high High median of data. -median_grouped Median, or 50th percentile, of grouped data. -mode Mode (most common value) of data. -================== ============================================= - -Calculate the arithmetic mean ("the average") of data: - ->>> mean([-1.0, 2.5, 3.25, 5.75]) -2.625 - - -Calculate the standard median of discrete data: - ->>> median([2, 3, 4, 5]) -3.5 - - -Calculate the median, or 50th percentile, of data grouped into class intervals -centred on the data values provided. E.g. if your data points are rounded to -the nearest whole number: - ->>> median_grouped([2, 2, 3, 3, 3, 4]) #doctest: +ELLIPSIS -2.8333333333... - -This should be interpreted in this way: you have two data points in the class -interval 1.5-2.5, three data points in the class interval 2.5-3.5, and one in -the class interval 3.5-4.5. The median of these data points is 2.8333... - - -Calculating variability or spread ---------------------------------- - -================== ============================================= -Function Description -================== ============================================= -pvariance Population variance of data. -variance Sample variance of data. -pstdev Population standard deviation of data. -stdev Sample standard deviation of data. -================== ============================================= - -Calculate the standard deviation of sample data: - ->>> stdev([2.5, 3.25, 5.5, 11.25, 11.75]) #doctest: +ELLIPSIS -4.38961843444... - -If you have previously calculated the mean, you can pass it as the optional -second argument to the four "spread" functions to avoid recalculating it: - ->>> data = [1, 2, 2, 4, 4, 4, 5, 6] ->>> mu = mean(data) ->>> pvariance(data, mu) -2.5 - - -Exceptions ----------- - -A single exception is defined: StatisticsError is a subclass of ValueError. - -""" - -__all__ = [ 'StatisticsError', - 'pstdev', 'pvariance', 'stdev', 'variance', - 'median', 'median_low', 'median_high', 'median_grouped', - 'mean', 'mode', - ] - - -import collections -import math - -from fractions import Fraction -from decimal import Decimal - - -# === Exceptions === - -class StatisticsError(ValueError): - pass - - -# === Private utilities === - -def _sum(data, start=0): - """_sum(data [, start]) -> value - - Return a high-precision sum of the given numeric data. If optional - argument ``start`` is given, it is added to the total. If ``data`` is - empty, ``start`` (defaulting to 0) is returned. - - - Examples - -------- - - >>> _sum([3, 2.25, 4.5, -0.5, 1.0], 0.75) - 11.0 - - Some sources of round-off error will be avoided: - - >>> _sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero. - 1000.0 - - Fractions and Decimals are also supported: - - >>> from fractions import Fraction as F - >>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)]) - Fraction(63, 20) - - >>> from decimal import Decimal as D - >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")] - >>> _sum(data) - Decimal('0.6963') - - Mixed types are currently treated as an error, except that int is - allowed. - """ - # We fail as soon as we reach a value that is not an int or the type of - # the first value which is not an int. E.g. _sum([int, int, float, int]) - # is okay, but sum([int, int, float, Fraction]) is not. - allowed_types = set([int, type(start)]) - n, d = _exact_ratio(start) - partials = {d: n} # map {denominator: sum of numerators} - # Micro-optimizations. - exact_ratio = _exact_ratio - partials_get = partials.get - # Add numerators for each denominator. - for x in data: - _check_type(type(x), allowed_types) - n, d = exact_ratio(x) - partials[d] = partials_get(d, 0) + n - # Find the expected result type. If allowed_types has only one item, it - # will be int; if it has two, use the one which isn't int. - assert len(allowed_types) in (1, 2) - if len(allowed_types) == 1: - assert allowed_types.pop() is int - T = int - else: - T = (allowed_types - set([int])).pop() - if None in partials: - assert issubclass(T, (float, Decimal)) - assert not math.isfinite(partials[None]) - return T(partials[None]) - total = Fraction() - for d, n in sorted(partials.items()): - total += Fraction(n, d) - if issubclass(T, int): - assert total.denominator == 1 - return T(total.numerator) - if issubclass(T, Decimal): - return T(total.numerator)/total.denominator - return T(total) - - -def _check_type(T, allowed): - if T not in allowed: - if len(allowed) == 1: - allowed.add(T) - else: - types = ', '.join([t.__name__ for t in allowed] + [T.__name__]) - raise TypeError("unsupported mixed types: %s" % types) - - -def _exact_ratio(x): - """Convert Real number x exactly to (numerator, denominator) pair. - - >>> _exact_ratio(0.25) - (1, 4) - - x is expected to be an int, Fraction, Decimal or float. - """ - try: - try: - # int, Fraction - return (x.numerator, x.denominator) - except AttributeError: - # float - try: - return x.as_integer_ratio() - except AttributeError: - # Decimal - try: - return _decimal_to_ratio(x) - except AttributeError: - msg = "can't convert type '{}' to numerator/denominator" - raise TypeError(msg.format(type(x).__name__)) from None - except (OverflowError, ValueError): - # INF or NAN - if __debug__: - # Decimal signalling NANs cannot be converted to float :-( - if isinstance(x, Decimal): - assert not x.is_finite() - else: - assert not math.isfinite(x) - return (x, None) - - -# FIXME This is faster than Fraction.from_decimal, but still too slow. -def _decimal_to_ratio(d): - """Convert Decimal d to exact integer ratio (numerator, denominator). - - >>> from decimal import Decimal - >>> _decimal_to_ratio(Decimal("2.6")) - (26, 10) - - """ - sign, digits, exp = d.as_tuple() - if exp in ('F', 'n', 'N'): # INF, NAN, sNAN - assert not d.is_finite() - raise ValueError - num = 0 - for digit in digits: - num = num*10 + digit - if exp < 0: - den = 10**-exp - else: - num *= 10**exp - den = 1 - if sign: - num = -num - return (num, den) - - -def _counts(data): - # Generate a table of sorted (value, frequency) pairs. - table = collections.Counter(iter(data)).most_common() - if not table: - return table - # Extract the values with the highest frequency. - maxfreq = table[0][1] - for i in range(1, len(table)): - if table[i][1] != maxfreq: - table = table[:i] - break - return table - - -# === Measures of central tendency (averages) === - -def mean(data): - """Return the sample arithmetic mean of data. - - >>> mean([1, 2, 3, 4, 4]) - 2.8 - - >>> from fractions import Fraction as F - >>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)]) - Fraction(13, 21) - - >>> from decimal import Decimal as D - >>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")]) - Decimal('0.5625') - - If ``data`` is empty, StatisticsError will be raised. - """ - if iter(data) is data: - data = list(data) - n = len(data) - if n < 1: - raise StatisticsError('mean requires at least one data point') - return _sum(data)/n - - -# FIXME: investigate ways to calculate medians without sorting? Quickselect? -def median(data): - """Return the median (middle value) of numeric data. - - When the number of data points is odd, return the middle data point. - When the number of data points is even, the median is interpolated by - taking the average of the two middle values: - - >>> median([1, 3, 5]) - 3 - >>> median([1, 3, 5, 7]) - 4.0 - - """ - data = sorted(data) - n = len(data) - if n == 0: - raise StatisticsError("no median for empty data") - if n%2 == 1: - return data[n//2] - else: - i = n//2 - return (data[i - 1] + data[i])/2 - - -def median_low(data): - """Return the low median of numeric data. - - When the number of data points is odd, the middle value is returned. - When it is even, the smaller of the two middle values is returned. - - >>> median_low([1, 3, 5]) - 3 - >>> median_low([1, 3, 5, 7]) - 3 - - """ - data = sorted(data) - n = len(data) - if n == 0: - raise StatisticsError("no median for empty data") - if n%2 == 1: - return data[n//2] - else: - return data[n//2 - 1] - - -def median_high(data): - """Return the high median of data. - - When the number of data points is odd, the middle value is returned. - When it is even, the larger of the two middle values is returned. - - >>> median_high([1, 3, 5]) - 3 - >>> median_high([1, 3, 5, 7]) - 5 - - """ - data = sorted(data) - n = len(data) - if n == 0: - raise StatisticsError("no median for empty data") - return data[n//2] - - -def median_grouped(data, interval=1): - """"Return the 50th percentile (median) of grouped continuous data. - - >>> median_grouped([1, 2, 2, 3, 4, 4, 4, 4, 4, 5]) - 3.7 - >>> median_grouped([52, 52, 53, 54]) - 52.5 - - This calculates the median as the 50th percentile, and should be - used when your data is continuous and grouped. In the above example, - the values 1, 2, 3, etc. actually represent the midpoint of classes - 0.5-1.5, 1.5-2.5, 2.5-3.5, etc. The middle value falls somewhere in - class 3.5-4.5, and interpolation is used to estimate it. - - Optional argument ``interval`` represents the class interval, and - defaults to 1. Changing the class interval naturally will change the - interpolated 50th percentile value: - - >>> median_grouped([1, 3, 3, 5, 7], interval=1) - 3.25 - >>> median_grouped([1, 3, 3, 5, 7], interval=2) - 3.5 - - This function does not check whether the data points are at least - ``interval`` apart. - """ - data = sorted(data) - n = len(data) - if n == 0: - raise StatisticsError("no median for empty data") - elif n == 1: - return data[0] - # Find the value at the midpoint. Remember this corresponds to the - # centre of the class interval. - x = data[n//2] - for obj in (x, interval): - if isinstance(obj, (str, bytes)): - raise TypeError('expected number but got %r' % obj) - try: - L = x - interval/2 # The lower limit of the median interval. - except TypeError: - # Mixed type. For now we just coerce to float. - L = float(x) - float(interval)/2 - cf = data.index(x) # Number of values below the median interval. - # FIXME The following line could be more efficient for big lists. - f = data.count(x) # Number of data points in the median interval. - return L + interval*(n/2 - cf)/f - - -def mode(data): - """Return the most common data point from discrete or nominal data. - - ``mode`` assumes discrete data, and returns a single value. This is the - standard treatment of the mode as commonly taught in schools: - - >>> mode([1, 1, 2, 3, 3, 3, 3, 4]) - 3 - - This also works with nominal (non-numeric) data: - - >>> mode(["red", "blue", "blue", "red", "green", "red", "red"]) - 'red' - - If there is not exactly one most common value, ``mode`` will raise - StatisticsError. - """ - # Generate a table of sorted (value, frequency) pairs. - table = _counts(data) - if len(table) == 1: - return table[0][0] - elif table: - raise StatisticsError( - 'no unique mode; found %d equally common values' % len(table) - ) - else: - raise StatisticsError('no mode for empty data') - - -# === Measures of spread === - -# See http://mathworld.wolfram.com/Variance.html -# http://mathworld.wolfram.com/SampleVariance.html -# http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance -# -# Under no circumstances use the so-called "computational formula for -# variance", as that is only suitable for hand calculations with a small -# amount of low-precision data. It has terrible numeric properties. -# -# See a comparison of three computational methods here: -# http://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/ - -def _ss(data, c=None): - """Return sum of square deviations of sequence data. - - If ``c`` is None, the mean is calculated in one pass, and the deviations - from the mean are calculated in a second pass. Otherwise, deviations are - calculated from ``c`` as given. Use the second case with care, as it can - lead to garbage results. - """ - if c is None: - c = mean(data) - ss = _sum((x-c)**2 for x in data) - # The following sum should mathematically equal zero, but due to rounding - # error may not. - ss -= _sum((x-c) for x in data)**2/len(data) - assert not ss < 0, 'negative sum of square deviations: %f' % ss - return ss - - -def variance(data, xbar=None): - """Return the sample variance of data. - - data should be an iterable of Real-valued numbers, with at least two - values. The optional argument xbar, if given, should be the mean of - the data. If it is missing or None, the mean is automatically calculated. - - Use this function when your data is a sample from a population. To - calculate the variance from the entire population, see ``pvariance``. - - Examples: - - >>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5] - >>> variance(data) - 1.3720238095238095 - - If you have already calculated the mean of your data, you can pass it as - the optional second argument ``xbar`` to avoid recalculating it: - - >>> m = mean(data) - >>> variance(data, m) - 1.3720238095238095 - - This function does not check that ``xbar`` is actually the mean of - ``data``. Giving arbitrary values for ``xbar`` may lead to invalid or - impossible results. - - Decimals and Fractions are supported: - - >>> from decimal import Decimal as D - >>> variance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")]) - Decimal('31.01875') - - >>> from fractions import Fraction as F - >>> variance([F(1, 6), F(1, 2), F(5, 3)]) - Fraction(67, 108) - - """ - if iter(data) is data: - data = list(data) - n = len(data) - if n < 2: - raise StatisticsError('variance requires at least two data points') - ss = _ss(data, xbar) - return ss/(n-1) - - -def pvariance(data, mu=None): - """Return the population variance of ``data``. - - data should be an iterable of Real-valued numbers, with at least one - value. The optional argument mu, if given, should be the mean of - the data. If it is missing or None, the mean is automatically calculated. - - Use this function to calculate the variance from the entire population. - To estimate the variance from a sample, the ``variance`` function is - usually a better choice. - - Examples: - - >>> data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25] - >>> pvariance(data) - 1.25 - - If you have already calculated the mean of the data, you can pass it as - the optional second argument to avoid recalculating it: - - >>> mu = mean(data) - >>> pvariance(data, mu) - 1.25 - - This function does not check that ``mu`` is actually the mean of ``data``. - Giving arbitrary values for ``mu`` may lead to invalid or impossible - results. - - Decimals and Fractions are supported: - - >>> from decimal import Decimal as D - >>> pvariance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")]) - Decimal('24.815') - - >>> from fractions import Fraction as F - >>> pvariance([F(1, 4), F(5, 4), F(1, 2)]) - Fraction(13, 72) - - """ - if iter(data) is data: - data = list(data) - n = len(data) - if n < 1: - raise StatisticsError('pvariance requires at least one data point') - ss = _ss(data, mu) - return ss/n - - -def stdev(data, xbar=None): - """Return the square root of the sample variance. - - See ``variance`` for arguments and other details. - - >>> stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]) - 1.0810874155219827 - - """ - var = variance(data, xbar) - try: - return var.sqrt() - except AttributeError: - return math.sqrt(var) - - -def pstdev(data, mu=None): - """Return the square root of the population variance. - - See ``pvariance`` for arguments and other details. - - >>> pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]) - 0.986893273527251 - - """ - var = pvariance(data, mu) - try: - return var.sqrt() - except AttributeError: - return math.sqrt(var) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..14d43a1 --- /dev/null +++ b/setup.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +import os +import sys +from distutils.sysconfig import get_python_lib +from distutils.core import setup +from setuptools import find_packages + +setup(name='simpynet', + version='1.0', + description='A simulator Python framework used to run simulations on all TCP/IP levels in a network', + author=['Davide Neri','Artem Piazzi'], + author_email='davideneri18@gmail.com', + url='https://github.com/dido18/spn', + packages=find_packages(), + include_package_data=True, + #packages=['simpynet'], + ) diff --git a/__init__.py b/simpynet/__init__.py similarity index 84% rename from __init__.py rename to simpynet/__init__.py index fda3775..17a791f 100644 --- a/__init__.py +++ b/simpynet/__init__.py @@ -4,7 +4,8 @@ @author: ubuntudido """ -from lib import simpy +import simpy +#from lib import simpy from src import physical from src import link from src import network @@ -23,16 +24,17 @@ from src.network.ip import IP from src.transport.udp_protocol import UDP_Protocol from src.transport.udp_packet import UDP_Packet -from src.application.dhcp_client import DHCP_client +from src.application.dhcp_client import DHCP_client from src.application.dhcp_server import DHCP_server from src.environment import Environment -from lib.prettytable.prettytable import PrettyTable +#from lib.prettytable.prettytable import PrettyTable -__LOGNAME__ = "SimPyNet" -__STATISTIC_LOG__= "Statistic" + +__LOGNAME__ = "simpynet_log" +__STATISTIC_LOG__= "simpynet_statistic" __PHYSICAL_LOG_N__ = 2 __LINK_LOG_N__ = 3 __NETWORK_LOG_N__ = 4 @@ -41,7 +43,7 @@ __STATISTIC_N__ = 100 __all__ = [ - 'physical', + 'physical', 'link', 'network', 'transport', @@ -50,7 +52,7 @@ 'PointToPoint', 'Hub', 'Frame', - 'Switch', + 'Switch', 'NIC', 'Mac', 'Host', @@ -65,4 +67,4 @@ 'statistics', 'DHCP_client', 'DHCP_server' -] \ No newline at end of file +] diff --git a/examples/dns.py b/simpynet/examples/dns.py similarity index 100% rename from examples/dns.py rename to simpynet/examples/dns.py diff --git a/examples/example_DHCP.py b/simpynet/examples/example_DHCP.py similarity index 84% rename from examples/example_DHCP.py rename to simpynet/examples/example_DHCP.py index 1da7968..a27a43f 100644 --- a/examples/example_DHCP.py +++ b/simpynet/examples/example_DHCP.py @@ -6,55 +6,54 @@ """ -import SimPyNet as spn +import simpynet as spn def example( env ): - + """ - + dhcp_client------l1------dhcp_server - + """ # -------------------------------------------------------- c_host = spn.Host( env, spn.Mac('aa.aa.aa.aa.aa.aa'), spn.IP('0.0.0.0') ) s_host = spn.Host( env, spn.Mac('bb.bb.bb.bb.bb.bb'), spn.IP('192.168.1.1') ) - + client=spn.DHCP_client( env , c_host ) server=spn.DHCP_server( env , s_host ) - + def choose_ip_server( lista_ip): - if len(lista_ip) > 0: + if len(lista_ip) > 0: return lista_ip.pop(0) - + else: return None - - + + def choose_ip_for_client(): return '123' - + server.add_function_choose( choose_ip_for_client) - + client.add_function_choose( choose_ip_server) - + l1 = spn.Link(env, lambda x,y: 1 , lambda src,trg,l: 1) - + spn.physical.plug( l1 , c_host ) spn.physical.plug( l1 , s_host ) - - + + def funct(): yield env.timeout(1) client.send_DHCP_DISCOVERED() - + env.process( funct()) - - - - + + + + e=spn.Environment() e.add_network(example) e.run( 1, spn.__TRANSPORT_LOG_N__) - \ No newline at end of file diff --git a/examples/example_P21.py b/simpynet/examples/example_P21.py similarity index 100% rename from examples/example_P21.py rename to simpynet/examples/example_P21.py diff --git a/examples/examples_link.py b/simpynet/examples/examples_link.py similarity index 78% rename from examples/examples_link.py rename to simpynet/examples/examples_link.py index 2c9ab5d..35989a3 100644 --- a/examples/examples_link.py +++ b/simpynet/examples/examples_link.py @@ -4,24 +4,26 @@ @author: unkown """ -import SimPyNet as spn + +#import SimPyNet as spn +import spn def example1(env): - + """ - This example simulates a simple network with three NIC interface connect + This example simulates a simple network with three NIC interface connect with a switch. nic0 (with mac address "00:00:00:00:00:00") sends data with destination nic1 (with mac address "00:00:00:00:00:01"). Switch sends data in broadcast, because its arp table is empty. Both nic1 anc nic2 receives data. - After 10 seconds, nic1 send a packet to nic0 + After 10 seconds, nic1 send a packet to nic0 (with destination mac "00:00:00:00:00:00"). Switch has the values in its arp table, then sends data only to nic0. - - nic1 - + + nic1 + |nic0|----l0----| switch0 |----l1 -----|nic1| | | @@ -29,74 +31,74 @@ def example1(env): | | |nic2| - + """ - + l0 = spn.Link( env, lambda n, x:3, lambda src,trg, l: 1 ) l1 = spn.Link( env, lambda n, x:3, lambda src,trg, l: 1 ) l2 = spn.Link( env, lambda n, x:3, lambda src,trg, l: 1 ) - + nic0 = spn.NIC(env, spn.Mac("00:00:00:00:00:00")) nic1 = spn.NIC(env, spn.Mac("00:00:00:00:00:01")) nic2 = spn.NIC(env, spn.Mac("00:00:00:00:00:02")) - + queue_size=10000 # bit switch0 = spn.Switch(env, lambda x,s:0.1, queue_size ) - + spn.physical.plug( l0 , nic0 ) spn.physical.plug( l1 , nic1 ) - spn.physical.plug( l2 , nic2 ) - - + spn.physical.plug( l2 , nic2 ) + + spn.physical.plug( l0 , switch0 ) spn.physical.plug( l1 , switch0 ) spn.physical.plug( l2 , switch0 ) - + data_host0 = [] for i in range(10): data_host0.append("Hello from host0") - + data_host1 = [] for i in range(10): data_host1.append("Hello from host1") - - - def receiver0( data ): + + + def receiver0( data ): print data - def receiver1( data ): + def receiver1( data ): print data - - def receiver2( data ): + + def receiver2( data ): print data - - + + nic0_h = nic0.add_host_handler( receiver0 ) # return NIC 's handler to send nic1_h = nic1.add_host_handler( receiver1 ) - nic2_h = nic2.add_host_handler( receiver2 ) - - + nic2_h = nic2.add_host_handler( receiver2 ) + + def sender0(): while( len( data_host0 ) >0): data=data_host0.pop(0) nic0_h( data , spn.Mac("00:00:00:00:00:01") ) yield env.timeout(6) - + def sender1(): yield env.timeout(10) while( len( data_host1 ) >0): data=data_host1.pop(0) nic1_h( data , spn.Mac("00:00:00:00:00:00") ) yield env.timeout(7) - - dcrd_nic2=nic2.collector_discardedframes + + dcrd_nic2=nic2.collector_discardedframes rcv_nic1= nic1.collector_receivedframes rcv_nic0= nic0.collector_sentframes - env.add_collector_functions('discarded nic2', dcrd_nic2 ) - env.add_collector_functions('received nic1', rcv_nic1 ) - env.add_collector_functions('received nic0', rcv_nic0 ) + env.add_collector_functions('discarded nic2', dcrd_nic2 ) + env.add_collector_functions('received nic1', rcv_nic1 ) + env.add_collector_functions('received nic0', rcv_nic0 ) env.process(sender0()) env.process(sender1()) @@ -104,7 +106,3 @@ def sender1(): e = spn.Environment() e.add_network(example1) e.run( 1 , spn.__LINK_LOG_N__ ) - - - - \ No newline at end of file diff --git a/examples/examples_network.py b/simpynet/examples/examples_network.py similarity index 100% rename from examples/examples_network.py rename to simpynet/examples/examples_network.py diff --git a/examples/examples_physical.py b/simpynet/examples/examples_physical.py similarity index 100% rename from examples/examples_physical.py rename to simpynet/examples/examples_physical.py diff --git a/examples/selective_repeat.py b/simpynet/examples/selective_repeat.py similarity index 100% rename from examples/selective_repeat.py rename to simpynet/examples/selective_repeat.py diff --git a/examples/time_collector.py b/simpynet/examples/time_collector.py similarity index 100% rename from examples/time_collector.py rename to simpynet/examples/time_collector.py diff --git a/src/__init__.py b/simpynet/src/__init__.py similarity index 100% rename from src/__init__.py rename to simpynet/src/__init__.py diff --git a/src/application/__init__.py b/simpynet/src/application/__init__.py similarity index 100% rename from src/application/__init__.py rename to simpynet/src/application/__init__.py diff --git a/src/application/dhcp_client.py b/simpynet/src/application/dhcp_client.py similarity index 80% rename from src/application/dhcp_client.py rename to simpynet/src/application/dhcp_client.py index 36359be..3578351 100644 --- a/src/application/dhcp_client.py +++ b/simpynet/src/application/dhcp_client.py @@ -5,127 +5,124 @@ @author: dido """ -import SimPyNet as spn -import logging +import simpynet as spn +import logging class DHCP_client( ): - - - def __init__(self , env , host ): - + + + def __init__(self , env , host ): + self.env = env - self.host = host + self.host = host self.client_port = 67 self.list_ip_server = [] - self.choose_ip_server = None - + self.choose_ip_server = None + self.udp=spn.UDP_Protocol( env ) - send_host=self.host.add_transport_handler( self.udp.get_handler() ) + send_host=self.host.add_transport_handler( self.udp.get_handler() ) self.udp.add_handler( send_host ) self.send_udp = self.udp.add_application_handler( self._receive_dhcp , self.client_port ) # handler per ricevere - + self.lgr = logging.getLogger(spn.__LOGNAME__) - + def _receive_dhcp( self, message ): """ - _receive_dhcp( message ) - the client recieves one or more DHCPOFFER + _receive_dhcp( message ) - the client recieves one or more DHCPOFFER messages from one or more servers. """ - + diz_opt =message.values - option=diz_opt['option_53'] - - if option =='DHCP Offer': + option=diz_opt['option_53'] + + if option =='DHCP Offer': self.list_ip_server.append( diz_opt['option_54'] ) ip_server = self.choose_ip_server( self.list_ip_server ) self.send_DHCP_REQUEST( diz_opt , ip_server ) self.lgr.log(spn.__APPLICATION_LOG_N__," " + str(float(self.env.now)) + ' DHCP client sends DHCP REQUEST message to :'+str(ip_server)+' with request IP : '+str(diz_opt['YIADDR']) ) - + if option == 'DHCP Ack': self.host.ip_addr=diz_opt['YIADDR'] self.lgr.log(spn.__APPLICATION_LOG_N__," " + str(float(self.env.now)) + ' DHCP client allocates IP : '+str( diz_opt['YIADDR'] )) - + def send_DHCP_DISCOVERED( self ): """ - send_DHCP_DISCOVERED() - send a DHCPdiscovered message in broadcast + send_DHCP_DISCOVERED() - send a DHCPdiscovered message in broadcast on its local physical subnet. """ - - dscv_msg = DHCP_DISCOVERED_message( self.host.mac_addr , self.host.ip_addr) - self.send_udp( dscv_msg , self.client_port , 68 , spn.IP('255.255.255.255') ) + + dscv_msg = DHCP_DISCOVERED_message( self.host.mac_addr , self.host.ip_addr) + self.send_udp( dscv_msg , self.client_port , 68 , spn.IP('255.255.255.255') ) self.lgr.log(spn.__APPLICATION_LOG_N__," " + str(float(self.env.now)) + ' DHCP client sends DHCP DISCOVERED message to : 255.255.255.255') - - + + def send_DHCP_REQUEST( self , diz_opt , ip_server ): """ - send_DHCPREQUEST( diz_option) - In response to the DHCP offer, - the client replies with a DHCP request, broadcast to the server, + send_DHCPREQUEST( diz_option) - In response to the DHCP offer, + the client replies with a DHCP request, broadcast to the server, requesting the offered address. """ ip_offer = diz_opt['YIADDR'] qst_msg = DHCP_REQUEST_message( self.host.mac_addr ,self.host.ip_addr, - ip_server , - ip_offer + ip_server , + ip_offer ) - + self.send_udp( qst_msg , self.client_port , 68 , spn.IP('255.255.255.255') ) - + def add_function_choose( self, func ): """ - add_function_choose(func ) - adds function that choose ip_server. + add_function_choose(func ) - adds function that choose ip_server. """ - self.choose_ip_server = func - + self.choose_ip_server = func + class DHCP_DISCOVERED_message( ): - - + + def __init__( self , mac_client , client_ip ): - + self.values={} self.values['CIADDR'] = client_ip # client IP address self.values['CHADDR'] = mac_client # client hardware address (MAC) self.values['option_53']='DHCP Discover' - - + + def __str__(self): s=' option : '+ self.values['option_53'] return s - + def __len__(self): return len( self.values) - + def __repr__(self): s=' option : '+ self.values['option_53'] return s - - - + + + class DHCP_REQUEST_message( ): - - + + def __init__( self , client_mac , client_ip , ip_server , ip_request ): - + self.values={} - - self.values['CIADDR'] = client_ip - self.values['CHADDR'] = client_mac - self.values['YIADDR'] = ip_server # choosen ip server + + self.values['CIADDR'] = client_ip + self.values['CHADDR'] = client_mac + self.values['YIADDR'] = ip_server # choosen ip server self.values['option_50'] = ip_request # ip request self.values['option_53']='DHCP Request' - - + + def __str__(self): s=' option : '+ self.values['option_53'] return s - + def __len__(self): return len( self.values) - + def __repr__(self): s=' option : '+ self.values['option_53'] return s - - - \ No newline at end of file diff --git a/src/application/dhcp_server.py b/simpynet/src/application/dhcp_server.py similarity index 76% rename from src/application/dhcp_server.py rename to simpynet/src/application/dhcp_server.py index 4fe5d88..accf157 100644 --- a/src/application/dhcp_server.py +++ b/simpynet/src/application/dhcp_server.py @@ -4,141 +4,140 @@ @author: dido """ -import SimPyNet as spn +import simpynet as spn import logging class DHCP_server( ): - - - def __init__(self , env , host ): - + + + def __init__(self , env , host ): + self.env = env - self.host = host - self.my_port = 68 + self.host = host + self.my_port = 68 self.udp = spn.UDP_Protocol( env ) self.choose_ip_for_client = None - - send_host = self.host.add_transport_handler( self.udp.get_handler() ) + + send_host = self.host.add_transport_handler( self.udp.get_handler() ) rcv_udp = self.udp.add_handler( send_host ) self.send_udp = self.udp.add_application_handler( self._rcv_server_dhcp , self.my_port ) # handler per ricevere self.lgr = logging.getLogger(spn.__LOGNAME__) - + def add_function_choose( self , funct ): """ add_function_choose(funct ) _ """ - self.choose_ip_for_client = funct - - + self.choose_ip_for_client = funct + + def _rcv_server_dhcp( self , message) : - + """ _rcv_server_dhcp( message) - server responds in different ways for different received messages from client. - + """ - + diz_opt = message.values option =diz_opt['option_53'] - + if option =='DHCP Discover': - + """ Each server may respond with a DHCPOFFER message that includes an available network address in the 'yiaddr' field """ - + ip_offer = self.choose_ip_for_client() self.send_DHCP_OFFER( diz_opt , ip_offer ) self.lgr.log(spn.__APPLICATION_LOG_N__," " + str(float(self.env.now)) + ' DHCP server sends DHCP OFFER message to :'+str(diz_opt['CIADDR'])+' with IP offer : '+str(ip_offer)) - + elif option =='DHCP Request' : - + ip_server = diz_opt['YIADDR'] - + if str(ip_server) != str(self.host.ip_addr): self.lgr.log(spn.__APPLICATION_LOG_N__," " + str(float(self.env.now)) + ' DHCP server declined DHCP REQUEST ') else: self.send_DHCP_ACK( diz_opt ) self.lgr.log(spn.__APPLICATION_LOG_N__," " + str(float(self.env.now)) + ' DHCP server sends DHCP ACK to: '+str(diz_opt['CIADDR'])) - - - - + + + + def send_DHCP_OFFER( self , diz_opt , ip_offer ): - + mac_client = diz_opt['CHADDR'] - ip_client = diz_opt['CIADDR'] - offer_msg = DHCP_OFFER_message( self.host.ip_addr , - mac_client, - ip_offer , + ip_client = diz_opt['CIADDR'] + offer_msg = DHCP_OFFER_message( self.host.ip_addr , + mac_client, + ip_offer , ip_client ) - + self.host.default_gateway= mac_client - - self.send_udp(offer_msg, self.my_port , 67 , spn.IP() ) #sent in broadcast - - + + self.send_udp(offer_msg, self.my_port , 67 , spn.IP() ) #sent in broadcast + + def send_DHCP_ACK( self, diz_opt ): - + ip_client = diz_opt['option_50'] - + dst_ip = diz_opt['CIADDR'] ack_msg = DHCP_ACK_message( ip_client ) - + self.send_udp( ack_msg , self.my_port , 67 , dst_ip ) - - - + + + class DHCP_OFFER_message( ): - - + + def __init__( self ,server_ip , client_mac , ip_offer , client_ip): - + self.values={} - self.values['CIADDR'] = client_ip + self.values['CIADDR'] = client_ip self.values['YIADDR'] = ip_offer # your (client) ip self.values['CHADDR'] = client_mac # Client hardware address - + self.values['option_53']='DHCP Offer' #defines the "type" of the DHCP message. - self.values['option_54']=str(server_ip) - - - + self.values['option_54']=str(server_ip) + + + def __str__(self): s='Option : '+self.values['option_53'] return s - + def __len__(self): return len( self.values) - + def __repr__(self): s='Option : '+self.values['option_53'] return s - - - + + + class DHCP_ACK_message( ): - - + + def __init__( self , yiaddr ): - - self.values={} - self.values['YIADDR']= yiaddr + + self.values={} + self.values['YIADDR']= yiaddr self.values['option_53'] ='DHCP Ack' - + def __str__(self): s='Option : '+self.values['option_53'] return s - + def __len__(self): return len( self.values) - + def __repr__(self): s='Option : '+self.values['option_53'] return s - \ No newline at end of file diff --git a/src/environment/__init__.py b/simpynet/src/environment/__init__.py similarity index 100% rename from src/environment/__init__.py rename to simpynet/src/environment/__init__.py diff --git a/src/environment/environment.py b/simpynet/src/environment/environment.py similarity index 82% rename from src/environment/environment.py rename to simpynet/src/environment/environment.py index 6d67a9e..ee1b26a 100644 --- a/src/environment/environment.py +++ b/simpynet/src/environment/environment.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- """ -This module simulates the environment extending the environment provided by +This module simulates the environment extending the environment provided by ``simpy``.\n It also collects the statistics. """ import logging -import SimPyNet +import simpynet -class Environment(SimPyNet.simpy.Environment): +class Environment(simpynet.simpy.Environment): """ Simulates the Environment and collects the statistics data. """ @@ -17,9 +17,9 @@ def __init__( self, initial_time=0 ): self.collectors_function = {} self.collectors_values={} self.network = None - self.lgr = logging.getLogger(SimPyNet.__LOGNAME__) + self.lgr = logging.getLogger(simpynet.__LOGNAME__) self.frmt = logging.Formatter('%(asctime)s %(message)s ') - + def add_network( self , network): @@ -28,8 +28,8 @@ def add_network( self , network): When run() is called, builds a network with the given function. """ self.network = network - - + + def add_collector_functions( self , cltr_name, collector , interval_collection=1 ): """ E.add_collector_functions( collector_name , collector , interval_collection=1 ) @@ -38,35 +38,35 @@ def add_collector_functions( self , cltr_name, collector , interval_collection=1 """ self.collectors_function[cltr_name] = [ collector, interval_collection ] if not self.collectors_values.has_key(cltr_name): - self.collectors_values[cltr_name]= {} - + self.collectors_values[cltr_name]= {} + return self.collectors_values[cltr_name] - - + + def _add_values( self , cltr_name ): """ Saves the collected values for each collector. """ - + collector = self.collectors_function[cltr_name][0] time = float(self.now) if not self.collectors_values[cltr_name].has_key(time): self.collectors_values[cltr_name][time] = [] - - self.collectors_values[cltr_name][time].append( collector() ) - + self.collectors_values[cltr_name][time].append( collector() ) + + def run_stats_function( self , function ): """ E.run_stats_function( function ) Returns function( collectors_values ) """ return function( self.collectors_values ) - - + + def _stats_process( self ): """ - Main process that manages the statistics collectors and the time the + Main process that manages the statistics collectors and the time the next one is due to be called. """ time_next_collection = { name : 0 @@ -79,49 +79,44 @@ def _stats_process( self ): time_next_collection[name] = self.collectors_function[name][1] time = time_next_collection[name] if time < min_interval: min_interval = time - + if self.peek() == float('inf'): break - + yield self.timeout( min_interval ) - + for name in time_next_collection.iterkeys(): time_next_collection[name]-=min_interval min_interval = float("inf") - + def run( self , number_of_runs = 1 , logging_level = 1 , until=None): """ E.run( number_of_runs = 1 , logging_level = 1 , until=None ) Builds and simulates a given network *number_of_runs* times. *logging_level* is the level of the logger. - *until* is the maximum time of execution. It's set to None unless + *until* is the maximum time of execution. It's set to None unless specified. """ - SimPyNet.simpy.Environment.__init__( self ) - - fh_log = logging.FileHandler(SimPyNet.__LOGNAME__, mode='a') + simpynet.simpy.Environment.__init__( self ) + + fh_log = logging.FileHandler(simpynet.__LOGNAME__, mode='a') fh_log.setFormatter(self.frmt) self.lgr.addHandler(fh_log) - + self.network( self ) - - self.lgr.setLevel(logging_level) + + self.lgr.setLevel(logging_level) self.lgr.log( 9 , ' New run ; Remaining runs : '+str(number_of_runs)) - - self.process(self._stats_process()) - - - number_of_runs -= 1 - SimPyNet.simpy.Environment.run( self ) - - self.lgr.removeHandler(fh_log) - - if number_of_runs > 0: - self.run( number_of_runs , logging_level , until ) + self.process(self._stats_process()) + + + number_of_runs -= 1 + simpynet.simpy.Environment.run( self ) - + self.lgr.removeHandler(fh_log) - \ No newline at end of file + if number_of_runs > 0: + self.run( number_of_runs , logging_level , until ) diff --git a/src/environment/environment.py.bk b/simpynet/src/environment/environment.py.bk similarity index 100% rename from src/environment/environment.py.bk rename to simpynet/src/environment/environment.py.bk diff --git a/src/link/SimPyNet b/simpynet/src/link/SimPyNet similarity index 89% rename from src/link/SimPyNet rename to simpynet/src/link/SimPyNet index bbfab2e..7bbdc15 100644 --- a/src/link/SimPyNet +++ b/simpynet/src/link/SimPyNet @@ -1,5 +1,5 @@ 2014-05-29 19:38:16,655 New run -2014-05-29 19:38:16,656 0.0 192.0.0.0 : Sent to: 192.0.0.1 -2014-05-29 19:38:16,656 3.0 00.00.00.00.00 : Sent to: ff:ff:ff:ff:ff:ff -2014-05-29 19:38:16,656 11.0 192.0.0.1 : Sent to: 192.0.0.0 -2014-05-29 19:38:16,657 14.0 00.00.00.00.01 : Sent to: ff:ff:ff:ff:ff:ff +2014-05-29 19:38:16,656 0.0 192.0.0.0 : Sent to: 192.0.0.1 +2014-05-29 19:38:16,656 3.0 00.00.00.00.00 : Sent to: ff:ff:ff:ff:ff:ff +2014-05-29 19:38:16,656 11.0 192.0.0.1 : Sent to: 192.0.0.0 +2014-05-29 19:38:16,657 14.0 00.00.00.00.01 : Sent to: ff:ff:ff:ff:ff:ff diff --git a/src/link/__init__.py b/simpynet/src/link/__init__.py similarity index 100% rename from src/link/__init__.py rename to simpynet/src/link/__init__.py diff --git a/src/link/df b/simpynet/src/link/df similarity index 100% rename from src/link/df rename to simpynet/src/link/df diff --git a/src/link/frame.py b/simpynet/src/link/frame.py similarity index 93% rename from src/link/frame.py rename to simpynet/src/link/frame.py index 07fb5a8..1aca402 100644 --- a/src/link/frame.py +++ b/simpynet/src/link/frame.py @@ -12,15 +12,15 @@ def __init__(self,src_mac,dst_mac,datagram, CRC=None, tpe=None): self.dst_mac=dst_mac self.datagram=datagram self.size=len(datagram)+26 - - + + def __str__(self): s='Source: '+repr(self.src_mac)+' Destination: '+repr(self.dst_mac) return s - + def __len__(self): return self.size - + def __repr__(self): s='Source: '+repr(self.src_mac)+' Destination: '+repr(self.dst_mac) - return s \ No newline at end of file + return s diff --git a/src/link/mac.py b/simpynet/src/link/mac.py similarity index 87% rename from src/link/mac.py rename to simpynet/src/link/mac.py index 71a7ac3..5ec5d49 100644 --- a/src/link/mac.py +++ b/simpynet/src/link/mac.py @@ -10,21 +10,19 @@ class Mac(): """ def __init__(self, mac = "ff:ff:ff:ff:ff:ff"): self.mac = mac.lower() - - + + def __eq__(self, m): return self.mac == m.mac - - + + def __str__(self): return self.mac - - + + def __repr__(self): return self.mac - + @staticmethod def get_broadcast(): return "ff:ff:ff:ff:ff:ff" - - \ No newline at end of file diff --git a/src/link/nic.py b/simpynet/src/link/nic.py similarity index 84% rename from src/link/nic.py rename to simpynet/src/link/nic.py index 4d38d35..9c329f6 100644 --- a/src/link/nic.py +++ b/simpynet/src/link/nic.py @@ -2,7 +2,7 @@ """ This module simulates the Network Interface Card at link layer. """ -import SimPyNet +import simpynet from frame import Frame from mac import Mac import logging @@ -14,35 +14,35 @@ class NIC(): ``simpy``. *mac_addr* is the MAC Address associated to the NIC \n """ - def __init__(self, + def __init__(self, environment, mac_addr - ): + ): self.mac_addr=mac_addr self.corrupted_frames = 0 self.host_handler = None self.output_handler = None - + self.env = environment - - self.resource = SimPyNet.simpy.Resource( self.env , 1 ) - - self.lgr = logging.getLogger(SimPyNet.__LOGNAME__) - + + self.resource = simpynet.simpy.Resource( self.env , 1 ) + + self.lgr = logging.getLogger(simpynet.__LOGNAME__) + self.discarded_frames = 0 self.sent_frames = 0 self.received_frames = 0 - - - + + + def add_handler(self, name , handler): """ N.add_handler(handler) - add handler to NIC """ self.output_handler=handler # dimensione della coda return self.get_handler() - - + + def get_handler( self ): """ N.get_handler() - get NIC's handler @@ -51,8 +51,8 @@ def _new_handler( name, data, d_trm_delay = 0.0): return self.env.process(self._receive(name, data, d_trm_delay )) return _new_handler - - + + def add_host_handler( self, host_handler ): """ N.add_host_handler(handler) - add host handler to NIC @@ -60,11 +60,11 @@ def add_host_handler( self, host_handler ): def _new_handler( data , mac ): return self.env.process(self._send( data , mac )) self.host_handler = host_handler - + return _new_handler - - - + + + def _receive( self , name , data , d_trm_delay ): """ Handles the frame and sends the processed data to the host. @@ -74,16 +74,16 @@ def _receive( self , name , data , d_trm_delay ): if (hasattr(data, "dst_mac") and hasattr(data, "datagram")): if self.mac_addr == data.dst_mac or data.dst_mac == Mac() : - self.lgr.log( SimPyNet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.mac_addr) + ' : Received from: ' + repr(data.src_mac) ) + self.lgr.log( simpynet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.mac_addr) + ' : Received from: ' + repr(data.src_mac) ) self.host_handler( data.datagram ) else: self.discarded_frames += 1 - self.lgr.log( SimPyNet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.mac_addr) + ' : Discarded from: ' + repr(data.src_mac) ) + self.lgr.log( simpynet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.mac_addr) + ' : Discarded from: ' + repr(data.src_mac) ) else: self.corrupted_frames += 1 - - + + def _send( self , data , mac = Mac()): """ Handles the data and sends the processed frame to the host. @@ -92,35 +92,34 @@ def _send( self , data , mac = Mac()): with self.resource.request() as req: yield req yield self.output_handler( repr(self) , f ) - self.lgr.log( SimPyNet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.mac_addr) + ' : Sent to: ' + repr(mac) ) + self.lgr.log( simpynet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.mac_addr) + ' : Sent to: ' + repr(mac) ) self.sent_frames += 1 - - + + def collector_discardedframes( self ): """ collector_discardedframes() - returns number of discarded frames """ return self.discarded_frames - - + + def collector_sentframes(self ): """ collector_sentframes() - returns number of sent frames """ return self.sent_frames - - + + def collector_receivedframes( self ): """ collector_receivedframes() - returns number of received frames """ return self.received_frames - - + + def __repr__(self): """ repr( NIC ) - NIC's unique name identified by its MAC address """ return repr(self.mac_addr) - diff --git a/src/link/switch.py b/simpynet/src/link/switch.py similarity index 79% rename from src/link/switch.py rename to simpynet/src/link/switch.py index 3979210..99a2c97 100644 --- a/src/link/switch.py +++ b/simpynet/src/link/switch.py @@ -2,164 +2,163 @@ """ This module simulates the switch at the link layer. """ -import SimPyNet +import simpynet import logging unique_number = 0 -class Switch(SimPyNet.physical.hub.Hub): +class Switch(simpynet.physical.hub.Hub): """ Simulates a switch at the link layer.\n *environment* is the class which simulates the passing of time provided by ``simpy``. \n - If not specified processing delay is set to ``0`` + If not specified processing delay is set to ``0`` and queue size is ``inf``.\n """ - def __init__(self, - environment, + def __init__(self, + environment, prcss_delay= lambda x, y : 0.0, queue_size='inf' ): self.unique_number = self.__class__.unique_number self.__class__.unique_number = self.__class__.unique_number + 1 - - self.lgr = logging.getLogger(SimPyNet.__LOGNAME__) - + + self.lgr = logging.getLogger(simpynet.__LOGNAME__) + self.prcss_delay = prcss_delay self.queue_size = queue_size self.arp_table = {} self.queues = {} - self.handlers = {} - + self.handlers = {} + self.lost_frames = 0 self.sent_frames = 0 self.received_frames = 0 self.processing_time = 0 - + self.env = environment - - self.switch_resource = SimPyNet.simpy.Resource( self.env , 1 ) + + self.switch_resource = simpynet.simpy.Resource( self.env , 1 ) self.output_buffer = {} self.prg_delay = lambda x,y:0.0 - - - + + + def add_handler(self, name , handler): """ S.add_handler(handler) - add handler to switch """ self.queues[name]=[0,0] # first variable input queue, second output - self.output_buffer[name] = SimPyNet.simpy.Resource( self.env , 1 ) - SimPyNet.physical.hub.Hub.add_handler( self , name , handler) + self.output_buffer[name] = simpynet.simpy.Resource( self.env , 1 ) + simpynet.physical.hub.Hub.add_handler( self , name , handler) return self.get_handler() - - + + def get_handler( self ): """ S.get_handler() - get switch's handler """ - def _new_handler(name, data, d_trm_delay = 0.0): + def _new_handler(name, data, d_trm_delay = 0.0): return self.env.process(self._add_to_queue(name, data, d_trm_delay )) - + return _new_handler - - + + def _add_to_queue( self , name , data , d_trm_delay ): """ Adds the data to the input-buffer. If full, the frame is lost """ yield self.env.timeout( d_trm_delay ) - self.lgr.log( SimPyNet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Received from: ' + repr(data.src_mac) ) + self.lgr.log( simpynet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Received from: ' + repr(data.src_mac) ) self.received_frames += 1 - if (self.queues[name][0] + len( data ) > self.queue_size + if (self.queues[name][0] + len( data ) > self.queue_size or self.queue_size==0): self.lost_frames += 1 - self.lgr.log( SimPyNet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac)) + self.lgr.log( simpynet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac)) else: self.queues[name][0] += len(data) self.env.process( self._switch( name, data, d_trm_delay) ) - - + + def _switch( self , name , data , d_trm_delay ): """ - Simulates the switching process. + Simulates the switching process. The data is processed and put to the appropriate output buffer. """ with self.switch_resource.request() as req: yield req temp_time = float(self.env.now) - yield self.env.timeout( self.prcss_delay( data, repr( self ) ) ) + yield self.env.timeout( self.prcss_delay( data, repr( self ) ) ) src_mac = repr(data.src_mac) for mac in self.arp_table: if self.arp_table[mac] == name: del self.arp_table[mac] break self.arp_table[src_mac] = name # si aggiunge all'arp table il riferimento mac-interface - + self.queues[name][0] -= len(data) - + dst_mac = repr(data.dst_mac) if self.arp_table.has_key(dst_mac): self.env.process ( self._deliver( self.arp_table[dst_mac] , data , d_trm_delay ) ) - + else: self.env.process( self._broadcast( name , data , d_trm_delay ) ) self.processing_time += float(self.env.now) - temp_time - - + + def _deliver( self , name , data , d_trm_delay): """ Simulates the transmission from the output-buffer on the appropriate port. """ - if (self.queues[name][1] + len( data ) > self.queue_size + if (self.queues[name][1] + len( data ) > self.queue_size or self.queue_size==0): self.lost_frames += 1 - self.lgr.log( SimPyNet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac)) + self.lgr.log( simpynet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac)) else: self.queues[name][1] += len(data) with self.output_buffer[name].request() as req: yield req - yield self.env.process(SimPyNet + yield self.env.process(simpynet .physical.hub.Hub ._deliver(self, name, data, d_trm_delay)) - self.lgr.log( SimPyNet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Sent to: ' + repr(data.dst_mac) ) + self.lgr.log( simpynet.__LINK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Sent to: ' + repr(data.dst_mac) ) self.sent_frames +=1 self.queues[name][1] -= len(data) - - + + def collector_sentframes( self ): """ collector_sentframes() - returns number of sent frames """ return self.sent_frames - - + + def collector_lostframes(self ): """ collector_sentframes() - returns number of lost frames """ return self.lost_frames - - + + def collector_receivedframes( self ): """ collector_receivedframes() - returns number of received frames """ return self.received_frames - - + + def collector_processingtime( self ): """ collector_processingtime() - returns total processing time """ return self.processing_time - - + + def __repr__(self): """ repr( Switch ) - Switch's unique name """ return "Switch"+str(self.unique_number) - diff --git a/src/network/__init__.py b/simpynet/src/network/__init__.py similarity index 100% rename from src/network/__init__.py rename to simpynet/src/network/__init__.py diff --git a/src/network/datagram.py b/simpynet/src/network/datagram.py similarity index 93% rename from src/network/datagram.py rename to simpynet/src/network/datagram.py index d1a10a9..364e8ee 100644 --- a/src/network/datagram.py +++ b/simpynet/src/network/datagram.py @@ -14,15 +14,15 @@ def __init__( self , src_ip , dst_ip , segment , protocol=None , ttl=float('inf' self.protocol = protocol self.ttl = ttl self.size=len(segment)+160 - - + + def __str__(self): s='Source: '+repr(self.src_ip)+' Destination: '+repr(self.dst_ip) return s - + def __len__(self): return self.size - + def __repr__(self): s='Source: '+repr(self.src_ip)+' Destination: '+repr(self.dst_ip) - return s \ No newline at end of file + return s diff --git a/src/network/host.py b/simpynet/src/network/host.py similarity index 74% rename from src/network/host.py rename to simpynet/src/network/host.py index 9e9347e..4e14eba 100644 --- a/src/network/host.py +++ b/simpynet/src/network/host.py @@ -2,124 +2,124 @@ """ This module simulates the host at network layer. """ -import SimPyNet +import simpynet from ip import IP -class Host(SimPyNet.link.nic.NIC): +class Host(simpynet.link.nic.NIC): """ Simulates a host at the network layer.\n *environment* is the class which simulates the passing of time provided by - ``simpy``. - *mac_addr* and *ip_addr* are the mac and ip addresses associated to the + ``simpy``. + *mac_addr* and *ip_addr* are the mac and ip addresses associated to the host \n """ - def __init__(self, + def __init__(self, environment, mac_addr, ip_addr - ): + ): + + simpynet.link.nic.NIC.__init__( self , environment , mac_addr) + - SimPyNet.link.nic.NIC.__init__( self , environment , mac_addr) - - self.ip_addr = ip_addr self.host_handler = self._receive_host self.transport_handler = [] - + self.discarded_datagrams = 0 self.sent_datagrams = 0 self.received_datagrams = 0 self.corrupted_datagrams = 0 - - self.default_gateway = SimPyNet.Mac() - + self.default_gateway = simpynet.Mac() + + def add_transport_handler( self, transport_handler ): """ H.add_transport_handler(handler) - add transport handler """ def _new_handler( data , dst_ip ): self._send( data , dst_ip ) - + self.transport_handler.append( transport_handler ) - + return _new_handler - - + + def set_default_gateway( self , mac_gateway ): """ H.set_default_gateway( mac_gateway ) - sets the mac to the default gateway """ self.default_gateway = mac_gateway - - + + def _receive_host( self , datagram ): """ Handles the frame and sends the processed data to the host. """ self.received_datagrams += 1 if (hasattr(datagram, "dst_ip") and hasattr(datagram, "segment")): - if self.ip_addr == datagram.dst_ip or datagram.dst_ip== IP(): # or in broadcast - self.lgr.log( SimPyNet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.ip_addr) + ' : Received from: ' + repr(datagram.src_ip) ) - for h in self.transport_handler: + if self.ip_addr == datagram.dst_ip or datagram.dst_ip== IP(): # or in broadcast + self.lgr.log( simpynet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.ip_addr) + ' : Received from: ' + repr(datagram.src_ip) ) + for h in self.transport_handler: h( datagram.segment ) else: - self.lgr.log( SimPyNet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.ip_addr) + ' : Discarded from: ' + repr(datagram.src_ip) ) + self.lgr.log( simpynet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.ip_addr) + ' : Discarded from: ' + repr(datagram.src_ip) ) self.discarded_datagrams += 1 else: self.corrupted_datagrams += 1 - - - - + + + + def _send( self , message , dst_ip ): """ Handles the data and sends the processed frame to the host. """ - d = SimPyNet.Datagram( self.ip_addr , dst_ip , message ) - self.env.process(SimPyNet.link.nic.NIC._send( self , d , self.default_gateway )) + d = simpynet.Datagram( self.ip_addr , dst_ip , message ) + self.env.process(simpynet.link.nic.NIC._send( self , d , self.default_gateway )) self.sent_datagrams += 1 - self.lgr.log( SimPyNet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.ip_addr) + ' : Sent to: ' + repr(dst_ip) ) - - - + self.lgr.log( simpynet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self.ip_addr) + ' : Sent to: ' + repr(dst_ip) ) + + + def __repr__(self): """ repr( Host ) - Host's unique name identified by its IP address """ return repr(self.ip_addr) - - + + def collector_lostdatagrams( self ): """ collector_lostdatagrams() - returns number of lost datagrams """ - return self.lost_datagrams - - + return self.lost_datagrams + + def collector_sentdatagrams( self ): """ collector_sentdatagrams() - returns number of sent datagrams """ return self.sent_datagrams - - + + def collector_receiveddatagrams( self ): """ collector_receiveddatagrams() - returns number of received datagrams """ return self.received_datagrams - - + + def collector_discarded_datagrams( self ): """ collector_discarded_datagrams() - returns number of discarded datagrams """ - return self.discarded_datagrams - - + return self.discarded_datagrams + + def collector_corrupted_datagrams( self ): """ collector_corrupted_datagrams() - returns number of corrupted datagrams """ - return self.corrupted_datagrams \ No newline at end of file + return self.corrupted_datagrams diff --git a/src/network/ip.py b/simpynet/src/network/ip.py similarity index 88% rename from src/network/ip.py rename to simpynet/src/network/ip.py index df48225..e57db8a 100644 --- a/src/network/ip.py +++ b/simpynet/src/network/ip.py @@ -9,20 +9,19 @@ class IP(): """ def __init__(self, ip="255.255.255.255" ): self.ip = ip - - + + def __eq__(self, i): return self.ip == i.ip - - + + def __str__(self): return self.ip - - + + def __repr__(self): return self.ip @staticmethod def get_broadcast(): return "255.255.255.255" - \ No newline at end of file diff --git a/src/network/router.py b/simpynet/src/network/router.py similarity index 81% rename from src/network/router.py rename to simpynet/src/network/router.py index 274e9bb..8394995 100644 --- a/src/network/router.py +++ b/simpynet/src/network/router.py @@ -2,35 +2,35 @@ """ This module simulates the Router at network layer. """ -import SimPyNet +import simpynet import logging -class Router(SimPyNet.link.switch.Switch): +class Router(simpynet.link.switch.Switch): """ Simulates a Router at the network layer.\n *environment* is the class which simulates the passing of time provided by ``simpy``. *mac* is the MAC Address associated to the Router. - *forwarding* is the functions that forwards the datagrams (receives as + *forwarding* is the functions that forwards the datagrams (receives as parameters the datagram and the Mac Address and returns the destination Mac Address). - Processing delay (*prcss_delay*) is set to 0 unless specified and queue + Processing delay (*prcss_delay*) is set to 0 unless specified and queue size (*queue_size*) is infinite unless specified. - + \n """ - def __init__(self, - environment, + def __init__(self, + environment, mac, forwarding, prcss_delay=lambda x, y: 0.0, queue_size=float('inf') ): - - self.lgr = logging.getLogger(SimPyNet.__LOGNAME__) - + + self.lgr = logging.getLogger(simpynet.__LOGNAME__) + self.env = environment self.mac = mac self.forwarding = forwarding @@ -38,112 +38,111 @@ def __init__(self, self.queue_size = queue_size self.arp_table = {} self.queues = {} - self.handlers = {} - + self.handlers = {} + self.lost_datagrams = 0 self.sent_datagrams = 0 self.received_datagrams = 0 self.processing_time = 0 - - self.switch_resource = SimPyNet.simpy.Resource( self.env , 1 ) + + self.switch_resource = simpynet.simpy.Resource( self.env , 1 ) self.output_buffer = {} self.prg_delay = lambda x,y:0.0 - - + + def _add_to_queue( self , name , data , d_trm_delay ): """ Adds the data to the input-buffer. If full, the datagram is lost """ yield self.env.timeout( d_trm_delay ) - self.lgr.log( SimPyNet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Received from: ' + repr(data.src_mac) ) + self.lgr.log( simpynet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Received from: ' + repr(data.src_mac) ) self.received_datagrams += 1 if (self.queues[name][0] + len( data ) > self.queue_size): - self.lgr.log( SimPyNet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac) ) + self.lgr.log( simpynet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac) ) self.lost_datagrams += 1 else: self.queues[name][0] += len(data) self.env.process( self._forwarding( name, data, d_trm_delay) ) - - + + def _forwarding( self , name , data , d_trm_delay ): """ - Simulates the forwarding process. + Simulates the forwarding process. The data is processed and put to the appropriate output buffer. """ with self.switch_resource.request() as req: yield req temp_time = float(self.env.now) - yield self.env.timeout( self.prcss_delay( data, repr( self ) ) ) + yield self.env.timeout( self.prcss_delay( data, repr( self ) ) ) src_mac = repr(data.src_mac) for mac in self.arp_table: if self.arp_table[mac] == name: del self.arp_table[mac] break self.arp_table[repr(src_mac)] = name # si aggiunge all'arp table il riferimento mac-interface - + self.queues[name][0] -= len(data) - if ( (data.dst_mac == self.mac or data.dst_mac == SimPyNet.Mac()) and data.datagram.ttl>0): + if ( (data.dst_mac == self.mac or data.dst_mac == simpynet.Mac()) and data.datagram.ttl>0): datagram = data.datagram datagram.ttl -= 1 - dst_mac = self.forwarding( datagram , repr( self ) ) - data = SimPyNet.Frame( self.mac , dst_mac , datagram ) + dst_mac = self.forwarding( datagram , repr( self ) ) + data = simpynet.Frame( self.mac , dst_mac , datagram ) if self.arp_table.has_key(repr(dst_mac)): self.env.process ( self._deliver( self.arp_table[repr(dst_mac)] , data , d_trm_delay ) ) else: self.env.process( self._broadcast( name , data , d_trm_delay ) ) self.processing_time += float(self.env.now) - temp_time - - + + def _deliver( self , name , data , d_trm_delay ): """ Simulates the transmission from the output-buffer on the appropriate port. """ if (self.queues[name][1] + len( data ) > self.queue_size): self.lost_datagrams += 1 - self.lgr.log( SimPyNet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac) ) + self.lgr.log( simpynet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + repr(data.src_mac) + " to " + repr(data.dst_mac) ) else: self.queues[name][1] += len(data) with self.output_buffer[name].request() as req: yield req - yield self.env.process(SimPyNet + yield self.env.process(simpynet .physical.hub.Hub ._deliver(self, name, data, d_trm_delay)) - self.lgr.log( SimPyNet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Sent to: ' + repr(data.dst_mac) ) + self.lgr.log( simpynet.__NETWORK_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Sent to: ' + repr(data.dst_mac) ) self.sent_datagrams +=1 self.queues[name][1] -= len(data) - - + + def __repr__(self): """ repr( Router ) - Router's unique name """ return repr( self.mac ) - - + + def collector_lostdatagrams( self ): """ collector_lostdatagrams() - returns number of lost datagrams """ - return self.lost_datagrams - - + return self.lost_datagrams + + def collector_sentdatagrams( self ): """ collector_sentdatagrams() - returns number of sent datagrams """ return self.sent_datagrams - - + + def collector_receiveddatagrams( self ): """ collector_receiveddatagrams() - returns number of received datagrams """ return self.received_datagrams - - + + def collector_processingtime( self ): """ collector_processingtimes() - returns total processing time """ - return self.processing_time - \ No newline at end of file + return self.processing_time diff --git a/src/physical/__init__.py b/simpynet/src/physical/__init__.py similarity index 100% rename from src/physical/__init__.py rename to simpynet/src/physical/__init__.py diff --git a/src/physical/databits.py b/simpynet/src/physical/databits.py similarity index 90% rename from src/physical/databits.py rename to simpynet/src/physical/databits.py index a8e6efb..e9a249d 100644 --- a/src/physical/databits.py +++ b/simpynet/src/physical/databits.py @@ -7,16 +7,16 @@ class Databits(): """ Most basic type of data """ - + def __init__( self , size ): self.size=size self.has_error=False - - + + def __len__( self ): return self.size - - + + def set_error(self, boolean): self.has_error=boolean return self diff --git a/src/physical/hub.py b/simpynet/src/physical/hub.py similarity index 83% rename from src/physical/hub.py rename to simpynet/src/physical/hub.py index 6c71d83..a80975c 100644 --- a/src/physical/hub.py +++ b/simpynet/src/physical/hub.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ -This module contain +This module contain """ import logging -import SimPyNet +import simpynet class Hub(): """ Simulates a Hub at the physical layer.\n @@ -13,73 +13,73 @@ class Hub(): parameters : source, target, data. Is set to 0 unless specified. . """ - + unique_number = 0 # for a unique name of hubs. - - def __init__(self, + + def __init__(self, environment, prg_delay = lambda data , hub: 0.0 ): self.unique_number = self.__class__.unique_number self.__class__.unique_number = self.__class__.unique_number + 1 - - self.lgr = logging.getLogger(SimPyNet.__LOGNAME__) - + + self.lgr = logging.getLogger(simpynet.__LOGNAME__) + self.prg_delay=prg_delay self.total_sent_data=0 - - self.handlers={} + + self.handlers={} self.env = environment - - - + + + def add_handler(self, name_interface , handler): """ H.add_handler( name_interface , handler) - add handler to hub """ self.handlers[ name_interface] = handler return self.get_handler( ) - - + + def get_handler( self ): """ H.get_handler() - get hub's handler """ - def _new_handler(sender_interface, data, trm_delay = 0.0): + def _new_handler(sender_interface, data, trm_delay = 0.0): return self.env.process(self._propagate(sender_interface, data, trm_delay )) - + return _new_handler - - + + def _propagate( self , sender_interface , data , d_trm_delay ): """ Handler """ - self.lgr.log( SimPyNet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Begin transmission from: ' + str(sender_interface ) ) + self.lgr.log( simpynet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Begin transmission from: ' + str(sender_interface ) ) self.total_sent_data += len( data ) self.env.process( self._broadcast( sender_interface , data , d_trm_delay ) ) - yield self.env.timeout( d_trm_delay ) - + yield self.env.timeout( d_trm_delay ) + def _broadcast( self , sender_interface , data , d_trm_delay ): """ Trasmits data from the interface to all the other handlers. """ - + yield self.env.timeout(self.prg_delay( data , repr( self ) ) ) for trg in self.handlers: if trg != sender_interface: self.env.process( self._deliver( trg , data, d_trm_delay ) ) - - + + def _deliver( self , trg_interface , data , d_trm_delay ): """ A single trasmission of a data to a destination handler. """ - + yield self.handlers[ trg_interface ]( repr(self) , data, d_trm_delay ) - self.lgr.log( SimPyNet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : End transmission to: ' + str(trg_interface ) ) - + self.lgr.log( simpynet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : End transmission to: ' + str(trg_interface ) ) + def remove_handler( self , name_interface ): """ L.remove_handler( name_interface ) - remove the handler associated with @@ -89,19 +89,17 @@ def remove_handler( self , name_interface ): del self.handlers[name_interface] else: raise Exception("Handler not found") - - + + def __repr__(self): """ repr( Hub ) - hub's unique name """ return "Hub"+str(self.unique_number) - - + + def collector_sentdatabits( self ): """ collector_sentdatabits() - returns total data sent """ return self.total_sent_data - - \ No newline at end of file diff --git a/src/physical/link_agent.py b/simpynet/src/physical/link_agent.py similarity index 78% rename from src/physical/link_agent.py rename to simpynet/src/physical/link_agent.py index af9715d..3b1006b 100644 --- a/src/physical/link_agent.py +++ b/simpynet/src/physical/link_agent.py @@ -4,49 +4,49 @@ import random import logging -import SimPyNet - +import simpynet + class Link(): """ Simulates a shared link at the physical layer.\n *environment* is the class which simulates the passing of time provided by ``simpy.Environment``. \n *trm_delay* is the function that simulates trasmission delay of link. \n - *prg_delay* is the funcion that simulates propagation delay of link. It has three + *prg_delay* is the funcion that simulates propagation delay of link. It has three arguments: source, target, data.\n - *loss_prob* is the function that simulates loss probability of packets. + *loss_prob* is the function that simulates loss probability of packets. It has two arguments: source, destination. \n - *f_error* is the function that simulates an error in data. + *f_error* is the function that simulates an error in data. It has three arguments: source, target, data, return an corrupt data.\n The module ``logging`` is used to creates a log file of esecution, saved in the working directory. """ unique_number = 0 - - def __init__(self, + + def __init__(self, environment, - trm_delay , + trm_delay , prg_delay = lambda src , trg , link: 0.0, - loss_prob = lambda src , trg , link: 0.0 , - f_error = lambda src , trg , data , link: data , + loss_prob = lambda src , trg , link: 0.0 , + f_error = lambda src , trg , data , link: data , ): self.unique_number = self.__class__.unique_number self.__class__.unique_number = self.__class__.unique_number + 1 - - self.lgr = logging.getLogger(SimPyNet.__LOGNAME__) + + self.lgr = logging.getLogger(simpynet.__LOGNAME__) self.trm_delay=trm_delay self.prg_delay=prg_delay self.loss_prob=loss_prob self.f_error=f_error - + self.lost_data=0 self.total_sent_data = 0 - - self.handlers={} + + self.handlers={} self.env = environment - + def add_handler( self, name_interface , handler ): """ L.add_handler( handler ) - add handler to link \n @@ -54,71 +54,71 @@ def add_handler( self, name_interface , handler ): """ self.handlers[name_interface] = handler return self.get_handler( ) - - + + def get_handler( self ): """ L.get_handler() - get link's handler """ - def _new_handler( sender_interface, data , trm_delay = 0.0 ): + def _new_handler( sender_interface, data , trm_delay = 0.0 ): return self.env.process( self._do_send ( sender_interface , data , trm_delay ) ) - + return _new_handler - - def _do_send( self , src_interface , data , d_trm_delay ): + + def _do_send( self , src_interface , data , d_trm_delay ): """ - Link's handler. - Transmits data from a sender interface to all the other handlers + Link's handler. + Transmits data from a sender interface to all the other handlers (except the sender). """ - self.lgr.log( SimPyNet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Begin transmission from: ' + src_interface ) - if d_trm_delay > self.trm_delay( data , repr( self ) ): + self.lgr.log( simpynet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Begin transmission from: ' + src_interface ) + if d_trm_delay > self.trm_delay( data , repr( self ) ): trm_time = d_trm_delay else: trm_time = self.trm_delay( data , repr( self ) ) self._broadcast( src_interface , data , trm_time ) yield self.env.timeout( trm_time ) self.total_sent_data += len( data ) - + def _broadcast( self , src_interface , data , trm_time ): """ Trasmits a data from a interface of the link, to all handler (without itself). """ for trg_interface in self.handlers.keys(): - if trg_interface != src_interface: - self.env.process( self._deliver( src_interface , - trg_interface , + if trg_interface != src_interface: + self.env.process( self._deliver( src_interface , + trg_interface , data , trm_time ) ) - - - + + + def _deliver( self , src_interface , trg_interface , data , trm_time ): """ A single trasmission of data from a source to a destination handler. - + """ prg_delay = self.prg_delay( src_interface , trg_interface , repr( self )) if prg_delay > 0: yield self.env.timeout( prg_delay ) random_value = random.random() if random_value <= self.loss_prob( src_interface , trg_interface , repr( self ) ): - self.lgr.log( SimPyNet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + src_interface + " to " + trg_interface ) + self.lgr.log( simpynet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : Lost from: ' + src_interface + " to " + trg_interface ) self.lost_data+=1 else: yield self.handlers[trg_interface] ( repr( self ) ,self.f_error( src_interface , trg_interface , data , repr( self ) ) , trm_time ) - - self.lgr.log( SimPyNet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : End transmission to: ' + trg_interface) - + + self.lgr.log( simpynet.__PHYSICAL_LOG_N__ , " " + str(float(self.env.now)) + " " + repr(self) + ' : End transmission to: ' + trg_interface) + def remove_handler( self , name_interface ): """ - L.remove_handler( name_interface ) - remove handler associates with + L.remove_handler( name_interface ) - remove handler associates with name_interface of link. """ - + if self.handlers.has_key( name_interface ): del self.handlers[name_interface] else: @@ -130,25 +130,25 @@ def __repr__(self): repr(L) - unique name of link. """ return "Link"+str(self.unique_number) - - + + def collector_lostdatabits( self ): """ collector_lostdatabits() - returns total lost data """ return self.lost_data - - + + def collector_sentdatabits( self ): """ collector_sentdatabits() - returns total sent data """ - return self.total_sent_data + return self.total_sent_data + + + - - - class PointToPoint(Link): """ Simulates a point to point link at the physical layer.\n @@ -159,9 +159,9 @@ class PointToPoint(Link): The variable *deterministic* defines if the delay is fixed or unpredictable . """ - + unique_number = 0 - + def add_handler(self, name_interface, handler): """ L.add_handler(name_interface, handler) - add handler to link associate @@ -171,12 +171,10 @@ def add_handler(self, name_interface, handler): raise Exception("There are already 2 nodes connected.") else: return Link.add_handler(self,name_interface,handler) - - + + def __repr__(self): """ repr(L) - unique name of link. """ return "PointToPoint"+str(self.unique_number) - - diff --git a/src/transport/__init__.py b/simpynet/src/transport/__init__.py similarity index 100% rename from src/transport/__init__.py rename to simpynet/src/transport/__init__.py diff --git a/src/transport/tcp_packet.py b/simpynet/src/transport/tcp_packet.py similarity index 94% rename from src/transport/tcp_packet.py rename to simpynet/src/transport/tcp_packet.py index c4252ed..bd82c53 100644 --- a/src/transport/tcp_packet.py +++ b/simpynet/src/transport/tcp_packet.py @@ -6,7 +6,7 @@ """ class TCP_Packet(): - + def __init__( self , src_port , dst_port , message , seq_number , ack_number , windows_size , ack=0 ): self.src_port=src_port self.dst_port=dst_port @@ -16,14 +16,14 @@ def __init__( self , src_port , dst_port , message , seq_number , ack_number , w self.windows_size=windows_size self.ack=ack self.size=len(message)+160 - + def __str__(self): s='Source: '+repr(self.src_port)+' Destination: '+repr(self.dst_port) return s - + def __len__(self): return self.size - + def __repr__(self): s='Source: '+repr(self.src_port)+' Destination: '+repr(self.dst_port) - return s \ No newline at end of file + return s diff --git a/src/transport/tcp_protocol.py b/simpynet/src/transport/tcp_protocol.py similarity index 89% rename from src/transport/tcp_protocol.py rename to simpynet/src/transport/tcp_protocol.py index 71eb2a2..9d91ebd 100644 --- a/src/transport/tcp_protocol.py +++ b/simpynet/src/transport/tcp_protocol.py @@ -8,9 +8,9 @@ import tcp_packet class TCP_Protocol(): - def __init__(self, + def __init__(self, environment - ): + ): self.application_handlers = {} self.dst = {} self.host_handler = None @@ -19,23 +19,23 @@ def __init__(self, self.corrupted_packets = 0 self.data_queue={} self.MSS = 11680 - - + + def add_handler(self, name , handler): """ N.add_handler(handler) - add handler to NIC """ self.host_handler=handler # dimensione della coda return self.get_handler() - - + + def get_handler( self ): """ N.get_handler() - get NIC's handler """ return self._receive - - + + def add_application_handler( self, app_handler , src_port ): """ N.add_host_handler(handler) - add host handler to NIC @@ -49,16 +49,16 @@ def _new_handler( data , src_port ): self.application_handlers[src_port] = ( app_handler ) except Exception as e: print e - + return _new_handler - - + + def establish_connection( self , src_port , dst_port , ip_addr ): self.dst[src_port] = (dst_port , ip_addr) self.cwindow_seq_ack[src_port] = self.MSS , 0 , 0 #syn ecc. - - + + def _receive( self , packet ): """ Handles the frame and sends the processed data to the host. @@ -68,18 +68,18 @@ def _receive( self , packet ): self.application_handlers[packet.port]( packet.message ) else: self.corrupted_packets += 1 - - + + def _send( self , data , src_port ): """ Handles the data and sends the processed frame to the host. """ self.data_queue[src_port].append(data) - - - - - + + + + + p = tcp_packet.UDP_Packet( src_port , dst_port , data ) self.host_handler( self , p , ip_addr ) - self.sent_packets += 1 \ No newline at end of file + self.sent_packets += 1 diff --git a/src/transport/udp_packet.py b/simpynet/src/transport/udp_packet.py similarity index 94% rename from src/transport/udp_packet.py rename to simpynet/src/transport/udp_packet.py index 875ec35..45df03d 100644 --- a/src/transport/udp_packet.py +++ b/simpynet/src/transport/udp_packet.py @@ -13,14 +13,14 @@ def __init__( self , src_port , dst_port , message ): self.message=message self.size=len(message)+64 self.length = len(message) - + def __str__(self): s='Source: '+repr(self.src_port)+' Destination: '+repr(self.dst_port) return s - + def __len__(self): return self.size - + def __repr__(self): s='Source: '+str(self.src_port)+' Destination: '+str(self.dst_port) - return s \ No newline at end of file + return s diff --git a/src/transport/udp_protocol.py b/simpynet/src/transport/udp_protocol.py similarity index 80% rename from src/transport/udp_protocol.py rename to simpynet/src/transport/udp_protocol.py index 406542d..5e74d02 100644 --- a/src/transport/udp_protocol.py +++ b/simpynet/src/transport/udp_protocol.py @@ -4,7 +4,7 @@ """ import udp_packet -import SimPyNet +import simpynet import logging class UDP_Protocol(): @@ -13,39 +13,39 @@ class UDP_Protocol(): *environment* is the class which simulates the passing of time provided by ``simpy``. """ - def __init__(self , environment ): + def __init__(self , environment ): self.application_handlers = {} self.host_handler = None self.sent_packets = 0 self.received_packets = 0 self.corrupted_packets = 0 - self.env = environment - - self.lgr = logging.getLogger(SimPyNet.__LOGNAME__) - - + self.env = environment + + self.lgr = logging.getLogger(simpynet.__LOGNAME__) + + def add_handler( self , handler ): """ - UDP.add_handler(handler) + UDP.add_handler(handler) """ - self.host_handler=handler + self.host_handler=handler return self.get_handler() - - + + def get_handler( self ): """ UDP.get_handler() - get UDP_Protocol's handler """ return self._receive - - + + def add_application_handler( self, app_handler , port ): """ UDP.add_application_handler(handler) - add application handler to UDP_Protocol """ def _new_handler( data , src_port , dst_port , ip_addr ): self._send( data , src_port , dst_port , ip_addr) - + try: if self.application_handlers.has_key(port): raise Exception("Port already assigned to another process") @@ -53,23 +53,23 @@ def _new_handler( data , src_port , dst_port , ip_addr ): self.application_handlers[port] = ( app_handler ) except Exception as e: print e - + return _new_handler - - + + def _receive( self , packet ): """ Handles the frame and sends the processed data to the application. """ self.received_packets += 1 if (hasattr(packet, "dst_port") and hasattr(packet, "message")): - self.lgr.log( SimPyNet.__TRANSPORT_LOG_N__, " " + str(float(self.env.now)) + " Receive, Source port : " + str(packet.src_port )+ ' : Destinantion port : ' +str( packet.dst_port)) + self.lgr.log( simpynet.__TRANSPORT_LOG_N__, " " + str(float(self.env.now)) + " Receive, Source port : " + str(packet.src_port )+ ' : Destinantion port : ' +str( packet.dst_port)) self.application_handlers[packet.dst_port]( packet.message ) else: - self.lgr.log( SimPyNet.__TRANSPORT_LOG_N__, " " + str(float(self.env.now)) + " Discarded From : " + str(packet.src_port) + ' To : ' + str(packet.dst_port)) + self.lgr.log( simpynet.__TRANSPORT_LOG_N__, " " + str(float(self.env.now)) + " Discarded From : " + str(packet.src_port) + ' To : ' + str(packet.dst_port)) self.corrupted_packets += 1 - - + + def _send( self , message , src_port , dst_port , ip_addr ): """ Handles the data and sends the processed frame to the host. @@ -77,5 +77,5 @@ def _send( self , message , src_port , dst_port , ip_addr ): p = udp_packet.UDP_Packet( src_port , dst_port , message ) self.host_handler( p , ip_addr ) - self.lgr.log( SimPyNet.__TRANSPORT_LOG_N__, " " + str(float(self.env.now)) + " Send, Source port : " + str(p.src_port) + ': Destination port : ' +str( p.dst_port)+ ' To : '+str(ip_addr) ) - self.sent_packets += 1 \ No newline at end of file + self.lgr.log( simpynet.__TRANSPORT_LOG_N__, " " + str(float(self.env.now)) + " Send, Source port : " + str(p.src_port) + ': Destination port : ' +str( p.dst_port)+ ' To : '+str(ip_addr) ) + self.sent_packets += 1 diff --git a/src/utl.py b/simpynet/src/utl.py similarity index 100% rename from src/utl.py rename to simpynet/src/utl.py diff --git a/test/TestLink.py b/simpynet/test/TestLink.py similarity index 91% rename from test/TestLink.py rename to simpynet/test/TestLink.py index fef102b..215ffb1 100644 --- a/test/TestLink.py +++ b/simpynet/test/TestLink.py @@ -6,20 +6,21 @@ """ import simpy + import link import physical def f(Obj,data): - + print ('%f %s ha finito di elaborare %s')%(Obj.env.now, Obj.name,data.name) - + def start(Obj,data): - + print ('%f %s ha inviato il dato %s sul link %s')%(Obj.env.now, Obj.name,data.name,Obj.next_connection.name) - + def R18(): - + size_packet=8 # in bits num_packets=5 list_data=[] @@ -33,14 +34,14 @@ def R18(): for i in range(num_packets): list_data.append(physical.Databits(i,size_packet)) - + env=simpy.Environment() - + A=link.Host(env,'mac_a') B=link.Host(env,'mac_b') - + rA=link.Router(env,0.1,3,'mac_rA') - + l1=link.Link(env,'l1',rate_link,delay_prop_link) A.addHandler(l1.getHandler) @@ -49,12 +50,12 @@ def R18(): l1.addHandler(A.getHandler) l1.addHandler(B.getHandler) rA.addHandler(l1.getHandler) - - + + A.sendData(list_data, 'mac_rA') - B.sendData(list_data, 'mac_rA') - + B.sendData(list_data, 'mac_rA') + env.run(until=1000) - -R18() \ No newline at end of file + +R18() diff --git a/test/TestPhysical.py b/simpynet/test/TestPhysical.py similarity index 100% rename from test/TestPhysical.py rename to simpynet/test/TestPhysical.py diff --git a/src/__init__.pyc b/src/__init__.pyc deleted file mode 100644 index 51d9359ad35ed22d3f4903f12b9b63083e2cd7e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmZSn%**wLKQTC&0SXv_v;zt`0yj; diff --git a/src/application/__init__.pyc b/src/application/__init__.pyc deleted file mode 100644 index 6a0b1908731985feaed5198a0d02ecd4115e20f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmZ9F|4PF!5XaLWPN@h!Lco8De~5Dhv4~7MQ2b+zwt|#ED9PahZBvqTu$S-w_ZmKc zUblhZK|Yu7-F@$Z?~7=6^;HOR3?K6xpS?y%LMo(3q)!@;v_7eP;*B~WO+ea^R3Yk+ z6!FRENf1$Hpmrds?r1pxeH%JTXLNd>CP_Mbpm(>^B#P$s;MUrtR4S<>^@9<6=yvCb z#cQ_WqEXPfe?<#s17^9u=1y^xbEyUAdz)N~{%Fgwb=b8p9&Gt|ARaq^upcP1l)dAK zWzWVz%U-npY=AC|+5jt+(L!}f(;S>)C0NC5-?2iqtL+jTvqrGG?;9m*r*y|S$7#p; YO}}+#xcsrza%kXbf`1z#CjO=O3qLSij{pDw diff --git a/src/application/dhcp_client.pyc b/src/application/dhcp_client.pyc deleted file mode 100644 index 1c36190965b195ff979fce5069c64cdd71f7b8c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5740 zcmdT|?Q$DQ744B^#ggNEFD5K3%@zgPBG_@h0OO+8YkOJm2HD%nZWV^AW_D(5jaHsf z)MKXt2|oxAz#~8vkHQ=A0PH!pM7HrcK12w-f{iki|v!Y|E(9P z@>9p}hq&DDP{c~@qYPABQ^P>**CzG4+OMk^e=QX^)L)f)q112p8!Dbtaa+aB>K57; zRJ@?#`RZ1a)_R?boa7#g_U1SVizL>0rXL+A`qSe~>$`gO!P=b%YpbjJ&h6EE?e>S^ zanaAmYdTKjyq*3BpKh^$$G1M&+_TXzO|qg|M1cDr(f)lD2^N7BC_3O*zzTe?$sIUd zmkL~!rR+DA>2>DtWW>#A;NtHlF82=<&jR&KDT_)?DzNSE)YKtvq6@ZtWlj}MHMqb> zUidNd954vfpe2=gHE471(EGeTxwD|2)p;l2P>i@J9g9-d)C)iBB~@HjgDWz@)rs|M zoVeGi!OLz+hdb9%6m&nyP6{ripSwc#&2O0<<>R8bgpOhA3Y(6sOU5V3SjKkqEGe#_ zFY4#HOJ;g$(T%c#hxolU_h$m>(Xd*S* zjnk7bOT(33dbIbnn-nW<9Iec_zCtfccrK5gioe#TSz1_o8OGcV8n3m2@>{zWyc=8z z-m69QgCK0;;!(OexCbB&lzI*b2o-MYij&YK?p|p?z*};0g;E6fbe-kMxn&0 z2GJxin^S|P!Uh6wU;)-&v}N1^;RiGLTLQV4(8H(LYWfx&xgaww(N+c@;n2LILqR|{ z(MupNDtk#xzR^^z*BFPo^g4u)`wNP;El(;Olas_j zX%d}}^-(@fH1%Wm)LRN2KDa20boZE zPCD;7k_P^|W&bPVO3#QjC$4C-{rMN$y+_p=id?Tb(pW!9i$2!y_&9Ne-r3V@8tMYw z-PzdMGLB3TlN-_3?!Et>A{-8L&aF}M+212=b3yAnhc3v#sL z=VLSq(S7Q>s5C2(@|t=s>OTPMd~KUWd~=jF@s^dR$vOz0KV(x6SrehfH9H4h3a#>Y^MnkElE0N}B1f#(5Y=A<$Lf z8!{AD||o z)T5C`V;7y+BX@X?NGk~SR~^|Z@AGt3^WEV6Kyr~&@!)fG0ZsrC!@w(%M~w&m8fvQg zUjBi<+5~@+YY=}BexLWaf5CJGfy%j({T-xWLPSJn!fGdjC?UpYF3~tO>a84eP6@m< zh0-Y@Mr?-?iK2N#PK522o`GR$&a3W>sZs4S*G&ix3*MhU#;hD+- z%&OZdEEyj4t1x6fK*v<9%nv#4N3=(y&?Iq6bn7t^tMdho`7Y*I$0Y$rHQT{EK|N^I zC21hIc}dd1jLB;b5>`OPCx`A!+^!Co+1J5w7D*8Esa}U|37_b6T2H#M-C?2q7+neP z^7N2(w$3SVnh`ts;m&=8%iTheyzy%b z-d4E3q7c+LUUK;NP)Q{sRc|ZE9)W`OG7OP%PS?4~3azWRye z8AO!ud#j8ghI;Hz{X492dM}w=D|Of>ITEbPw~ll)-QlxuE>H0hF3EwIOfTYF1?Xx! z_)dbwmus!5n@bGKX*RT zPSLqidZZ0K+4rt6&A-$nnUPZv7T&h*QB3zyYG+3+sm~ZLsD&iVw;HXwA@%8+e9kBcq%p6G z0bMlSKoF2Klq9J`DtyLJs>9r*r&&q`CAMqsqBN8niMXfUdu}`Go+yU#?=CMhi!M$D zqf`j!hIzmaiK$c)gqt$%Pmkj4r@)gQ;u0^R9W`plTh)*AMztYMpKu8iG>i1Tv2K`O z;Mun)8p5DI(LD*u|37LWtpD@uzWy&%f_ebTRw}VTm6#I1*CfwjVQrj8NOR^}vjVD=%q#PnMFIhP=mt@v;pIiNk37|yOCSI2>)~|10|MoX6%DC?U diff --git a/src/application/dhcp_server.pyc b/src/application/dhcp_server.pyc deleted file mode 100644 index 31b42d773304a9d36366e70a1063f14f3cfd8695..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5822 zcmds5U2h{-89p<1oY--;yIo)j{m=*sbqkbkcNd`$+NRES8@f2@B)Mp6aj<44&Uoz^ zcg{&xL5YO4h>J={h)XUIe}W%Y@dMy_-ZNuwwjgaU5)gLg_?-8Ayx-4xC$)d9wEp_H zKkY`U{M7LKJ|6QKnpmj=w4RE6HTKkjU-fJ1pr&H{HC0?!zgOysQlB2wRlK0$mWmrQ z62^-vURLqajMU(1yPY_B#3 zC=j|P9f&Hb95ht8+gZTQh{j-tzg;}$FKEs@b*7X?$CnPyb`)RPni|!);EAK)NG!wR zMYbbPjh3Xdphiua49m`I6{V%lYC^#YSd2Bx(rHWUt0#`#in7<#=mp_*y<&8OjCMOd z#OtM-Xge>WfjCeodYTtjCe1Xnbj1GVj@~J9n@9OrCXVB&?CRxN zV!weE(NUh8M5j}Im=`)4r%7h*H!(4YV{MB;W_YMR8f5V}Da13wkstNB~y&cSp# zPNRWM^K3SE9do*fPD-!p_$Zp%B@j)L#hQ{)jd4C4rrFRo(Fdo87?ymsez^UhxACj{ zT8l2@VIl4mg-kL&3>hCGjU~I~*qM3A7(6(C~Pw6KgjdpI0-h3 z^d#x;r9~d4S(*o7V$=R!V$wbg+uuzmJEy(G_DvD>pHj3>g-Ye#nx2YkTH^q#^)+xc z^qumX9=|VoP5&jdulf-Mt{#cT6%c08h#+(UV15E}D=0rS`r&aFiG`gLJ2DR52=r`K zuOlX*ktK-2o9J1FzO21MW03K`k@;ni4V71Qu|oqrK4OP1gohnQN}VG|1f*7(SCZq8 zd{ulQDTK7d3YT57?ySzF@|bOsxKMZmbgC%?7`#GyQ;k|oGW^{FJlw(z87!z(p)?!? z=U|>^Wq%j8i;8UD^SE|Nt)4IN$g>45V+RK>t5KW7M>SP^DU|0Dq3PIsj-zqN3WdQT z&#yx3SrtTRVEl3g$$y4bXL0dt8g_z7V$5Keyb=63aG)C$2~t0cO^{{)=flIK05k-T z2d5?gRLqW=PZdd&AaCO8EUYW?N#HQ$t=R?!@N6K7nolK)@7*zsd;)*4nVKjE^czAQ zHkco|UzscgWPY1KDBB-?ct5Q81~w1YtVGe)#^z>dZgB78)E)&}J3&rOg0;X9A7yix ze14n&M7iTrx8wU?(B)|(AYrV0v^Kh%XvA4C|BM$0biGs!!E}_TyzzPBiIA-}{ zhlIpbpgVG}ur%+SAB5{rX9o&$g}5LDjrXb(wMErH~p)IK;`};CO8H5Tz4xEo9t;H8v6pgvza*x!b}YaEj))Ei{usRNdjc zKjEM9M(0TUrYB~2h2DAvd>`O3y!@5l@>*WotKr*(w*>L&p_!L{fkEXZc%t+Yj196N zVhQso!z#RoD_CD$bb_c`qRQJ(`GJPC6h-`i1ekeYW@jlWBqf|}dFzT(V`EV!Np1|H zM^|je*C53{9>Yk29bw6~-(jAXWkxobBtQfPf!32eJ|1Hjehb5pi6Ute(iYiAiJa=`EqMoS{Re?kwck!4#G^wOF zi+UF?^47CnW;O=CT4%~5sWGUjb<_goU_q^0gjrSdbH0Ijmg6*gPogS(3El8ba-(K) zHEi>O30E<^aJk)e1=!2n5TKdISy`;|O@{%-gIv~Xq_fLy#&bzaudsxb_@RH~vSjJH z!Y=`q&LSAitj%`@_rgR|`D*pnxtMRa!zDdK6}BZMarugQTy08&JXM?WTr&^P%Z^M* zXRUFGYMqlcLrHlzoPw2-5>C8$X14{3vt5zDC$hWukt7R68*UQ>xpF*9|4}drJ>b;^a3grpay`K<5l|p& zBYO0oGm>T0Y0CO>ZplkGBK`O~JcisI@LU$;TyZRijLtt$wJL^1vFC=GkTueO4QC{+ zzM%9BK=uCuFai{Rq*|3z{s)1D5OPi#iG~1{27zUslddNvAsemZJVHgt#npeTh@oLbn-Q2(V%D({B5FMoe diff --git a/src/environment/__init__.pyc b/src/environment/__init__.pyc deleted file mode 100644 index c7d564e9c46e318c4683c6b7ec5f68d8f3bca66a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmY+A!Ab-%42CluS*7C1o2MS$Y$>S89&rsa(DMcbz*A1cwiXaDNB`5-GY>Q9G+TlZpucUHSp7x|do zCrGJ$C*_wy|0*7-1jT>E6~!2OhgK`o;`bU{E!Rt2>u50U95%*247E-eLx+C`qLjv& ZB<1ODNC!0N%6ipr_4S1QalFWceFC%yNK^m- diff --git a/src/environment/environment.pyc b/src/environment/environment.pyc deleted file mode 100644 index 67dfd9de6091169d503e1adc3eb0d9aa8fb159d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4921 zcmcgw&2k&Z5$;_OAOVpUB}%gC%Fo0V#}LcKic_U3mrSW7OY&dIuxmkE6$Yy1YIguG zxY&hv1{7H#Pt+Ua8S(&mg1kVEd4n8s$T5{mF3H!uKL91?7=ql!Oz-^kbobZYgT_DC zI)6U-V?U(w)4=xwJmx+~M6?I$P~=kL&>k8^+oe61q6TS~#tj-T(GdJ4dPQ_j^xM59 zp3v_$d7a;bbaoG7qb6B2OSCfaWR?V08)Xl)(&qbZCn;jSXyUY#f^HPW&HydiDyWcrxFrKJF6u zpR$+(5RK#F9H2Oy9P||q-Qa`bWPB?8H;I`|idw>KQM4?LWr|j$u_D7TvO`guUNva1 zLtekj5|}3-9feQ-f)W-aQ$ZU2uhXa~u)(&>kFgMiE=ct}Lfd=;G9o%9@`*5p8&1{0 zp$g6t*(Qx!WS42YLg$VQYYP)HI~C^&kMDPRgPwT@^!;vNLK{!?ZKK}rPP8$>h}+#& zytEr2wdRAwkJCsWYa{&9JXvo1;Vi`(S!%Cg`~v?dNM;zl1a2=&HDn{g*UKzFly;Jh zMsN+=27jdOQ*?E1SI~nlzdh+`D@OMHr{8?ud-(Me-?!|`$tag8pJn+ZuohEzhhaXk zaxm!`oeV9XAx>i(Lv%i47kv_9euvhhyMynH&duOqZuMmFd6tdf%a8N;NDp@5!6D{m z%F{L;>}nGa%rqTTf(=X_4i+4JvAsP#kwf^k@2-~-c583yDl|jmM&zo^TSRdSb2cP zsY|B~8i5jxMWZ|1a%c>e0KwcT;R7Z(JE=gZ=xp}o;O1{wsa9&C_1dv(e>YuK;X2Fx zG?-}h;hceXoWeVS0>7LKjNJQZ!CDU^u}ZyiZ3tzv*8}Fy@p#y`|2UDm9aUBDJ5wm4$ZD`In&ODOn}V_qWE-iDm) zCAA{v_W1=~8IC8^+HkHr>#m^gg3EB!tvzKEk#h6QB`Yw5!yu5JHRL@g-XSf*xxh7F zl|dP~l3##Z`BBZ-95>k;&yhhLawL7SlFb8<+PC__QJFSN`H>NexT%I&u5=I{%w1}3 zI7t@0ZVO9^`4IgP0+S5!koQQlmts(lBeLaD!E}m5JU6O$9ZZh{yu`~(4v&m%pN&== zbsJsN0iotK=c;qdxlvf^LL!{UN5C2yEs?s9S+zEwY$NvhoU-OwsXGVDJWe92&+^pN zuex(3n^LF+oEE{56*~`x65=ZuUZT6R-QGOpC*YQ`G340D?!ZsT{C|VMvtL!7{utUj zbJ)w_l-zdd_^)JL%Kt^jWARklY2 zO*!-jNZu-)TTov37SDu*h_-(%faPp+d0r>r+==4QhS_9#{|gqMg=sqyZsK$(qDors zbyx7FNGi9`5^;C)nU>5EBO~IO81LQc-BjzM9%R*KGb_mAamYeOZX87#y1^4L1oh9c zt-?K*om^W@;uPgpk#4=8f#I=wFHb}EEo@gtp3<`(fn(kSp=Qfjb5>FNZGx^jH@Qb^ z?mNzs`s#`e86j=ny1Y-CFZYd>xodTWXWx=IX{{qT3jXR7t zAYA%aszn%fc)41ls~Wt<00)L~6!;-#y?yQ$Gq1|Z>kK|w3>!D;V-6v;gx6kM{Cd4J z2Dj*NiSob8Di{dhN|AQnAO}dtZL2RnV}6CuRl`bRIcnV5%dJ^hle0O}LmT_%WheESyN-Bn5uDp>7xIr)2x zRVf~zo#}~(9(jCW@wjM^>(^&-qQAfuHqp7q+X&nsZhigo7=Iq8Z_5N}y&BFQEojE@ zr);X?{+5m8+t^$R&Jr%(2Rym~0%0v@`&>`5qiXMkK)_JDXfYXn!ANo@F@cmn!vi0$ zFE1Q;zk!U;@fZ$sw6X!PucH86bJyLUx*Ls-vw_yC(~}<&%IhM>blh=SiN0U_w1O_* znteaYLfVy18ZBqF(_UF!Yp=Ce o+V2*IyUVgkxyl~rZ85>K5TP$cRMGTKq1*c`j$s0Uoz=#F0sYLo7XSbN diff --git a/src/link/__init__.pyc b/src/link/__init__.pyc deleted file mode 100644 index 5c3678ff62cb5bc2630a45a0eaa974c6917b4c9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 436 zcmYk2Z%e~45XP_V*3E+8N65>)5?AIQipXT5C^)p8Z%QDPHoCB;8A<9G_`>h(2hd9^ z=!JWZ-;sNAB=|avK0i!W!f*Ik=a}XJr2@!NJ*a%BJ&-=+K6s7;r~{A#$OkMN~sp>y}(^HHl|rdYZ%+NqkP?lj$sq=0$7Q zN++akHfq;I;u~Y);k+H9-0DK|9z(=zCv3UyIY53a%0F1%FP4)7H|cg@tWqUooy)6p zNwEpmxtPu+?H+M1vM5vc9o9i3%GA{f=07pdh>L{27_JSy)|N|pr_@``$&D88oUVjc zrD%jADYt^IxDnKB8=49Ev|n;djV@^|nhj+PuUW>9w|hHA*Gs9Yw&qveYOWD_N8R}W DUhiG_ diff --git a/src/link/frame.pyc b/src/link/frame.pyc deleted file mode 100644 index c372706b0d35244782998f8fa6783b0467e5e4e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1575 zcmcgs!EO^V5S`sklO|9Q2NZ;aXUCrBHzQ|14@N)Ue4kaceKLL@^9WDa)INaUjOOPqs8w5EEUl{ACYG)VDxO<)>dTc?WmHSod{)bHWBn*%-CKP4 zE9q)w26hD&TpVaVI2*XHu zOnHQ7Y%UYJBr*(A0X)qFq!bxsbj6es8Y75#5Fa1wbN867C=mzP%U5PqR)Xsg1@E)J zE@OGdN9MhVmbQsK1iP4Jk<%iH8$1=l<29R$~&&@gnc9?z! zSH-)wJ!Ab(y!&E{x$|x18rLdqqbue%xFW2ZV&+!wH@S@kGc8b*LP~VF-bC;;hSx-6 zMvMa-lnmupffKig_{tusaF1Q; z#D?gaGP<@oQW7Ovc)RvDT8PpbC5vDYL;*3okO5^hCPZ({17t&`D0!NQ0?3ac3Lp|^ zM>D{gi`8iF4uSG*kmZIl<{>8ws1S8jZ=+Zmee%CT={x*kw@2US491{SjCsWPPoeS& zK5g5%;cLd22%g4tHxy618rN;b81>ED#_AyheHZ2c2K{++0zo@yH_ch(HVUBF&-%&z f!LT>X@-DSp1!zNw+dUmo?M0{4OSz-Kpr8H%24h*! diff --git a/src/link/mac.pyc b/src/link/mac.pyc deleted file mode 100644 index 511b550e82119885f88c87854fee2bc596a57e31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1546 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 diff --git a/src/network/datagram.pyc b/src/network/datagram.pyc deleted file mode 100644 index 3e2202927670363f6df168fbc9931d3e0f37a248..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1567 zcmcgs!EVz)5FOh|+5`#`2t`6dvIK`55I!PRsA?e+Hw$fYA(xe9Z_83=9eFpZg377f z`31g+pWz31GfrC}IHn}p@yzac=FPlWC%+CxKi_|!m9+gOcphWH|j zuK^yRVH`2HKOhBJ;4Ho^wOxr?`M|1xHfKxYf^=xrHlZn-S^$R7J!_oy_;vfmOgK(w znfg?57RALVc2LZcVvh+{C;TzAtX;coX)0S}Yj@sUxNLedKgs6C+svn|~R>W;6^&I>`_~U_cv*J`0m$P&wXi#N4y- zSM)YQxA?8x^Nh@zbz7Rl)IWfcPK^)N6~Q*{FcmMUy)~=$wsz^Y8p1y?3cu^(nIx@! zXtjRK@wwF$y@;e{&d5t@W>2n`2Y@$ok&5f;GzNJ$@GvA=j}hK83)F%8AB3oTNcS%? z6wk=6i{(~o&HQWq6#5Mr`9ubwJ9ov>XdQS`weG%EFgqTrri9KM<_$)jugq!P6hX1K zI$*)0ryfGwq8Du^cS#|xd~-Y}93+G2$#B>kCV7`kZuBFZh~HfRkjwn0>&lUQS2*V& F{tbYNMAQHP diff --git a/src/network/host.pyc b/src/network/host.pyc deleted file mode 100644 index 38f1368d103642fec894c7078f6ee8f8ad8f4523..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5438 zcmc&&U2`18744Z_t-LGAl5N0H0n%V77E=f?RV3v~rIN@LG9QZ>2`F4kjfa`hYSz0m z>**WW>yvw6ofS^&7Gd^yWO`>pZhV}`uAEd|IfwU zSXG}E?tj2#-i1h%x((^6#8(qf-9`idEj97gw54vhl$W$s>Zx&CjhEE8qxLYor2eAR zL#4jBz0B|J^gEpH8boh*KQnq-BnOjJn{0Y8iEL`L-B0y?VXTg<&Qp6>ly~(cI!epU zUiM#n27M1-wm!toTegkcHnbayIrt}B<{m@}i(vz#Pg`LLtb`S8v}rmUODbtgqf=Q+ zOFPnYP9@9I=&IzLG5@=JAia{ zbmz{dcGFLzIE<2{ysD!-(O0us{Zyv16vkyFn`x@0#^5lpND*f^mqgn_)MPR_pPn@1 zHa1&dTiiW?w@Qsc1YeC?q{9QH9(d}3uO5OV4_bTCX9s=rPH&OreFTStS`3QHzge%H3pn`g1y2RFAwD-5$d zvtekLF<8N3*U%2jG)}YoX~^wbo-7;TKJc5QrM-X-TSmE=73IA55_*%HXE2$RT0 zqcWNrdmjA;M|-U6BD$(2j%#v;xG2km8DKxAPTN00Wd&vEc|ua~K`uCY zlTP;R3Z9O!Np@Kuf52V#aXDdbp|y2=_^C-tGyG#|)9LWNq8Py?Z47aE{&$hbWs@L_qp8WYS4~yHW@8${~ z{-z{gcXcs9uanNYQP9;u zGl3XW8$#^O6A*57@QbVah-~B-V@nl&me34wo{5+w(DSSCQPIiye~U?)0om8gVz-q&J5HNq^12qfy zkOmQceN|rZs>fFK2xSCnC?Hnf^_BUgYJ=1d6?@8WIiXAG2$Ew2>9(?6WqZ=UqQGqexem;JUNq=TU;kM&*42$KFVWTE+FX7^{QTZO)#vxFc|U!{&M7Nm zw)Xf!1;_=JuhW3`_+r(A`!f8S7@ChD8WwCztaqf@LmW>fMbhtGAF^)DOqpnUf6XZg zgUuY7xYkpcRx*r9M@;dCo{0Emc*gp=MtGHEljyy&m|oWsh1h?iPA1jBpwDjvbPJhp zKQfWErNs4ss|2`|WVp$wboXKukS>z4!f^2J?&lu|AA}!%{9ZUf-lHYPmX(96{F*{jyg>0H#Y+^7|5Cn4@{wt$@Th2%3YmpF$)dSKi$B0+xLMVH+I!7g z_hQnP*CVM2$I7FWaOWxl3H^PL)UQ7rci%B^|AVlRmE5q%GQ5QyLfR4ug zxR^}R*cN3t0RnX-Fny-sGPMU~ZghSy{VFZ_216Z*pi(P0lejlMMiwf}OW_lx)dJCA zeRRfFzQoY~4G!G)2f?AvS>T9={Zeo|Vmu1u9{~rM)g1F2;QDRohxk(Koe2vg+T(-e zS6Jc#Robtwe20o4EnfOR8 z^VlNpqxdw|&G9*!fjN5QZnoZrUy_pb8;zK*}*(HWE@V2qC^ME2Pyb;lf@%O|bZU8{XI>#VnpT|UWPDb;D` z#v>nN`(*Yfx(r{7+qV!J(GdzV4h#T@Xr4%Lkj#NJ2+EMuFeoYMJyNOYmsq(^WtH!V zw+9fT3=KS+*kS1I!SM9tWPGJJ!m#acWcLxoMMM`w98yx{5oV@Ah69*&E*tznR_o=b zQtUZ;FI)kwSlE>=lu@>*n$CQyTT>U6S;xcB3+q+Wo9O zf9!0nn%enCnCP<<=LgU?h1S+9&FrPpnERKEVV_-P8|E^Bsgx&Q%Jr9G@`n)4*WNTM z?H5*+l7by^&~AcV!VO6nClS&ljSdF~@+H&Wd#L9-u!WWXAYw?v^Zwv3!aJ4_Y5W_P C_5OAM diff --git a/src/network/router.pyc b/src/network/router.pyc deleted file mode 100644 index 8b6a408dcfa63585c3cde056e136aba5921d9784..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6461 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 diff --git a/src/physical/databits.pyc b/src/physical/databits.pyc deleted file mode 100644 index f39b72183eb265d77da38a679795cd9d7c311127..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1086 zcmcIiv2NQi5Isq@TPp~VA?Q*RP&aicfy_k^CqcIwh3utX2wGxbwlZaiGy(_Nl5gt| z^#}TnjFX|r<_h5Hc;wxC_m0`05h^++ zMaej+8E(HqQ*RJJM?YV>;NwUuc$|80f{ZF+vOX>5liBBBc*lW|(QdayXD$b}l vT+6+j=z3ot{n*dl)(lpzr(b%YY^IOTgxr%SI6pso^5j4->Cfwl$t+kl)K3;=>hr1uE8Gmn3rp^f#OBE) zkv(~2U%$xxR0z+LI+$u#jX$fZ32f$WojMamwBaQ>+|s=>+-Rimt#Qn@N8)P*wyKH1<)9@ z59VjS2cox%WOke-A73FCG?mim*mzC3_%Q}Al{&>Y;=o8BrVwZ^>|xcn)M;BmjMEME zDje;qmo0e)HWvmx1^tjpU%d=xU~>v;Ab%_E8+Zg#iI%C9MjPbjeEz11a!kTh1 zJGQmo#7a?3Ca@Vf3%KrMiX95KlHQY+G4~&QHoX1iJrE+VVp7W^jRE;fSoM`p3gcie zhNugfX;Nkd6fcj#p5>~7mb1l)5I<+-F`2y$=!^m2-k9%$pL++LYF~=pYh7veqdr;# zB!v|q_b>=>06ZxF7Lkh>mqr8X-911jhD(4EM*_)67B(p@Q{-vwR&GKcYdyK zXdGNk=^ikzhKg8rOyERMz^5U}d&Q!2UjB9Z0_=)6c7sAM#tn$qIZ>}+D$c4pyHQHL z&=YuBHVN9W`oAl6B60q2k@^;POvjde=7dK2!-MkzQ&pH#KRI@<0y6mFPY#^{P!@3jhw~3Nh6w3Piw55vKeW7R)O8*Eh*1PI-li=XH$3Tf-g5xr4Vb-D5aQ3RDB5ZU{ z`wCDI(@6cU#Q+JYL1bG7I}$H}JOCHW`3$#rWhqjRBc%S(fLCW-qfF3#82((q;#_!`4_44q^@Q+^>9E_ifKL}WLA^7#|f2V%)`U4N1xxheV>8V z43}Zm`5NNGJBljt!4%1^tX@dOtnIvxiFY7q%a~PAu+ZbWO0qO@UW}Ldzgl>aCt&{r zoD3#Pm(9%DNHM*@2oW=I(ytwbSDnMwmFQ|D_CQG+dtlxoCiqHbqnoxUC>8-JU`GEq z`sNREs-y1y`8VoYSb-SA)3&N_tR7$454X-E{_KXr_fIgUP)4{f!Lq^iQJnxl6gtdY z0-;n}>!6L;oD_vDR;ae>RSa^WaADLR4wzB}xA+=IRCTP`F^C7Ws0M}yR37z!DtH5* z_(QA-z`mXYZ^gpJR59^SLHQF>7jQPNMejyC0i4&I6{sw=3$J_M3qXLQQSW1Mr*jny zUAfCRN=SPO&PfbcuDp7F4`t0B&Yg4RwHJN?o((#;QRE4!`>mmA+?IX-x4l1FL^Qe^?R zH`0P&deK;Zgq!>kEdZ@8_A{dE}5t0YNp8TRN z@e0g$jNtWr(P$nGvNAcoJ*k;}|NoUOc9uFAh@~=q7$cT$Lc%uT{}#yn)QK@!l^PRE zLZEHLe5sT|N;0TK>X$bX`OYii1{jL5s{(5Me j;U3HyF{4aO+YeC_gST=^F~VCxs~__s;(uKvw4La`OIb(k diff --git a/src/physical/link_agent.pyc b/src/physical/link_agent.pyc deleted file mode 100644 index 4fd5618eec6af2e517793f38b0fb2b678d8878cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7974 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 diff --git a/src/transport/__init__.pyc b/src/transport/__init__.pyc deleted file mode 100644 index d0f117269050e6eb84ef806f29d86553b0abc02a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 354 zcmYL@&uWA)5XQ$p-4V1HAba&}M zzR7&UFJHXVL%>fzMFW>|v9IyyCln4)p*p}_=p5)>s9bR7*@Mo5-iOM^*@q&$nVDCV z;ByqU5u7MV%7Z1_dLl_e;v|iuGG?x+la(1Qg>I=HBrV!LpLW7fJvQ{3KpAUgYZ!YTCUb|cy!VRRPJFEJu6>l1`{?`u DFYHs@ diff --git a/src/transport/udp_packet.pyc b/src/transport/udp_packet.pyc deleted file mode 100644 index 1dfcb2ca62c91b401e604a6652b898bbce068c68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1642 zcmcgsO^?$s5S=95E-fF@3Q)Nq`M{y4J#b@%&@Q_Y7gSYgSAr^%i4xNq+N6qIq@v}t z-1)Km0p4@U_QI8>^6Sia9KXC7JNf;v|LOg&Ng=x@!T&jidk!c?X23|wj+C+d6uA}o zID?VVzplr1C-FS9ta$f?ISj z!4YFzVz>*yO(ZuW8b%xpxJfW#@h}p(1&2rmOzP}Ibg{QU4!&+=&w;}(z~2M7rqKN0 z;FQj{&M{_I#<_fM@NSW%sXPfe``vi1MPTk1L&n+(v;3S=l$4vKTrnfYr)p$EHJ_F0_t~S}GTejbq6y{91M{v}Yao$$Bw{>-0_rTd8)75RfSPGa-jp=z?V1?)zLTU*OojT%s~dRD3PJ&07~NF-4E zv7k7x031B+0uE(iN{-6Hw73}=%!+HvU`~$9#(Bcn>u`fE78U8x1ny;sa5NgGQRvb< z3wFaS9wvo#Ep&AAEKy7C=GIkR+F$`eFbbo+#94O+LyIIz(g#UgH=adflo!Rp$em~< zC4nOJ6B*_^JHQ88itHrr3sjTB&7uYytslZTZXT5&_~PEJ?#kVpLEt!jxKrpUR70B# zw{_GB({wOMR9G-xRtTfEAV9%cDcpH3%@@1pz^@r?|D(o_rfmTlFOj85>G%JMidby$*?Q z--A##kZh_oUaYij(kY_}kO7TMN-{FBI|rW}4CzbG2u#!-VFTzn`FuCL`EJjoVGp>hlc zT>mmVFD(q%o0H-j@gB9rJ8H?T+nFa{9W_C&dZ!wlid@fd{t3~p8raaB7*c_3T>cpR z^|BF-%!g+r)$K)yv8E;Q8v>P%p<-N_yIY!yQvyVmxWfh1zvIw_YC+dgmCXYdTN*}lkyEI zeygfrj#e2h8Zu~-CIig?oL+lupOrzIPJ`bPGJ+-aV5RaZTta40HO%V>tmkpof)1LO z3mm)1E-(95qtgG{XQp0OuiT}0wr+TApVr{i_=tG7;hJ+i{{}?uL$~z~F4K$cB1Gzd z21Cav+s4!ZpXeSW6%&yY1}GB>ULh$d;nPM+uYE8s+(|6M0eaOp@%GEh`+d{P4+>;K zWR$sPEa&09mROf&VMZ?2eM}rMbuG0Kawh2Vd6DmP64OMpk)zn{oJQFvdn&K15U z9#jDtfY0MQ28tlq@K?IM^?UvXZ~UM;zVEFo9GBQ0=ArY+7)=AR{GtCUDtb6|Am6ac z?aOOXFF#hxJf-vtROLClgwNAl=RQ0rxbMC0b-jy?t{3qf)QFGr{#O(n$ft20VG-Or zZptI1a?0-`6k24wGPEg!mH>+Z5?zmwA{cU9mF6km8=&2cVB(;p9g^g#VL|yGx5@^r z3;!6e-2&s`DsVi!r@>0%Z0|xmlamXpKC9cn$TPPs}!u4 bhMQ#x&kI&o0lr>B`YH8N8%%o({5JjtrGce|