Skip to content

Commit

Permalink
Group supports +(plus) operator
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelchen committed Jun 4, 2019
1 parent 15ffeb9 commit 3922d9e
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 26 deletions.
4 changes: 4 additions & 0 deletions RELEASENOTE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

# v1.1.6

* Group supports +(plus) operator

# v1.1.5

* Fix issue of creating single option group as class attribute
Expand Down
32 changes: 26 additions & 6 deletions optenum/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
class OptionGroup(list):

def __init__(self, *args):

# to ensure args are in list, consider the following
#
# class Foo(Options):
Expand All @@ -23,18 +24,37 @@ def __init__(self, *args):
# G2 = OptionGroup(B) # should get args [ (2, 'B is 2') ]
#

n = len(args)
if n == 0:
super(OptionGroup, self).__init__()
else:
super(OptionGroup, self).__init__(args)
super(OptionGroup, self).__init__()
for arg in args:
if isinstance(arg, OptionGroup):
for a in arg:
if a not in self:
self.append(a)
else:
if arg not in self:
self.append(arg)

def add(self, opt):
super(OptionGroup, self).append(opt)
if isinstance(opt, Option):
super(OptionGroup, self).append(opt)
else:
raise TypeError('Only "%s" can be added into "%s". "%s" is "%s".'
% (Option.__name__, OptionGroup.__name__, opt, type(opt)))

def remove(self, opt):
super(OptionGroup, self).remove(opt)

def __add__(self, other):
if isinstance(other, OptionGroup):
r = OptionGroup(*super(OptionGroup, self).__add__(other))
return r
elif isinstance(other, Option):
r = OptionGroup(*super(OptionGroup, self).__add__([other]))
return r
else:
raise TypeError('Can not add "%s"<%s> to %s. Please explicitly convert it to "%s" or "%s"'
% (other, type(other).__name__, OptionGroup.__name__, Option.__name__, OptionGroup.__name__))


class OptionsMeta(type):

Expand Down
2 changes: 1 addition & 1 deletion optenum/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
""" version file """

__version__ = '1.1.5'
__version__ = '1.1.6'
52 changes: 33 additions & 19 deletions tests/test_option.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class Favorite(object):

class TestOption(unittest.TestCase):

def raiseAssert(self, e):
raise AssertionError('Should raise %s' % e)

# def test_not_defined(self):
# self.assertIsInstance(Option.NOT_DEFINED, Option)
# self.assertIs(Option.NOT_DEFINED.code, None)
Expand All @@ -53,25 +56,12 @@ def test_option(self):
self.assertIsInstance(opt, Option)
self.assertTrue(issubclass(type(opt), Option))

# def test_option_tags(self):
# opt = Option(1, 'FOO', tags=['foo', 'Bar_1', 'ba1z', 'QUX'])
# self.assertIn('foo', opt.tags)
# self.assertIn('Bar_1', opt.tags)
# self.assertIn('ba1z', opt.tags)
# self.assertIn('QUX', opt.tags)
#
# def _test_tag_name(tag):
# return Option(1, 'FOO', tags=[tag, ])
#
# self.assertRaises(ValueError, _test_tag_name, '1a')
# self.assertRaises(ValueError, _test_tag_name, 'a-b')
# self.assertRaises(ValueError, _test_tag_name, ' a')
# self.assertRaises(ValueError, _test_tag_name, 'a b')
# self.assertRaises(ValueError, _test_tag_name, 'ab ')
# self.assertRaises(ValueError, _test_tag_name, 'a.b')
# self.assertRaises(ValueError, _test_tag_name, '')
# self.assertRaises(ValueError, _test_tag_name, '_foo')
# self.assertRaises(ValueError, _test_tag_name, '_BAR')
def test_option_tags(self):
opt = Option(1, 'FOO', tags=['FOO', 'BAR_1', 'BA1Z', 'QUX'])
self.assertIn('FOO', opt.tags)
self.assertIn('BAR_1', opt.tags)
self.assertIn('BA1Z', opt.tags)
self.assertIn('QUX', opt.tags)

