Skip to content

Commit

Permalink
Merge 2ee4158 into e816d26
Browse files Browse the repository at this point in the history
  • Loading branch information
Nurdok committed Nov 16, 2015
2 parents e816d26 + 2ee4158 commit 04a79ad
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 40 deletions.
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Installation
Usage
^^^^^

To create your own struct, inherit from `BasicStruct` and define the field with the `__slots__` class member.
`BasicStruct` are efficient objects that are automatically comparable, hashable, picklable, printable and reprable.
To create your own struct, inherit from `BasicStruct` and define the field with the `__slots__` class member.
`BasicStruct` are efficient objects that are automatically comparable, hashable, picklable, printable and reprable.

.. code-block:: python
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@
package_dir={'': 'src'},
py_modules=['basicstruct'],
install_requires=['six'],
test_require=['nose'],
test_suite='nose.collector',
)
19 changes: 16 additions & 3 deletions src/basicstruct.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Copyright (c) 2015 Amir Rachum.
# This program is distributed under the MIT license.

"""This is a placeholder."""
"""basicstruct provides BasicStruct, a class for simple struct-like objects."""

import six
from copy import deepcopy
from six.moves import zip
from itertools import chain
from collections import Mapping

__version__ = '1.0.3'
__version__ = '1.0.4-alpha'


class BasicStruct(object):
Expand All @@ -17,13 +18,25 @@ class BasicStruct(object):
__slots__ = () # should be extended by deriving classes

def __init__(self, *args, **kwargs):
default_values = isinstance(self.__slots__, Mapping)
ordered = (not type(self.__slots__) == dict and
not isinstance(self.__slots__, set))

if args and not ordered:
raise ValueError("Can't pass non-keyword arguments to {}, since "
"__slots__ was declared with an unordered "
"iterable.".format(self.__class__.__name__))

arg_pairs = zip(self.__slots__, args)
for key, value in chain(arg_pairs, six.iteritems(kwargs)):
setattr(self, key, value)

for key in self.__slots__:
if not hasattr(self, key):
setattr(self, key, None)
default_value = None
if default_values:
default_value = self.__slots__[key]
setattr(self, key, default_value)

