Esempio n. 1
0
 def test_order_does_not_affect_equality(self):
     d = dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7)
     od1 = OrderedDict(sorted(d.items()))
     od2 = OrderedDict(reversed(list(od1.items())))
     dd1 = DotDict(sorted(d.items()))
     dd2 = DotDict(reversed(list(dd1.items())))
     for d1, d2 in [(dd1, dd2), (dd1, d), (dd2, d), (dd1, od1), (dd2, od2)]:
         assert_equal(d1, d2)
         assert_equal(d2, d1)
     if not IRONPYTHON:
         # https://github.com/IronLanguages/main/issues/1168
         for d1, d2 in [(dd1, od2), (dd2, od1)]:
             assert_equal(d1, d2)
             assert_equal(d2, d1)
     assert_not_equal(od1, od2)
Esempio n. 2
0
 def test_order_does_not_affect_equality(self):
     d = dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7)
     od1 = OrderedDict(sorted(d.items()))
     od2 = OrderedDict(reversed(list(od1.items())))
     dd1 = DotDict(sorted(d.items()))
     dd2 = DotDict(reversed(list(dd1.items())))
     for d1, d2 in [(dd1, dd2), (dd1, d), (dd2, d), (dd1, od1), (dd2, od2)]:
         assert_equal(d1, d2)
         assert_equal(d2, d1)
     if not IRONPYTHON:
         # https://github.com/IronLanguages/main/issues/1168
         for d1, d2 in [(dd1, od2), (dd2, od1)]:
             assert_equal(d1, d2)
             assert_equal(d2, d1)
     assert_not_equal(od1, od2)
Esempio n. 3
0
class TestDotDict(unittest.TestCase):

    def setUp(self):
        self.dd = DotDict([('z', 1), (2, 'y'), ('x', 3)])

    def test_init(self):
        assert_true(DotDict() == DotDict({}) == DotDict([]))
        assert_true(DotDict(a=1) == DotDict({'a': 1}) == DotDict([('a', 1)]))
        assert_true(DotDict({'a': 1}, b=2) ==
                    DotDict({'a': 1, 'b': 2}) ==
                    DotDict([('a', 1), ('b', 2)]))
        assert_raises(TypeError, DotDict, None)

    def test_get(self):
        assert_equal(self.dd[2], 'y')
        assert_equal(self.dd.x, 3)
        assert_raises(KeyError, self.dd.__getitem__, 'nonex')
        assert_raises(AttributeError, self.dd.__getattr__, 'nonex')

    def test_equality(self):
        assert_true(self.dd == self.dd)
        assert_false(self.dd != self.dd)
        assert_true(self.dd == DotDict(self.dd))
        assert_false(self.dd != DotDict(self.dd))
        assert_false(self.dd == DotDict())
        assert_true(self.dd != DotDict())

    def test_equality_with_normal_dict(self):
        assert_equal(self.dd, {'z': 1, 2: 'y', 'x': 3})

    def test_hash(self):
        assert_raises(TypeError, hash, self.dd)

    def test_set(self):
        self.dd.x = 42
        self.dd.new = 43
        self.dd[2] = 44
        self.dd['n2'] = 45
        assert_equal(self.dd, {'z': 1, 2: 44, 'x': 42, 'new': 43, 'n2': 45})

    def test_del(self):
        del self.dd.x
        del self.dd[2]
        self.dd.pop('z')
        assert_equal(self.dd, {})
        assert_raises(KeyError, self.dd.__delitem__, 'nonex')
        assert_raises(AttributeError, self.dd.__delattr__, 'nonex')

    def test_same_str_and_repr_format_as_with_normal_dict(self):
        D = {'foo': 'bar', '"\'': '"\'', '\n': '\r', 1: 2, (): {}, True: False}
        for d in {}, {'a': 1}, D:
            for formatter in str, repr:
                result = formatter(DotDict(d))
                assert_equal(eval(result, {}), d)

    def test_is_ordered(self):
        assert_equal(list(self.dd), ['z', 2, 'x'])
        self.dd.z = 'new value'
        self.dd.a_new_item = 'last'
        self.dd.pop('x')
        assert_equal(list(self.dd.items()),
                     [('z', 'new value'), (2, 'y'), ('a_new_item', 'last')])
        self.dd.x = 'last'
        assert_equal(list(self.dd.items()),
                     [('z', 'new value'), (2, 'y'), ('a_new_item', 'last'), ('x', 'last')])

    def test_order_does_not_affect_equality(self):
        d = dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7)
        od1 = OrderedDict(sorted(d.items()))
        od2 = OrderedDict(reversed(list(od1.items())))
        dd1 = DotDict(sorted(d.items()))
        dd2 = DotDict(reversed(list(dd1.items())))
        for d1, d2 in [(dd1, dd2), (dd1, d), (dd2, d), (dd1, od1), (dd2, od2)]:
            assert_equal(d1, d2)
            assert_equal(d2, d1)
        if not IRONPYTHON:
            # https://github.com/IronLanguages/main/issues/1168
            for d1, d2 in [(dd1, od2), (dd2, od1)]:
                assert_equal(d1, d2)
                assert_equal(d2, d1)
        assert_not_equal(od1, od2)