def test_get_text(self):
opt = Option(code=1, name='OPT')
Expand Down Expand Up @@ -136,10 +126,12 @@ def test_op_lt(self):
self.assertLess(Ball.BASKETBALL, 'T')
try:
self.assertLess(Ball.FOOTBALL, Fruit.BANANA)
self.raiseAssert((TypeError, AssertionError))
except Exception as e:
self.assertIsInstance(e, (TypeError, AssertionError))
try:
self.assertLess(Ball.FOOTBALL, 2)
self.raiseAssert((TypeError, AssertionError))
except Exception as e:
self.assertIsInstance(e, (TypeError, AssertionError))
# self.assertRaises(TypeError, Ball.FOOTBALL.__lt__, Fruit.BANANA)
Expand All @@ -154,10 +146,12 @@ def test_op_le(self):

try:
self.assertLessEqual(Ball.FOOTBALL, Fruit.BANANA)
self.raiseAssert((TypeError, AssertionError))
except Exception as e:
self.assertIsInstance(e, (TypeError, AssertionError))
try:
self.assertLessEqual(Ball.FOOTBALL, 2)
self.raiseAssert((TypeError, AssertionError))
except Exception as e:
self.assertIsInstance(e, (TypeError, AssertionError))

Expand All @@ -173,10 +167,17 @@ def test_op_gt(self):

try:
self.assertGreater(Ball.FOOTBALL, Fruit.BANANA)
if six.PY3:
# Only for PY3. Because comparision between str and int is available in Python 2.7
self.raiseAssert(TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)

try:
self.assertGreater(Ball.FOOTBALL, 2)
if six.PY3:
# Only for PY3. Because comparision between str and int is available in Python 2.7
self.raiseAssert(TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)

Expand All @@ -192,11 +193,13 @@ def test_op_ge(self):

try:
self.assertGreaterEqual(Ball.FOOTBALL, Fruit.BANANA)
self.raiseAssert((TypeError, AssertionError))
except Exception as e:
self.assertIsInstance(e, (TypeError, AssertionError))

try:
self.assertGreaterEqual(Ball.FOOTBALL, 2.1)
self.raiseAssert((TypeError, AssertionError))
except Exception as e:
self.assertIsInstance(e, (TypeError, AssertionError))

Expand All @@ -210,6 +213,7 @@ def test_op_neg(self):
self.assertEqual(- Fruit.PEAR, 1)
try:
-Ball.BASKETBALL
self.assertIsInstance(e, TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)
# self.assertRaises(TypeError, Ball.BASKETBALL.__neg__)
Expand All @@ -220,6 +224,7 @@ def test_op_pos(self):
self.assertEqual(+ Fruit.APPLE, Fruit.APPLE)
try:
+Ball.BASKETBALL
self.assertIsInstance(e, TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)
# self.assertRaises(TypeError, Ball.BASKETBALL.__pos__)
Expand Down Expand Up @@ -247,6 +252,7 @@ def test_op_invert(self):
self.assertEqual(~ Fruit.PEAR, 0)
try:
~ Fruit.MONGO
self.assertIsInstance(e, TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)
self.assertRaises(ValueError, float, Ball.BASKETBALL)
Expand Down Expand Up @@ -296,10 +302,12 @@ def test_op_div(self):

try:
Ball.BASKETBALL / 2
self.assertIsInstance(e, TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)
try:
Fruit.APPLE / 0
self.assertIsInstance(e, ZeroDivisionError)
except Exception as e:
self.assertIsInstance(e, ZeroDivisionError)

Expand All @@ -317,14 +325,17 @@ def test_op_div(self):

try:
Ball.BASKETBALL // 2
self.assertIsInstance(e, TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)
try:
Fruit.APPLE // 0
self.assertIsInstance(e, ZeroDivisionError)
except Exception as e:
self.assertIsInstance(e, ZeroDivisionError)
try:
3 // Fruit.WATERMELON
self.assertIsInstance(e, ZeroDivisionError)
except Exception as e:
self.assertIsInstance(e, ZeroDivisionError)
# self.assertRaises(TypeError, Ball.BASKETBALL.__floordiv__, *(2, ))
Expand All @@ -345,14 +356,17 @@ def test_op_divmod(self):
self.assertEqual(divmod(Fruit.BANANA, 0.5), (6.0, 0.0))
try:
divmod(Fruit.BANANA, '2')
self.assertIsInstance(e, TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)
try:
divmod(Fruit.APPLE, 0)
self.assertIsInstance(e, ZeroDivisionError)
except Exception as e:
self.assertIsInstance(e, ZeroDivisionError)
try:
divmod(3, Fruit.WATERMELON)
self.assertIsInstance(e, ZeroDivisionError)
except Exception as e:
self.assertIsInstance(e, ZeroDivisionError)
# self.assertRaises(TypeError, divmod, *(Fruit.BANANA, 0.5))
Expand Down
70 changes: 70 additions & 0 deletions tests/test_options_customized.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def baz(self):

