1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
from test.test_json import PyTest
import pickle
import sys
import unittest
kepler_dict = {
"orbital_period": {
"mercury": 88,
"venus": 225,
"earth": 365,
"mars": 687,
"jupiter": 4331,
"saturn": 10_756,
"uranus": 30_687,
"neptune": 60_190,
},
"dist_from_sun": {
"mercury": 58,
"venus": 108,
"earth": 150,
"mars": 228,
"jupiter": 778,
"saturn": 1_400,
"uranus": 2_900,
"neptune": 4_500,
}
}
class TestAttrDict(PyTest):
def test_dict_subclass(self):
self.assertTrue(issubclass(self.AttrDict, dict))
def test_slots(self):
d = self.AttrDict(x=1, y=2)
with self.assertRaises(TypeError):
vars(d)
def test_constructor_signatures(self):
AttrDict = self.AttrDict
target = dict(x=1, y=2)
self.assertEqual(AttrDict(x=1, y=2), target) # kwargs
self.assertEqual(AttrDict(dict(x=1, y=2)), target) # mapping
self.assertEqual(AttrDict(dict(x=1, y=0), y=2), target) # mapping, kwargs
self.assertEqual(AttrDict([('x', 1), ('y', 2)]), target) # iterable
self.assertEqual(AttrDict([('x', 1), ('y', 0)], y=2), target) # iterable, kwargs
def test_getattr(self):
d = self.AttrDict(x=1, y=2)
self.assertEqual(d.x, 1)
with self.assertRaises(AttributeError):
d.z
def test_setattr(self):
d = self.AttrDict(x=1, y=2)
d.x = 3
d.z = 5
self.assertEqual(d, dict(x=3, y=2, z=5))
def test_delattr(self):
d = self.AttrDict(x=1, y=2)
del d.x
self.assertEqual(d, dict(y=2))
with self.assertRaises(AttributeError):
del d.z
def test_dir(self):
d = self.AttrDict(x=1, y=2)
self.assertTrue(set(dir(d)), set(dir(dict)).union({'x', 'y'}))
def test_repr(self):
# This repr is doesn't round-trip. It matches a regular dict.
# That seems to be the norm for AttrDict recipes being used
# in the wild. Also it supports the design concept that an
# AttrDict is just like a regular dict but has optional
# attribute style lookup.
self.assertEqual(repr(self.AttrDict(x=1, y=2)),
repr(dict(x=1, y=2)))
def test_overlapping_keys_and_methods(self):
d = self.AttrDict(items=50)
self.assertEqual(d['items'], 50)
self.assertEqual(d.items(), dict(d).items())
def test_invalid_attribute_names(self):
d = self.AttrDict({
'control': 'normal case',
'class': 'keyword',
'two words': 'contains space',
'hypen-ate': 'contains a hyphen'
})
self.assertEqual(d.control, dict(d)['control'])
self.assertEqual(d['class'], dict(d)['class'])
self.assertEqual(d['two words'], dict(d)['two words'])
self.assertEqual(d['hypen-ate'], dict(d)['hypen-ate'])
def test_object_hook_use_case(self):
AttrDict = self.AttrDict
json_string = self.dumps(kepler_dict)
kepler_ad = self.loads(json_string, object_hook=AttrDict)
self.assertEqual(kepler_ad, kepler_dict) # Match regular dict
self.assertIsInstance(kepler_ad, AttrDict) # Verify conversion
self.assertIsInstance(kepler_ad.orbital_period, AttrDict) # Nested
# Exercise dotted lookups
self.assertEqual(kepler_ad.orbital_period, kepler_dict['orbital_period'])
self.assertEqual(kepler_ad.orbital_period.earth,
kepler_dict['orbital_period']['earth'])
self.assertEqual(kepler_ad['orbital_period'].earth,
kepler_dict['orbital_period']['earth'])
# Dict style error handling and Attribute style error handling
with self.assertRaises(KeyError):
kepler_ad.orbital_period['pluto']
with self.assertRaises(AttributeError):
kepler_ad.orbital_period.Pluto
# Order preservation
self.assertEqual(list(kepler_ad.items()), list(kepler_dict.items()))
self.assertEqual(list(kepler_ad.orbital_period.items()),
list(kepler_dict['orbital_period'].items()))
# Round trip
self.assertEqual(self.dumps(kepler_ad), json_string)
def test_pickle(self):
AttrDict = self.AttrDict
json_string = self.dumps(kepler_dict)
kepler_ad = self.loads(json_string, object_hook=AttrDict)
# Pickling requires the cached module to be the real module
cached_module = sys.modules.get('json')
sys.modules['json'] = self.json
try:
for protocol in range(6):
kepler_ad2 = pickle.loads(pickle.dumps(kepler_ad, protocol))
self.assertEqual(kepler_ad2, kepler_ad)
self.assertEqual(type(kepler_ad2), AttrDict)
finally:
sys.modules['json'] = cached_module
if __name__ == "__main__":
unittest.main()
|