def to_dict(self, copy=False):
"""Convert the struct to a dictionary.
Expand Down
118 changes: 83 additions & 35 deletions src/tests.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,55 @@
from collections import OrderedDict

import six
import pickle
import unittest

from basicstruct import BasicStruct


class Foo(BasicStruct):
class SimpleBasicStruct(BasicStruct):
__slots__ = ('x', 'y')


class MyTestCase(unittest.TestCase):
class BasicStructWithUnorderedDefaultValues(BasicStruct):
__slots__ = {'x': 5, 'y': True}


class BasicStructWithOrderedDefaultValues(BasicStruct):
__slots__ = OrderedDict((('x', 5), ('y', True)))


class BasicStructTest(unittest.TestCase):
def test_attribute_access(self):
f = Foo(2, 'blah')
self.assertEqual(f.x, 2)
self.assertEqual(f.y, 'blah')
bs = SimpleBasicStruct(2, 'blah')
self.assertEqual(bs.x, 2)
self.assertEqual(bs.y, 'blah')

def test_attribute_access_with_kwargs(self):
f = Foo(x=2, y='blah')
self.assertEqual(f.x, 2)
self.assertEqual(f.y, 'blah')
bs = SimpleBasicStruct(x=2, y='blah')
self.assertEqual(bs.x, 2)
self.assertEqual(bs.y, 'blah')

def test_attribute_access_partly_kwargs(self):
f = Foo(2, y='blah')
self.assertEqual(f.x, 2)
self.assertEqual(f.y, 'blah')
bs = SimpleBasicStruct(2, y='blah')
self.assertEqual(bs.x, 2)
self.assertEqual(bs.y, 'blah')

def test_attribute_access_missing_values(self):
f = Foo(2)
self.assertEqual(f.x, 2)
self.assertEqual(f.y, None)
bs = SimpleBasicStruct(2)
self.assertEqual(bs.x, 2)
self.assertEqual(bs.y, None)

def test_attribute_access_missing_values_partial_kwargs(self):
f = Foo(y=2)
self.assertEqual(f.x, None)
self.assertEqual(f.y, 2)
bs = SimpleBasicStruct(y=2)
self.assertEqual(bs.x, None)
self.assertEqual(bs.y, 2)

def test_comparisons(self):
small = Foo(1, 'irrelevant')
medium = Foo(2, 5)
another_medium = Foo(2, 5)
large = Foo(2, 15)
small = SimpleBasicStruct(1, 'irrelevant')
medium = SimpleBasicStruct(2, 5)
another_medium = SimpleBasicStruct(2, 5)
large = SimpleBasicStruct(2, 15)

self.assertEqual(medium, another_medium)

Expand Down Expand Up @@ -87,42 +97,80 @@ def test_comparisons(self):
small >= 1

def test_repr(self):
f = Foo(1, 'irrelevant')
self.assertEqual(repr(f), "Foo(x=1, y='irrelevant')")
bs = SimpleBasicStruct(1, 'irrelevant')
self.assertEqual(repr(bs), "SimpleBasicStruct(x=1, y='irrelevant')")

def test_pickle(self):
f = Foo(1, 'irrelevant')
self.assertEqual(f, pickle.loads(pickle.dumps(f)))
bs = SimpleBasicStruct(1, 'irrelevant')
self.assertEqual(bs, pickle.loads(pickle.dumps(bs)))

def test_hash(self):
small = Foo(1, 'irrelevant')
medium = Foo(2, 5)
another_medium = Foo(2, 5)
large = Foo(2, 15)
small = SimpleBasicStruct(1, 'irrelevant')
medium = SimpleBasicStruct(2, 5)
another_medium = SimpleBasicStruct(2, 5)
large = SimpleBasicStruct(2, 15)

self.assertNotEqual(hash(small), hash(medium))
self.assertNotEqual(hash(medium), hash(large))
self.assertEqual(hash(medium), hash(another_medium))

def test_to_dict(self):
f = Foo(1, 2)
d1 = f.to_dict()
d2 = dict(f)
bs = SimpleBasicStruct(1, 2)
d1 = bs.to_dict()
d2 = dict(bs)
expected = {'x': 1, 'y': 2}

self.assertEqual(d1, expected)
self.assertEqual(d2, expected)

def test_to_dict_copy(self):
l = []
f = Foo(1, l)
d1 = f.to_dict()
d2 = f.to_dict(copy=True)
bs = SimpleBasicStruct(1, l)
d1 = bs.to_dict()
d2 = bs.to_dict(copy=True)
l.append(1)

self.assertEqual(d1, {'x': 1, 'y': [1]})
self.assertEqual(d2, {'x': 1, 'y': []})

def test_unordered_default_values(self):
bs = BasicStructWithUnorderedDefaultValues()
self.assertEqual(bs.x, 5)
self.assertEqual(bs.y, True)

def test_unordered_partial_default_values_keyword_args(self):
bs = BasicStructWithUnorderedDefaultValues(x=0)
self.assertEqual(bs.x, 0)
self.assertEqual(bs.y, True)

def test_unordered_default_values_non_keyword_arg(self):
with self.assertRaises(ValueError):
BasicStructWithUnorderedDefaultValues(0)

with self.assertRaises(ValueError):
BasicStructWithUnorderedDefaultValues(False, x=0)

def test_ordered_default_values(self):
bs = BasicStructWithOrderedDefaultValues()
self.assertEqual(bs.x, 5)
self.assertEqual(bs.y, True)

def test_ordered_partial_default_values_keyword_args(self):
bs = BasicStructWithOrderedDefaultValues(x=0)
self.assertEqual(bs.x, 0)
self.assertEqual(bs.y, True)

def test_ordered_partial_default_values(self):
bs = BasicStructWithOrderedDefaultValues(0)
self.assertEqual(bs.x, 0)
self.assertEqual(bs.y, True)

def test_ordered_partial_default_values_with_kwargs(self):
bs = BasicStructWithOrderedDefaultValues(y=False)
self.assertEqual(bs.x, 5)
self.assertEqual(bs.y, False)


if __name__ == '__main__':
unittest.main()

0 comments on commit 04a79ad

Please sign in to comment.