Esempio n. 4
0
 def _dot_dict(self, value):
     if is_dict_like(value):
         value = DotDict((n, self._dot_dict(v)) for n, v in value.items())
     return value
Esempio n. 5
0
class tally_sheet:

    r"""
    This class is the implementation of a tally sheet.  The sheet can be
    viewed as rows and columns.  Each row has a unique key field.

    This class provides methods to tally the results (totals, etc.).

    Example code:

    # Create an ordered dict to represent your field names/initial values.
    try:
        boot_results_fields = collections.OrderedDict([('total', 0), ('pass',
        0), ('fail', 0)])
    except AttributeError:
        boot_results_fields = DotDict([('total', 0), ('pass', 0), ('fail', 0)])
    # Create the tally sheet.
    boot_test_results = tally_sheet('boot type', boot_results_fields,
    'boot_test_results')
    # Set your sum fields (fields which are to be totalled).
    boot_test_results.set_sum_fields(['total', 'pass', 'fail'])
    # Set calc fields (within a row, a certain field can be derived from
    # other fields in the row.
    boot_test_results.set_calc_fields(['total=pass+fail'])

    # Create some records.
    boot_test_results.add_row('BMC Power On')
    boot_test_results.add_row('BMC Power Off')

    # Increment field values.
    boot_test_results.inc_row_field('BMC Power On', 'pass')
    boot_test_results.inc_row_field('BMC Power Off', 'pass')
    boot_test_results.inc_row_field('BMC Power On', 'fail')
    # Have the results tallied...
    boot_test_results.calc()
    # And printed...
    boot_test_results.print_report()

    Example result:

    Boot Type                      Total Pass Fail
    ------------------------------ ----- ---- ----
    BMC Power On                       2    1    1
    BMC Power Off                      1    1    0
    ==============================================
    Totals                             3    2    1

    """

    def __init__(self,
                 row_key_field_name='Description',
                 init_fields_dict=dict(),
                 obj_name='tally_sheet'):

        r"""
        Create a tally sheet object.

        Description of arguments:
        row_key_field_name          The name of the row key field (e.g.
                                    boot_type, team_name, etc.)
        init_fields_dict            A dictionary which contains field
                                    names/initial values.
        obj_name                    The name of the tally sheet.
        """

        self.__obj_name = obj_name
        # The row key field uniquely identifies the row.
        self.__row_key_field_name = row_key_field_name
        # Create a "table" which is an ordered dictionary.
        # If we're running python 2.7 or later, collections has an
        # OrderedDict we can use.  Otherwise, we'll try to use the DotDict (a
        # robot library).  If neither of those are available, we fail.
        try:
            self.__table = collections.OrderedDict()
        except AttributeError:
            self.__table = DotDict()
        # Save the initial fields dictionary.
        self.__init_fields_dict = init_fields_dict
        self.__totals_line = init_fields_dict
        self.__sum_fields = []
        self.__calc_fields = []

    def init(self,
             row_key_field_name,
             init_fields_dict,
             obj_name='tally_sheet'):
        self.__init__(row_key_field_name,
                      init_fields_dict,
                      obj_name='tally_sheet')

    def set_sum_fields(self, sum_fields):

        r"""
        Set the sum fields, i.e. create a list of field names which are to be
        summed and included on the totals line of reports.

        Description of arguments:
        sum_fields                  A list of field names.
        """

        self.__sum_fields = sum_fields

    def set_calc_fields(self, calc_fields):

        r"""
        Set the calc fields, i.e. create a list of field names within a given
        row which are to be calculated for the user.

        Description of arguments:
        calc_fields                 A string expression such as
                                    'total=pass+fail' which shows which field
                                    on a given row is derived from other
                                    fields in the same row.
        """

        self.__calc_fields = calc_fields

    def add_row(self, row_key, init_fields_dict=None):

        r"""
        Add a row to the tally sheet.

        Description of arguments:
        row_key                     A unique key value.
        init_fields_dict            A dictionary of field names/initial
                                    values.  The number of fields in this
                                    dictionary must be the same as what was
                                    specified when the tally sheet was
                                    created.  If no value is passed, the value
                                    used to create the tally sheet will be
                                    used.
        """

        if init_fields_dict is None:
            init_fields_dict = self.__init_fields_dict
        try:
            self.__table[row_key] = collections.OrderedDict(init_fields_dict)
        except AttributeError:
            self.__table[row_key] = DotDict(init_fields_dict)

    def update_row_field(self, row_key, field_key, value):

        r"""
        Update a field in a row with the specified value.

        Description of arguments:
        row_key                     A unique key value that identifies the row
                                    to be updated.
        field_key                   The key that identifies which field in the
                                    row that is to be updated.
        value                       The value to set into the specified
                                    row/field.
        """

        self.__table[row_key][field_key] = value

    def inc_row_field(self, row_key, field_key):

        r"""
        Increment the value of the specified field in the specified row.  The
        value of the field must be numeric.

        Description of arguments:
        row_key                     A unique key value that identifies the row
                                    to be updated.
        field_key                   The key that identifies which field in the
                                    row that is to be updated.
        """

        self.__table[row_key][field_key] += 1

    def dec_row_field(self, row_key, field_key):

        r"""
        Decrement the value of the specified field in the specified row.  The
        value of the field must be numeric.

        Description of arguments:
        row_key                     A unique key value that identifies the row
                                    to be updated.
        field_key                   The key that identifies which field in the
                                    row that is to be updated.
        """

        self.__table[row_key][field_key] -= 1

    def calc(self):

        r"""
        Calculate totals and row calc fields.  Also, return totals_line
        dictionary.
        """

        self.__totals_line = copy.deepcopy(self.__init_fields_dict)
        # Walk through the rows of the table.
        for row_key, value in self.__table.items():
            # Walk through the calc fields and process them.
            for calc_field in self.__calc_fields:
                tokens = [i for i in re.split(r'(\d+|\W+)', calc_field) if i]
                cmd_buf = ""
                for token in tokens:
                    if token in ("=", "+", "-", "*", "/"):
                        cmd_buf += token + " "
                    else:
                        # Note: Using "mangled" name for the sake of the exec
                        # statement (below).
                        cmd_buf += "self._" + self.__class__.__name__ +\
                                   "__table['" + row_key + "']['" +\
                                   token + "'] "
                exec(cmd_buf)

            for field_key, sub_value in value.items():
                if field_key in self.__sum_fields:
                    self.__totals_line[field_key] += sub_value

        return self.__totals_line

    def sprint_obj(self):

        r"""
        sprint the fields of this object.  This would normally be for debug
        purposes.
        """

        buffer = ""

        buffer += "class name: " + self.__class__.__name__ + "\n"
        buffer += gp.sprint_var(self.__obj_name)
        buffer += gp.sprint_var(self.__row_key_field_name)
        buffer += gp.sprint_var(self.__table)
        buffer += gp.sprint_var(self.__init_fields_dict)
        buffer += gp.sprint_var(self.__sum_fields)
        buffer += gp.sprint_var(self.__totals_line)
        buffer += gp.sprint_var(self.__calc_fields)
        buffer += gp.sprint_var(self.__table)

        return buffer

    def print_obj(self):

        r"""
        print the fields of this object to stdout.  This would normally be for
        debug purposes.
        """

        sys.stdout.write(self.sprint_obj())

    def sprint_report(self):

        r"""
        sprint the tally sheet in a formatted way.
        """

        buffer = ""
        # Build format strings.
        col_names = [self.__row_key_field_name.title()]
        report_width = 30
        key_width = 30
        format_string = '{0:<' + str(key_width) + '}'
        dash_format_string = '{0:-<' + str(key_width) + '}'
        field_num = 0

        first_rec = next(iter(self.__table.items()))
        for row_key, value in first_rec[1].items():
            field_num += 1
            if type(value) is int:
                align = ':>'
            else:
                align = ':<'
            format_string += ' {' + str(field_num) + align +\
                             str(len(row_key)) + '}'
            dash_format_string += ' {' + str(field_num) + ':->' +\
                                  str(len(row_key)) + '}'
            report_width += 1 + len(row_key)
            col_names.append(row_key.title())
        num_fields = field_num + 1
        totals_line_fmt = '{0:=<' + str(report_width) + '}'

        buffer += format_string.format(*col_names) + "\n"
        buffer += dash_format_string.format(*([''] * num_fields)) + "\n"
        for row_key, value in self.__table.items():
            buffer += format_string.format(row_key, *value.values()) + "\n"

        buffer += totals_line_fmt.format('') + "\n"
        buffer += format_string.format('Totals',
                                       *self.__totals_line.values()) + "\n"

        return buffer

    def print_report(self):

        r"""
        print the tally sheet in a formatted way.
        """

        sys.stdout.write(self.sprint_report())
