Coverage for tests/test_taskmetadata.py: 6%

170 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-04 02:59 -0800

1# This file is part of pipe_base. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import json 

23import unittest 

24 

25try: 

26 import numpy 

27except ImportError: 

28 numpy = None 

29 

30from lsst.pipe.base import TaskMetadata 

31 

32 

33class TaskMetadataTestCase(unittest.TestCase): 

34 def testTaskMetadata(self): 

35 """Full test of TaskMetadata API.""" 

36 meta = TaskMetadata() 

37 meta["test"] = 42 

38 self.assertEqual(meta["test"], 42) 

39 meta.add("test", 55) 

40 self.assertEqual(meta["test"], 55) 

41 meta.add("test", [1, 2]) 

42 self.assertEqual(meta.getScalar("test"), 2) 

43 self.assertEqual(meta.getArray("test"), [42, 55, 1, 2]) 

44 self.assertEqual(meta.get("test"), 2) 

45 meta["new.int"] = 30 

46 self.assertEqual(meta["new.int"], 30) 

47 self.assertEqual(meta.get("new.int", 20), 30) 

48 self.assertEqual(meta.get("not.present.at.all", 20), 20) 

49 self.assertEqual(meta["new"]["int"], 30) 

50 self.assertEqual(meta.get("new").get("int"), 30) 

51 self.assertEqual(meta.getArray("new.int"), [30]) 

52 self.assertEqual(meta.getScalar("new.int"), 30) 

53 self.assertIsInstance(meta["new"], TaskMetadata) 

54 self.assertIsInstance(meta.getScalar("new"), TaskMetadata) 

55 self.assertIsInstance(meta.getArray("new")[0], TaskMetadata) 

56 self.assertIsInstance(meta.get("new"), TaskMetadata) 

57 meta.add("new.int", 24) 

58 self.assertEqual(meta["new.int"], 24) 

59 meta["new.str"] = "str" 

60 self.assertEqual(meta["new.str"], "str") 

61 

62 meta["test"] = "string" 

63 self.assertEqual(meta["test"], "string") 

64 

65 self.assertIn("test", meta) 

66 self.assertIn("new", meta) 

67 self.assertIn("new.int", meta) 

68 self.assertNotIn("new2.int", meta) 

69 self.assertNotIn("test2", meta) 

70 

71 self.assertEqual(meta.paramNames(topLevelOnly=False), {"test", "new.int", "new.str"}) 

72 self.assertEqual(meta.paramNames(topLevelOnly=True), {"test"}) 

73 self.assertEqual(meta.names(topLevelOnly=False), {"test", "new", "new.int", "new.str"}) 

74 self.assertEqual(meta.keys(), ("test", "new")) 

75 self.assertEqual(len(meta), 2) 

76 self.assertEqual(len(meta["new"]), 2) 

77 

78 meta["new_array"] = ("a", "b") 

79 self.assertEqual(meta["new_array"], "b") 

80 self.assertEqual(meta.getArray("new_array"), ["a", "b"]) 

81 meta.add("new_array", "c") 

82 self.assertEqual(meta["new_array"], "c") 

83 self.assertEqual(meta.getArray("new_array"), ["a", "b", "c"]) 

84 meta["new_array"] = [1, 2, 3] 

85 self.assertEqual(meta.getArray("new_array"), [1, 2, 3]) 

86 

87 meta["meta"] = 5 

88 meta["meta"] = TaskMetadata() 

89 self.assertIsInstance(meta["meta"], TaskMetadata) 

90 meta["meta.a.b"] = "deep" 

91 self.assertEqual(meta["meta.a.b"], "deep") 

92 self.assertIsInstance(meta["meta.a"], TaskMetadata) 

93 

94 meta.add("via_scalar", 22) 

95 self.assertEqual(meta["via_scalar"], 22) 

96 

97 del meta["test"] 

98 self.assertNotIn("test", meta) 

99 del meta["new.int"] 

100 self.assertNotIn("new.int", meta) 

101 self.assertIn("new", meta) 

102 with self.assertRaises(KeyError): 

103 del meta["test2"] 

104 with self.assertRaises(KeyError) as cm: 

105 # Check that deleting a hierarchy that is not present also 

106 # reports the correct key. 

107 del meta["new.a.b.c"] 

108 self.assertIn("new.a.b.c", str(cm.exception)) 

109 

110 with self.assertRaises(KeyError) as cm: 

111 # Something that doesn't exist at all. 

112 meta["something.a.b"] 

113 # Ensure that the full key hierarchy is reported in the error message. 

114 self.assertIn("something.a.b", str(cm.exception)) 

115 

116 with self.assertRaises(KeyError) as cm: 

117 # Something that does exist at level 2 but not further down. 

118 meta["new.str.a"] 

119 # Ensure that the full key hierarchy is reported in the error message. 

120 self.assertIn("new.str.a", str(cm.exception)) 

121 

122 with self.assertRaises(KeyError) as cm: 

123 # Something that only exists at level 1. 

124 meta["new.str3"] 

125 # Ensure that the full key hierarchy is reported in the error message. 

126 self.assertIn("new.str3", str(cm.exception)) 

127 

128 with self.assertRaises(KeyError) as cm: 