class TestCustomizedOptions(unittest.TestCase):

def raiseAssert(self, e):
raise AssertionError('Should raise %s' % e)

def test_enum(self):
self.assertEqual(Fruit.APPLE, 1)
self.assertEqual(Fruit.APPLE.name, 'APPLE')
Expand All @@ -77,6 +80,8 @@ class MyOptions(Options):
foo = 1
Bar = 2
BAZ_1 = 3

self.raiseAssert(AttributeError)
except Exception as e:
self.assertIsInstance(e, AttributeError)

Expand All @@ -85,6 +90,7 @@ def test_diff_attr_option_name(self):
class MyOptions(Options):
FOO = 1
BAR = Option(code=2, name='Bar') # name must be 'BAR'
self.raiseAssert(ValueError)
except Exception as e:
self.assertIsInstance(e, ValueError)

Expand All @@ -93,13 +99,16 @@ def test_duplicated_options(self):
class MyOptions(Options):
FOO = 1
FOO = Option(code=2, name='Bar') # duplicated name

self.raiseAssert(ValueError)
except Exception as e:
self.assertIsInstance(e, ValueError)

try:
class MyOptions(Options):
FOO = 1
BAR = Option(code=1, name='Bar') # duplicated code
self.raiseAssert(ValueError)
except Exception as e:
self.assertIsInstance(e, ValueError)

Expand Down Expand Up @@ -201,6 +210,7 @@ def test_ignore_invalid_name(self):
try:
class Bar(Options):
Invalid= (1, 2)
self.raiseAssert(AttributeError)
except Exception as e:
self.assertIsInstance(e, AttributeError)

Expand Down Expand Up @@ -269,6 +279,66 @@ class Foo(Options):
self.assertEqual(Foo.G5, (Foo.E, ))
self.assertEqual(Foo.G6, (Foo.F, ))

def test_group_plus(self):

class Foo(Options):
A = 1
B = 2, 'B is 2'
C = 'C', 'C is letter', ['FOO']
D = Option('d', name='D')
E = Option('e1', 'E', 'Earth')
F = Option('15', 'F', 'Finish', ['FOO'])

G1 = G(A, B)
G2 = G(B, C)
G3 = G(C, D)
G4 = G(D, E, F)

GA = G1 + G2
GB = G1 + G3
GC = G2 + G4
# GD = G1 + G2 + A
# GD = G(G(G1 + G2), G(G3 + B))
GE = G2 + G3 + G(E)
GF = G(GA + G3 + F)

self.assertEqual(Foo.GA, (Foo.A, Foo.B, Foo.C))
self.assertEqual(Foo.GB, (Foo.A, Foo.B, Foo.C, Foo.D))
self.assertEqual(Foo.GC, (Foo.B, Foo.C, Foo.D, Foo.E, Foo.F))
self.assertEqual(Foo.GE, (Foo.B, Foo.C, Foo.D, Foo.E))
self.assertEqual(Foo.GF, (Foo.A, Foo.B, Foo.C, Foo.D, Foo.F))

try:
class Bar(Options):
A = 1
B = 2, 'B is 2'
C = 'C', 'C is letter', ['FOO']

G1 = G(A, B)
G2 = G(B, C)

GD = G1 + G2 + A

self.raiseAssert(TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)

try:
class Baz(Options):
A = 1
B = 2, 'B is 2'
C = 'C', 'C is letter', ['FOO']

G1 = G(A, B)
G2 = G(B, C)
G3 = G(C)

GD = G(G(G1 + G2), G(G3 + B))

self.raiseAssert(TypeError)
except Exception as e:
self.assertIsInstance(e, TypeError)


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

0 comments on commit 3922d9e

Please sign in to comment.