Esempio n. 6
0
 def _dot_dict(self, value):
     if is_dict_like(value):
         value = DotDict((n, self._dot_dict(v)) for n, v in value.items())
     return value
Esempio n. 7
0
class TestDotDict(unittest.TestCase):
    def setUp(self):
        self.dd = DotDict([('z', 1), (2, 'y'), ('x', 3)])

    def test_get(self):
        assert_equal(self.dd[2], 'y')
        assert_equal(self.dd.x, 3)
        assert_raises(KeyError, self.dd.__getitem__, 'nonex')
        assert_raises(AttributeError, self.dd.__getattr__, 'nonex')

    def test_equality_with_normal_dict(self):
        assert_equal(self.dd, {'z': 1, 2: 'y', 'x': 3})

    def test_set(self):
        self.dd.x = 42
        self.dd.new = 43
        self.dd[2] = 44
        self.dd['n2'] = 45
        assert_equal(self.dd, {'z': 1, 2: 44, 'x': 42, 'new': 43, 'n2': 45})

    def test_del(self):
        del self.dd.x
        del self.dd[2]
        self.dd.pop('z')
        assert_equal(self.dd, {})
        assert_raises(KeyError, self.dd.__delitem__, 'nonex')
        assert_raises(AttributeError, self.dd.__delattr__, 'nonex')

    def test_same_str_and_repr_format_as_with_normal_dict(self):
        D = {'foo': 'bar', '"\'': '"\'', '\n': '\r', 1: 2, (): {}, True: False}
        for d in {}, {'a': 1}, D:
            for formatter in str, repr:
                result = formatter(DotDict(d))
                assert_equal(eval(result, {}), d)

    def test_is_ordered(self):
        assert_equal(list(self.dd), ['z', 2, 'x'])
        self.dd.z = 'new value'
        self.dd.a_new_item = 'last'
        self.dd.pop('x')
        assert_equal(list(self.dd.items()), [('z', 'new value'), (2, 'y'),
                                             ('a_new_item', 'last')])
        self.dd.x = 'last'
        assert_equal(list(self.dd.items()), [('z', 'new value'), (2, 'y'),
                                             ('a_new_item', 'last'),
                                             ('x', 'last')])

    def test_order_does_not_affect_equality(self):
        d = dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7)
        od1 = OrderedDict(sorted(d.items()))
        od2 = OrderedDict(reversed(list(od1.items())))
        dd1 = DotDict(sorted(d.items()))
        dd2 = DotDict(reversed(list(dd1.items())))
        for d1, d2 in [(dd1, dd2), (dd1, d), (dd2, d), (dd1, od1), (dd2, od2)]:
            assert_equal(d1, d2)
            assert_equal(d2, d1)
        if not IRONPYTHON:
            # https://github.com/IronLanguages/main/issues/1168
            for d1, d2 in [(dd1, od2), (dd2, od1)]:
                assert_equal(d1, d2)
                assert_equal(d2, d1)
        assert_not_equal(od1, od2)