129 # Something that only exists at level 1 but as an array. 

130 meta.getArray("new.str3") 

131 # Ensure that the full key hierarchy is reported in the error message. 

132 self.assertIn("new.str3", str(cm.exception)) 

133 

134 with self.assertRaises(ValueError): 

135 meta.add("new", 1) 

136 

137 with self.assertRaises(KeyError): 

138 meta[42] 

139 

140 with self.assertRaises(KeyError): 

141 meta["not.present"] 

142 

143 with self.assertRaises(KeyError): 

144 meta["not_present"] 

145 

146 with self.assertRaises(KeyError): 

147 meta.getScalar("not_present") 

148 

149 with self.assertRaises(KeyError): 

150 meta.getArray("not_present") 

151 

152 def testValidation(self): 

153 """Test that validation works.""" 

154 meta = TaskMetadata() 

155 

156 class BadThing: 

157 pass 

158 

159 with self.assertRaises(ValueError): 

160 meta["bad"] = BadThing() 

161 

162 with self.assertRaises(ValueError): 

163 meta["bad_list"] = [BadThing()] 

164 

165 meta.add("int", 4) 

166 with self.assertRaises(ValueError): 

167 meta.add("int", "string") 

168 

169 with self.assertRaises(ValueError): 

170 meta.add("mapping", {}) 

171 

172 with self.assertRaises(ValueError): 

173 meta.add("int", ["string", "array"]) 

174 

175 with self.assertRaises(ValueError): 

176 meta["mixed"] = [1, "one"] 

177 

178 def testDict(self): 

179 """Construct a TaskMetadata from a dictionary.""" 

180 d = {"a": "b", "c": 1, "d": [1, 2], "e": {"f": "g", "h": {"i": [3, 4]}}} 

181 

182 meta = TaskMetadata.from_dict(d) 

183 self.assertEqual(meta["a"], "b") 

184 self.assertEqual(meta["e.f"], "g") 

185 self.assertEqual(meta.getArray("d"), [1, 2]) 

186 self.assertEqual(meta["e.h.i"], 4) 

187 

188 d2 = meta.to_dict() 

189 self.assertEqual(d2, d) 

190 

191 j = meta.json() 

192 meta2 = TaskMetadata.parse_obj(json.loads(j)) 

193 self.assertEqual(meta2, meta) 

194 

195 # Round trip. 

196 meta3 = TaskMetadata.from_metadata(meta) 

197 self.assertEqual(meta3, meta) 

198 

199 # Add a new element that would be a single-element array. 

200 # This will not equate as equal because from_metadata will move 

201 # the item to the scalar part of the model and pydantic does not 

202 # see them as equal. 

203 meta3.add("e.new", 5) 

204 meta4 = TaskMetadata.from_metadata(meta3) 

205 self.assertNotEqual(meta4, meta3) 

206 self.assertEqual(meta4["e.new"], meta3["e.new"]) 

207 del meta4["e.new"] 

208 del meta3["e.new"] 

209 self.assertEqual(meta4, meta3) 

210 

211 def testDeprecated(self): 

212 """Test the deprecated interface issues warnings.""" 

213 meta = TaskMetadata.from_dict({"a": 1, "b": 2}) 

214 

215 with self.assertWarns(FutureWarning): 

216 meta.set("c", 3) 

217 self.assertEqual(meta["c"], 3) 

218 with self.assertWarns(FutureWarning): 

219 self.assertEqual(meta.getAsDouble("c"), 3.0) 

220 

221 with self.assertWarns(FutureWarning): 

222 meta.remove("c") 

223 self.assertNotIn("c", meta) 

224 with self.assertWarns(FutureWarning): 

225 meta.remove("d") 

226 

227 with self.assertWarns(FutureWarning): 

228 self.assertEqual(meta.names(topLevelOnly=True), set(meta.keys())) 

229 

230 @unittest.skipIf(not numpy, "Numpy is required for this test.") 

231 def testNumpy(self): 

232 meta = TaskMetadata() 

233 meta["int"] = numpy.int64(42) 

234 self.assertEqual(meta["int"], 42) 

235 self.assertEqual(type(meta["int"]), int) 

236 

237 meta["float"] = numpy.float64(3.14) 

238 self.assertEqual(meta["float"], 3.14) 

239 self.assertEqual(type(meta["float"]), float) 

240 

241 meta.add("floatArray", [numpy.float64(1.5), numpy.float64(3.0)]) 

242 self.assertEqual(meta.getArray("floatArray"), [1.5, 3.0]) 

243 self.assertEqual(type(meta["floatArray"]), float) 

244 

245 meta.add("intArray", [numpy.int64(1), numpy.int64(3)]) 

246 self.assertEqual(meta.getArray("intArray"), [1, 3]) 

247 self.assertEqual(type(meta["intArray"]), int) 

248 

249 with self.assertRaises(ValueError): 

250 meta.add("mixed", [1.5, numpy.float64(4.5)]) 

251 

252 with self.assertRaises(ValueError): 

253 meta["numpy"] = numpy.zeros(5) 

254 

255 

256if __name__ == "__main__": 256 ↛ 257line 256 didn't jump to line 257, because the condition on line 256 was never true

257 unittest.main()