30
30
class TestBag(unittest.TestCase):
31
31
def test_simple(self):
32
# Initialize a bag; its attributes are the keywords of the ctor.
32
33
bag = Bag(a=1, b=2, c=3)
33
34
self.assertEqual(bag.a, 1)
34
35
self.assertEqual(bag.b, 2)
35
36
self.assertEqual(bag.c, 3)
37
38
def test_dash_translation(self):
39
# Dashes in keys get turned into underscore in attributes.
38
40
bag = Bag(**{'a-b': 1, 'c-d': 2, 'e-f': 3})
39
41
self.assertEqual(bag.a_b, 1)
40
42
self.assertEqual(bag.c_d, 2)
41
43
self.assertEqual(bag.e_f, 3)
43
45
def test_dash_literal_access(self):
46
# For keys with dashes, the original name is preserved in getitem.
44
47
bag = Bag(**{'a-b': 1, 'c-d': 2, 'e-f': 3})
45
48
self.assertEqual(bag['a-b'], 1)
46
49
self.assertEqual(bag['c-d'], 2)
47
50
self.assertEqual(bag['e-f'], 3)
49
52
def test_keyword_translation(self):
53
# Python keywords get a trailing underscore.
50
54
bag = Bag(**{'global': 1, 'with': 2, 'import': 3})
51
55
self.assertEqual(bag.global_, 1)
52
56
self.assertEqual(bag.with_, 2)
53
57
self.assertEqual(bag.import_, 3)
55
59
def test_repr(self):
60
# The repr of a bag includes its translated keys.
56
61
bag = Bag(**{'a-b': 1, 'global': 2, 'foo': 3})
57
62
self.assertEqual(repr(bag), '<Bag: a_b, foo, global_>')
59
64
def test_original(self):
65
# There's a magical attribute containing the original ctor arguments.
60
66
source = {'a-b': 1, 'global': 2, 'foo': 3}
61
67
bag = Bag(**source)
62
68
self.assertEqual(bag.__original__, source)
64
70
def test_add_key(self):
71
# We can add new keys/attributes via setitem.
65
72
bag = Bag(a=1, b=2, c=3)
66
73
bag['d'] = bag.b + bag.c
67
74
self.assertEqual(bag.d, 5)
69
76
def test_add_existing_key(self):
77
# A key set in the original ctor cannot be changed.
70
78
bag = Bag(a=1, b=2, c=3)
71
79
self.assertRaises(ValueError, setitem, bag, 'b', 5)
72
80
self.assertEqual(bag.b, 2)
82
def test_add_new_key(self):
83
# A key added by setitem can be changed.
84
bag = Bag(a=1, b=2, c=3)
87
self.assertEqual(bag.d, 5)
74
89
def test_pickle(self):
90
# Bags can be pickled and unpickled.
75
91
bag = Bag(a=1, b=2, c=3)
76
92
pck = pickle.dumps(bag)
77
93
new_bag = pickle.loads(pck)
78
94
self.assertEqual(new_bag.a, 1)
79
95
self.assertEqual(new_bag.b, 2)
80
96
self.assertEqual(new_bag.c, 3)
98
def test_update(self):
99
# Bags can be updated, similar to dicts.
100
bag = Bag(a=1, b=2, c=3)
102
self.assertEqual(bag.a, 1)
103
self.assertEqual(bag.b, 7)
104
self.assertEqual(bag.c, 3)
105
self.assertEqual(bag.d, 9)
107
def test_converters(self):
108
# The Bag ctor accepts a mapping of type converter functions.
109
bag = Bag(converters=dict(a=int, b=int),
111
self.assertEqual(bag.a, 1)
112
self.assertEqual(bag.b, 2)
113
self.assertEqual(bag.c, '3')
115
def test_converters_error(self):
116
# Type converter function errors get propagated.
117
converters = dict(a=int, b=int)
118
keywords = dict(a='1', b='foo', c=3)
119
self.assertRaises(ValueError, Bag, converters=converters, **keywords)
121
def test_update_converters(self):
122
# The update method also accepts converters.
123
bag = Bag(a=1, b=2, c=3)
124
bag.update(converters=dict(d=int),
126
self.assertEqual(bag.d, 4)
127
self.assertEqual(bag.e, '5')
129
def test_update_converter_overrides(self):
130
# Converters in the update method permanently override ctor converters.
131
converters = dict(a=int, b=int)
132
bag = Bag(converters=converters, a='1', b='2')
133
self.assertEqual(bag.a, 1)
134
self.assertEqual(bag.b, 2)
135
new_converters = dict(a=str)
136
bag.update(converters=new_converters, a='3', b='4')
137
self.assertEqual(bag.a, '3')
138
self.assertEqual(bag.b, 4)
139
bag.update(a='5', b='6')
140
self.assertEqual(bag.a, '5')
141
self.assertEqual(bag.b, 6)