class tally_sheet:

    r"""
    This class is the implementation of a tally sheet.  The sheet can be
    viewed as rows and columns.  Each row has a unique key field.

    This class provides methods to tally the results (totals, etc.).

    Example code:

    # Create an ordered dict to represent your field names/initial values.
    try:
        boot_results_fields = collections.OrderedDict([('total', 0), ('pass',
        0), ('fail', 0)])
    except AttributeError:
        boot_results_fields = DotDict([('total', 0), ('pass', 0), ('fail', 0)])
    # Create the tally sheet.
    boot_test_results = tally_sheet('boot type', boot_results_fields,
    'boot_test_results')
    # Set your sum fields (fields which are to be totalled).
    boot_test_results.set_sum_fields(['total', 'pass', 'fail'])
    # Set calc fields (within a row, a certain field can be derived from
    # other fields in the row.
    boot_test_results.set_calc_fields(['total=pass+fail'])

    # Create some records.
    boot_test_results.add_row('BMC Power On')
    boot_test_results.add_row('BMC Power Off')

    # Increment field values.
    boot_test_results.inc_row_field('BMC Power On', 'pass')
    boot_test_results.inc_row_field('BMC Power Off', 'pass')
    boot_test_results.inc_row_field('BMC Power On', 'fail')
    # Have the results tallied...
    boot_test_results.calc()
    # And printed...
    boot_test_results.print_report()

    Example result:

    Boot Type                           Total Pass Fail
    ----------------------------------- ----- ---- ----
    BMC Power On                            2    1    1
    BMC Power Off                           1    1    0
    ===================================================
    Totals                                  3    2    1

    """

    def __init__(self,
                 row_key_field_name='Description',
                 init_fields_dict=dict(),
                 obj_name='tally_sheet'):
        r"""
        Create a tally sheet object.

        Description of arguments:
        row_key_field_name          The name of the row key field (e.g.
                                    boot_type, team_name, etc.)
        init_fields_dict            A dictionary which contains field
                                    names/initial values.
        obj_name                    The name of the tally sheet.
        """

        self.__obj_name = obj_name
        # The row key field uniquely identifies the row.
        self.__row_key_field_name = row_key_field_name
        # Create a "table" which is an ordered dictionary.
        # If we're running python 2.7 or later, collections has an
        # OrderedDict we can use.  Otherwise, we'll try to use the DotDict (a
        # robot library).  If neither of those are available, we fail.
        try:
            self.__table = collections.OrderedDict()
        except AttributeError:
            self.__table = DotDict()
        # Save the initial fields dictionary.
        self.__init_fields_dict = init_fields_dict
        self.__totals_line = init_fields_dict
        self.__sum_fields = []
        self.__calc_fields = []

    def init(self,
             row_key_field_name,
             init_fields_dict,
             obj_name='tally_sheet'):
        self.__init__(row_key_field_name,
                      init_fields_dict,
                      obj_name='tally_sheet')

    def set_sum_fields(self, sum_fields):
        r"""
        Set the sum fields, i.e. create a list of field names which are to be
        summed and included on the totals line of reports.

        Description of arguments:
        sum_fields                  A list of field names.
        """

        self.__sum_fields = sum_fields

    def set_calc_fields(self, calc_fields):
        r"""
        Set the calc fields, i.e. create a list of field names within a given
        row which are to be calculated for the user.

        Description of arguments:
        calc_fields                 A string expression such as
                                    'total=pass+fail' which shows which field
                                    on a given row is derived from other
                                    fields in the same row.
        """

        self.__calc_fields = calc_fields

    def add_row(self, row_key, init_fields_dict=None):
        r"""
        Add a row to the tally sheet.

        Description of arguments:
        row_key                     A unique key value.
        init_fields_dict            A dictionary of field names/initial
                                    values.  The number of fields in this
                                    dictionary must be the same as what was
                                    specified when the tally sheet was
                                    created.  If no value is passed, the value
                                    used to create the tally sheet will be
                                    used.
        """

        if init_fields_dict is None:
            init_fields_dict = self.__init_fields_dict
        try:
            self.__table[row_key] = collections.OrderedDict(init_fields_dict)
        except AttributeError:
            self.__table[row_key] = DotDict(init_fields_dict)

    def update_row_field(self, row_key, field_key, value):
        r"""
        Update a field in a row with the specified value.

        Description of arguments:
        row_key                     A unique key value that identifies the row
                                    to be updated.
        field_key                   The key that identifies which field in the
                                    row that is to be updated.
        value                       The value to set into the specified
                                    row/field.
        """

        self.__table[row_key][field_key] = value

    def inc_row_field(self, row_key, field_key):
        r"""
        Increment the value of the specified field in the specified row.  The
        value of the field must be numeric.

        Description of arguments:
        row_key                     A unique key value that identifies the row
                                    to be updated.
        field_key                   The key that identifies which field in the
                                    row that is to be updated.
        """

        self.__table[row_key][field_key] += 1

    def dec_row_field(self, row_key, field_key):
        r"""
        Decrement the value of the specified field in the specified row.  The
        value of the field must be numeric.

        Description of arguments:
        row_key                     A unique key value that identifies the row
                                    to be updated.
        field_key                   The key that identifies which field in the
                                    row that is to be updated.
        """

        self.__table[row_key][field_key] -= 1

    def calc(self):
        r"""
        Calculate totals and row calc fields.  Also, return totals_line
        dictionary.
        """

        self.__totals_line = copy.deepcopy(self.__init_fields_dict)
        # Walk through the rows of the table.
        for row_key, value in self.__table.items():
            # Walk through the calc fields and process them.
            for calc_field in self.__calc_fields:
                tokens = [i for i in re.split(r'(\d+|\W+)', calc_field) if i]
                cmd_buf = ""
                for token in tokens:
                    if token in ("=", "+", "-", "*", "/"):
                        cmd_buf += token + " "
                    else:
                        # Note: Using "mangled" name for the sake of the exec
                        # statement (below).
                        cmd_buf += "self._" + self.__class__.__name__ +\
                                   "__table['" + row_key + "']['" +\
                                   token + "'] "
                exec(cmd_buf)

            for field_key, sub_value in value.items():
                if field_key in self.__sum_fields:
                    self.__totals_line[field_key] += sub_value

        return self.__totals_line

    def sprint_obj(self):
        r"""
        sprint the fields of this object.  This would normally be for debug
        purposes.
        """

        buffer = ""

        buffer += "class name: " + self.__class__.__name__ + "\n"
        buffer += gp.sprint_var(self.__obj_name)
        buffer += gp.sprint_var(self.__row_key_field_name)
        buffer += gp.sprint_var(self.__table)
        buffer += gp.sprint_var(self.__init_fields_dict)
        buffer += gp.sprint_var(self.__sum_fields)
        buffer += gp.sprint_var(self.__totals_line)
        buffer += gp.sprint_var(self.__calc_fields)
        buffer += gp.sprint_var(self.__table)

        return buffer

    def print_obj(self):
        r"""
        print the fields of this object to stdout.  This would normally be for
        debug purposes.
        """

        sys.stdout.write(self.sprint_obj())

    def sprint_report(self):
        r"""
        sprint the tally sheet in a formatted way.
        """

        buffer = ""
        # Build format strings.
        col_names = [self.__row_key_field_name.title()]
        report_width = 40
        key_width = 40
        format_string = '{0:<' + str(key_width) + '}'
        dash_format_string = '{0:-<' + str(key_width) + '}'
        field_num = 0

        first_rec = next(iter(self.__table.items()))
        for row_key, value in first_rec[1].items():
            field_num += 1
            if isinstance(value, int):
                align = ':>'
            else:
                align = ':<'
            format_string += ' {' + str(field_num) + align +\
                             str(len(row_key)) + '}'
            dash_format_string += ' {' + str(field_num) + ':->' +\
                                  str(len(row_key)) + '}'
            report_width += 1 + len(row_key)
            col_names.append(row_key.title())
        num_fields = field_num + 1
        totals_line_fmt = '{0:=<' + str(report_width) + '}'

        buffer += format_string.format(*col_names) + "\n"
        buffer += dash_format_string.format(*([''] * num_fields)) + "\n"
        for row_key, value in self.__table.items():
            buffer += format_string.format(row_key, *value.values()) + "\n"

        buffer += totals_line_fmt.format('') + "\n"
        buffer += format_string.format('Totals',
                                       *self.__totals_line.values()) + "\n"

        return buffer

    def print_report(self):
        r"""
        print the tally sheet in a formatted way.
        """

        sys.stdout.write(self.sprint_report())
