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 bf2a918..0000000 Binary files a/examples/selective_repeat.pyc and /dev/null differ 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 f805e35..0000000 Binary files a/lib/__init__.pyc and /dev/null differ diff --git a/lib/prettytable/__init__.py b/lib/prettytable/__init__.py deleted file mode 100644 index 4086619..0000000 --- a/lib/prettytable/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed May 21 08:46:00 2014 - -@author: dido -""" - -from prettytable import PrettyTable \ No newline at end of file diff --git a/lib/prettytable/__init__.pyc b/lib/prettytable/__init__.pyc deleted file mode 100644 index a3a8585..0000000 Binary files a/lib/prettytable/__init__.pyc and /dev/null differ 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 d4d1f94..0000000 Binary files a/lib/prettytable/prettytable.pyc and /dev/null differ diff --git a/lib/prettytable/prettytable_test.py b/lib/prettytable/prettytable_test.py deleted file mode 100644 index 66a821b..0000000 --- a/lib/prettytable/prettytable_test.py +++ /dev/null @@ -1,588 +0,0 @@ -# coding=UTF-8 - -from prettytable import * - -import sys -py3k = sys.version_info[0] >= 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 a30b1f5..0000000 Binary files a/lib/simpy/__init__.pyc and /dev/null differ 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 795c7fa..0000000 Binary files a/lib/simpy/_compat.pyc and /dev/null differ 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 ce71042..0000000 Binary files a/lib/simpy/core.pyc and /dev/null differ 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 aa3d3a5..0000000 Binary files a/lib/simpy/events.pyc and /dev/null differ diff --git a/lib/simpy/monitoring.py b/lib/simpy/monitoring.py deleted file mode 100644 index b1c3097..0000000 --- a/lib/simpy/monitoring.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -SimPy's monitoring capabilities will be added in version 3.1. - -""" diff --git a/lib/simpy/monitoring.pyc b/lib/simpy/monitoring.pyc deleted file mode 100644 index e3c0432..0000000 Binary files a/lib/simpy/monitoring.pyc and /dev/null differ diff --git a/lib/simpy/resources/__init__.py b/lib/simpy/resources/__init__.py deleted file mode 100644 index ab95dd7..0000000 --- a/lib/simpy/resources/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -SimPy defines three kinds of resources with one or more concrete resource types -each: - -- :mod:`~simpy.resources.resource`: Resources that can be used by a limited - number of processes at a time (e.g., a gas station with a limited number of - fuel pumps). - -- :mod:`~simpy.resources.container`: Resources that model the production and - consumption of a homogeneous, undifferentiated bulk. It may either be - continuous (like water) or discrete (like apples). - -- :mod:`~simpy.resources.store`: Resources that allow the production and - consumption of discrete Python objects. - -The :mod:`~simpy.resources.base` module defines the base classes that are used -by all resource types. - -""" diff --git a/lib/simpy/resources/__init__.pyc b/lib/simpy/resources/__init__.pyc deleted file mode 100644 index c08e5c6..0000000 Binary files a/lib/simpy/resources/__init__.pyc and /dev/null differ 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 7af78d7..0000000 Binary files a/lib/simpy/resources/base.pyc and /dev/null differ diff --git a/lib/simpy/resources/container.py b/lib/simpy/resources/container.py deleted file mode 100644 index d7a1c5b..0000000 --- a/lib/simpy/resources/container.py +++ /dev/null @@ -1,105 +0,0 @@ -""" -This module contains all :class:`Container` like resources. - -Containers model the production and consumption of a homogeneous, -undifferentiated bulk. It may either be continuous (like water) or discrete -(like apples). - -For example, a gasoline station stores gas (petrol) in large tanks. Tankers -increase, and refuelled cars decrease, the amount of gas in the station's -storage tanks. - -""" -from simpy.core import BoundClass -from simpy.resources import base - - -class ContainerPut(base.Put): - """An event that puts *amount* into the *container*. The event is triggered - as soon as there's enough space in the *container*. - - Raise a :exc:`ValueError` if ``amount <= 0``. - - """ - def __init__(self, container, amount): - if amount <= 0: - raise ValueError('amount(=%s) must be > 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 2bdf54b..0000000 Binary files a/lib/simpy/resources/container.pyc and /dev/null differ 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 8464003..0000000 Binary files a/lib/simpy/resources/resource.pyc and /dev/null differ 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 e5aace9..0000000 Binary files a/lib/simpy/resources/store.pyc and /dev/null differ 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 0ef7f90..0000000 Binary files a/lib/simpy/rt.pyc and /dev/null differ 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 1bd9419..0000000 Binary files a/lib/simpy/test/__init__.pyc and /dev/null differ diff --git a/lib/simpy/test/conftest.py b/lib/simpy/test/conftest.py deleted file mode 100644 index 8f98ff9..0000000 --- a/lib/simpy/test/conftest.py +++ /dev/null @@ -1,13 +0,0 @@ -import pytest - -import simpy - - -@pytest.fixture -def log(): - return [] - - -@pytest.fixture -def env(): - return simpy.Environment() diff --git a/lib/simpy/test/conftest.pyc b/lib/simpy/test/conftest.pyc deleted file mode 100644 index ebc0fc0..0000000 Binary files a/lib/simpy/test/conftest.pyc and /dev/null differ 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 d396425..0000000 Binary files a/lib/simpy/test/test_condition.pyc and /dev/null differ diff --git a/lib/simpy/test/test_environment.py b/lib/simpy/test/test_environment.py deleted file mode 100644 index 55edf9d..0000000 --- a/lib/simpy/test/test_environment.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -General test for the the `simpy.core.Environment`. - -""" -# Pytest gets the parameters "env" and "log" from the *conftest.py* file -import pytest - - -def test_event_queue_empty(env, log): - """The simulation should stop if there are no more events, that means, no - more active process.""" - def pem(env, log): - while env.now < 2: - log.append(env.now) - yield env.timeout(1) - - env.process(pem(env, log)) - env.run(10) - - assert log == [0, 1] - - -def test_run_negative_until(env): - """Test passing a negative time to run.""" - pytest.raises(ValueError, env.run, -3) - - -def test_run_resume(env): - """Stopped simulation can be resumed.""" - events = [env.timeout(t) for t in (5, 10, 15)] - - assert env.now == 0 - assert not any(event.processed for event in events) - - env.run(until=10) - assert env.now == 10 - assert all(event.processed for event in events[:1]) - assert not any(event.processed for event in events[1:]) - - env.run(until=15) - assert env.now == 15 - assert all(event.processed for event in events[:2]) - assert not any(event.processed for event in events[2:]) - - env.run() - assert env.now == 15 - assert all(event.processed for event in events) - - -def test_run_until_value(env): - """Anything that can be converted to a float is a valid until value.""" - env.run(until='3.141592') - assert env.now == 3.141592 diff --git a/lib/simpy/test/test_environment.pyc b/lib/simpy/test/test_environment.pyc deleted file mode 100644 index 5079360..0000000 Binary files a/lib/simpy/test/test_environment.pyc and /dev/null differ diff --git a/lib/simpy/test/test_event.py b/lib/simpy/test/test_event.py deleted file mode 100644 index 7dc7cca..0000000 --- a/lib/simpy/test/test_event.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -Tests for ``simpy.events.Event``. - -""" -# Pytest gets the parameters "env" and "log" from the *conftest.py* file -import re - -import pytest - - -def test_succeed(env): - """Test for the Environment.event() helper function.""" - def child(env, event): - value = yield event - assert value == 'ohai' - assert env.now == 5 - - def parent(env): - event = env.event() - env.process(child(env, event)) - yield env.timeout(5) - event.succeed('ohai') - - env.process(parent(env)) - env.run() - - -def test_fail(env): - """Test for the Environment.event() helper function.""" - def child(env, event): - try: - yield event - pytest.fail('Should not get here.') - except ValueError as err: - assert err.args[0] == 'ohai' - assert env.now == 5 - - def parent(env): - event = env.event() - env.process(child(env, event)) - yield env.timeout(5) - event.fail(ValueError('ohai')) - - env.process(parent(env)) - env.run() - - -def test_names(env): - def pem(): - yield env.exit() - - assert re.match(r'', 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 88dd926..0000000 Binary files a/lib/simpy/test/test_event.pyc and /dev/null differ 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 e9cc37c..0000000 Binary files a/lib/simpy/test/test_exceptions.pyc and /dev/null differ 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 ef2dbf8..0000000 Binary files a/lib/simpy/test/test_interrupts.pyc and /dev/null differ diff --git a/lib/simpy/test/test_process.py b/lib/simpy/test/test_process.py deleted file mode 100644 index e735ee8..0000000 --- a/lib/simpy/test/test_process.py +++ /dev/null @@ -1,238 +0,0 @@ -""" -Tests for the ``simpy.events.Process``. - -""" -# Pytest gets the parameters "env" and "log" from the *conftest.py* file -import pytest - -from simpy import Interrupt - - -def test_start_non_process(env): - """Check that you cannot start a normal function.""" - def foo(): - pass - - pytest.raises(ValueError, env.process, foo) - - -def test_get_state(env): - """A process is alive until it's generator has not terminated.""" - def pem_a(env): - yield env.timeout(3) - - def pem_b(env, pem_a): - yield env.timeout(1) - assert pem_a.is_alive - - yield env.timeout(3) - assert not pem_a.is_alive - - proc_a = env.process(pem_a(env)) - env.process(pem_b(env, proc_a)) - env.run() - - -def test_target(env): - def pem(env, event): - yield event - - event = env.timeout(5) - proc = env.process(pem(env, event)) - - # Wait until "proc" is initialized and yielded the event - while env.peek() < 5: - env.step() - assert proc.target is event - proc.interrupt() - - -def test_wait_for_proc(env): - """A process can wait until another process finishes.""" - def finisher(env): - yield env.timeout(5) - - def waiter(env, finisher): - proc = env.process(finisher(env)) - yield proc # Waits until "proc" finishes - - assert env.now == 5 - - env.process(waiter(env, finisher)) - env.run() - - -def test_exit(env): - """Processes can set a return value via an ``exit()`` function, - comparable to ``sys.exit()``. - - """ - def child(env): - yield env.timeout(1) - env.exit(env.now) - - 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() - - -@pytest.mark.skipif('sys.version_info[:2] < (3, 3)') -def test_return_value(env): - """Processes can set a return value.""" - # Python < 3.2 would raise a SyntaxError if this was real code ... - code = """def child(env): - yield env.timeout(1) - return env.now - """ - globs, locs = {}, {} - code = compile(code, '', '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 491f6a4..0000000 Binary files a/lib/simpy/test/test_process.pyc and /dev/null differ 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 cc85e5c..0000000 Binary files a/lib/simpy/test/test_resources.pyc and /dev/null differ diff --git a/lib/simpy/test/test_rt.py b/lib/simpy/test/test_rt.py deleted file mode 100644 index 7278c8a..0000000 --- a/lib/simpy/test/test_rt.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -Tests for Simpy's real-time behavior. - -""" -import time -try: - # Python >= 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 018db82..0000000 Binary files a/lib/simpy/test/test_rt.pyc and /dev/null differ 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 17be4a8..0000000 Binary files a/lib/simpy/test/test_timeout.pyc and /dev/null differ 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 d886764..0000000 Binary files a/lib/simpy/test/test_util.pyc and /dev/null differ 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 e4122c6..0000000 Binary files a/lib/simpy/util.pyc and /dev/null differ diff --git a/lib/statistics.py b/lib/statistics.py deleted file mode 100644 index 4e5524d..0000000 --- a/lib/statistics.py +++ /dev/null @@ -1,596 +0,0 @@ - -##Module statistics.py -## -## Copyright (c) 2013 Steven D'Aprano . -## -## 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 51d9359..0000000 Binary files a/src/__init__.pyc and /dev/null differ diff --git a/src/application/__init__.pyc b/src/application/__init__.pyc deleted file mode 100644 index 6a0b190..0000000 Binary files a/src/application/__init__.pyc and /dev/null differ diff --git a/src/application/dhcp_client.pyc b/src/application/dhcp_client.pyc deleted file mode 100644 index 1c36190..0000000 Binary files a/src/application/dhcp_client.pyc and /dev/null differ diff --git a/src/application/dhcp_server.pyc b/src/application/dhcp_server.pyc deleted file mode 100644 index 31b42d7..0000000 Binary files a/src/application/dhcp_server.pyc and /dev/null differ diff --git a/src/environment/__init__.pyc b/src/environment/__init__.pyc deleted file mode 100644 index c7d564e..0000000 Binary files a/src/environment/__init__.pyc and /dev/null differ diff --git a/src/environment/environment.pyc b/src/environment/environment.pyc deleted file mode 100644 index 67dfd9d..0000000 Binary files a/src/environment/environment.pyc and /dev/null differ diff --git a/src/link/__init__.pyc b/src/link/__init__.pyc deleted file mode 100644 index 5c3678f..0000000 Binary files a/src/link/__init__.pyc and /dev/null differ diff --git a/src/link/frame.pyc b/src/link/frame.pyc deleted file mode 100644 index c372706..0000000 Binary files a/src/link/frame.pyc and /dev/null differ diff --git a/src/link/mac.pyc b/src/link/mac.pyc deleted file mode 100644 index 511b550..0000000 Binary files a/src/link/mac.pyc and /dev/null differ diff --git a/src/link/nic.pyc b/src/link/nic.pyc deleted file mode 100644 index 421c836..0000000 Binary files a/src/link/nic.pyc and /dev/null differ diff --git a/src/link/switch.pyc b/src/link/switch.pyc deleted file mode 100644 index 64eafba..0000000 Binary files a/src/link/switch.pyc and /dev/null differ diff --git a/src/network/__init__.pyc b/src/network/__init__.pyc deleted file mode 100644 index 34b35ea..0000000 Binary files a/src/network/__init__.pyc and /dev/null differ diff --git a/src/network/datagram.pyc b/src/network/datagram.pyc deleted file mode 100644 index 3e22029..0000000 Binary files a/src/network/datagram.pyc and /dev/null differ diff --git a/src/network/host.pyc b/src/network/host.pyc deleted file mode 100644 index 38f1368..0000000 Binary files a/src/network/host.pyc and /dev/null differ diff --git a/src/network/ip.pyc b/src/network/ip.pyc deleted file mode 100644 index 41435bc..0000000 Binary files a/src/network/ip.pyc and /dev/null differ diff --git a/src/network/router.pyc b/src/network/router.pyc deleted file mode 100644 index 8b6a408..0000000 Binary files a/src/network/router.pyc and /dev/null differ diff --git a/src/physical/__init__.pyc b/src/physical/__init__.pyc deleted file mode 100644 index 94d4b9a..0000000 Binary files a/src/physical/__init__.pyc and /dev/null differ diff --git a/src/physical/databits.pyc b/src/physical/databits.pyc deleted file mode 100644 index f39b721..0000000 Binary files a/src/physical/databits.pyc and /dev/null differ diff --git a/src/physical/hub.pyc b/src/physical/hub.pyc deleted file mode 100644 index 939074e..0000000 Binary files a/src/physical/hub.pyc and /dev/null differ diff --git a/src/physical/link_agent.pyc b/src/physical/link_agent.pyc deleted file mode 100644 index 4fd5618..0000000 Binary files a/src/physical/link_agent.pyc and /dev/null differ diff --git a/src/transport/__init__.pyc b/src/transport/__init__.pyc deleted file mode 100644 index d0f1172..0000000 Binary files a/src/transport/__init__.pyc and /dev/null differ diff --git a/src/transport/udp_packet.pyc b/src/transport/udp_packet.pyc deleted file mode 100644 index 1dfcb2c..0000000 Binary files a/src/transport/udp_packet.pyc and /dev/null differ diff --git a/src/transport/udp_protocol.pyc b/src/transport/udp_protocol.pyc deleted file mode 100644 index 5957b1b..0000000 Binary files a/src/transport/udp_protocol.pyc and /dev/null differ