class TestDotDict(unittest.TestCase):
    def setUp(self):
        self.dd = DotDict([('z', 1), (2, 'y'), ('x', 3)])

    def test_get(self):
        assert_equal(self.dd[2], 'y')
        assert_equal(self.dd.x, 3)
        assert_raises(KeyError, self.dd.__getitem__, 'nonex')
        assert_raises(AttributeError, self.dd.__getattr__, 'nonex')

    def test_equality(self):
        assert_true(self.dd == self.dd)
        assert_false(self.dd != self.dd)
        assert_true(self.dd == DotDict(self.dd))
        assert_false(self.dd != DotDict(self.dd))
        assert_false(self.dd == DotDict())
        assert_true(self.dd != DotDict())

    def test_equality_with_normal_dict(self):
        assert_equal(self.dd, {'z': 1, 2: 'y', 'x': 3})

    def test_hash(self):
        assert_raises(TypeError, hash, self.dd)

    def test_set(self):
        self.dd.x = 42
        self.dd.new = 43
        self.dd[2] = 44
        self.dd['n2'] = 45
        assert_equal(self.dd, {'z': 1, 2: 44, 'x': 42, 'new': 43, 'n2': 45})

    def test_del(self):
        del self.dd.x
        del self.dd[2]
        self.dd.pop('z')
        assert_equal(self.dd, {})
        assert_raises(KeyError, self.dd.__delitem__, 'nonex')
        assert_raises(AttributeError, self.dd.__delattr__, 'nonex')

    def test_same_str_and_repr_format_as_with_normal_dict(self):
        D = {'foo': 'bar', '"\'': '"\'', '\n': '\r', 1: 2, (): {}, True: False}
        for d in {}, {'a': 1}, D:
            for formatter in str, repr:
                result = formatter(DotDict(d))
                assert_equal(eval(result, {}), d)

    def test_is_ordered(self):
        assert_equal(list(self.dd), ['z', 2, 'x'])
        self.dd.z = 'new value'
        self.dd.a_new_item = 'last'
        self.dd.pop('x')
        assert_equal(list(self.dd.items()), [('z', 'new value'), (2, 'y'),
                                             ('a_new_item', 'last')])
        self.dd.x = 'last'
        assert_equal(list(self.dd.items()), [('z', 'new value'), (2, 'y'),
                                             ('a_new_item', 'last'),
                                             ('x', 'last')])

    def test_order_does_not_affect_equality(self):
        d = dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7)
        od1 = OrderedDict(sorted(d.items()))
        od2 = OrderedDict(reversed(list(od1.items())))
        dd1 = DotDict(sorted(d.items()))
        dd2 = DotDict(reversed(list(dd1.items())))
        for d1, d2 in [(dd1, dd2), (dd1, d), (dd2, d), (dd1, od1), (dd2, od2)]:
            assert_equal(d1, d2)
            assert_equal(d2, d1)
        if not IRONPYTHON:
            # https://github.com/IronLanguages/main/issues/1168
            for d1, d2 in [(dd1, od2), (dd2, od1)]:
                assert_equal(d1, d2)
                assert_equal(d2, d1)
        assert_not_equal(od1, od2)

    def test_nested_dicts(self):
        leaf = {'key': 'value'}
        d = DotDict({
            'nested': leaf,
            'deeper': {
                'nesting': leaf
            }
        },
                    nested2=leaf)
        assert_equal(d.nested.key, 'value')
        assert_equal(d.deeper.nesting.key, 'value')
        assert_equal(d.nested2.key, 'value')

    def test_nested_dicts_inside_list_likes(self):
        leaf = {'key': 'value'}
        d = DotDict(list=[leaf, leaf, [leaf]], tuple=(leaf, {'deeper': leaf}))
        assert_equal(d.list[0].key, 'value')
        assert_equal(d.list[1].key, 'value')
        assert_equal(d.list[2][0].key, 'value')
        assert_equal(d.tuple[0].key, 'value')
        assert_equal(d.tuple[1].deeper.key, 'value')
        assert_true(isinstance(d.tuple, list))