Coverage for tests/test_taskmetadata.py: 9%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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/>.
22import unittest
23import json
25try:
26 import numpy
27except ImportError:
28 numpy = None
30from lsst.pipe.base import TaskMetadata
33class TaskMetadataTestCase(unittest.TestCase):
35 def testTaskMetadata(self):
36 """Full test of TaskMetadata API."""
37 meta = TaskMetadata()
38 meta["test"] = 42
39 self.assertEqual(meta["test"], 42)
40 meta.add("test", 55)
41 self.assertEqual(meta["test"], 55)
42 meta.add("test", [1, 2])
43 self.assertEqual(meta.getScalar("test"), 2)
44 self.assertEqual(meta.getArray("test"), [42, 55, 1, 2])
45 meta["new.int"] = 30
46 self.assertEqual(meta["new.int"], 30)
47 self.assertEqual(meta["new"]["int"], 30)
48 self.assertEqual(meta.getArray("new.int"), [30])
49 self.assertEqual(meta.getScalar("new.int"), 30)
50 self.assertIsInstance(meta["new"], TaskMetadata)
51 self.assertIsInstance(meta.getScalar("new"), TaskMetadata)
52 self.assertIsInstance(meta.getArray("new")[0], TaskMetadata)
53 meta.add("new.int", 24)
54 self.assertEqual(meta["new.int"], 24)
55 meta["new.str"] = "str"
56 self.assertEqual(meta["new.str"], "str")
58 meta["test"] = "string"
59 self.assertEqual(meta["test"], "string")
61 self.assertIn("test", meta)
62 self.assertIn("new", meta)
63 self.assertIn("new.int", meta)
64 self.assertNotIn("new2.int", meta)
65 self.assertNotIn("test2", meta)
67 self.assertEqual(meta.paramNames(topLevelOnly=False), {"test", "new.int", "new.str"})
68 self.assertEqual(meta.paramNames(topLevelOnly=True), {"test"})
69 self.assertEqual(meta.names(topLevelOnly=False), {"test", "new", "new.int", "new.str"})
70 self.assertEqual(meta.keys(), ("test", "new"))
71 self.assertEqual(len(meta), 2)
72 self.assertEqual(len(meta["new"]), 2)
74 meta["new_array"] = ("a", "b")
75 self.assertEqual(meta["new_array"], "b")
76 self.assertEqual(meta.getArray("new_array"), ["a", "b"])
77 meta.add("new_array", "c")
78 self.assertEqual(meta["new_array"], "c")
79 self.assertEqual(meta.getArray("new_array"), ["a", "b", "c"])
80 meta["new_array"] = [1, 2, 3]
81 self.assertEqual(meta.getArray("new_array"), [1, 2, 3])
83 meta["meta"] = 5
84 meta["meta"] = TaskMetadata()
85 self.assertIsInstance(meta["meta"], TaskMetadata)
86 meta["meta.a.b"] = "deep"
87 self.assertEqual(meta["meta.a.b"], "deep")
88 self.assertIsInstance(meta["meta.a"], TaskMetadata)
90 meta.add("via_scalar", 22)
91 self.assertEqual(meta["via_scalar"], 22)
93 del meta["test"]
94 self.assertNotIn("test", meta)
95 del meta["new.int"]
96 self.assertNotIn("new.int", meta)
97 self.assertIn("new", meta)
98 with self.assertRaises(KeyError):
99 del meta["test2"]
100 with self.assertRaises(KeyError) as cm:
101 # Check that deleting a hierarchy that is not present also
102 # reports the correct key.
103 del meta["new.a.b.c"]
104 self.assertIn("new.a.b.c", str(cm.exception))
106 with self.assertRaises(KeyError) as cm:
107 # Something that doesn't exist at all.
108 meta["something.a.b"]
109 # Ensure that the full key hierarchy is reported in the error message.
110 self.assertIn("something.a.b", str(cm.exception))
112 with self.assertRaises(KeyError) as cm:
113 # Something that does exist at level 2 but not further down.
114 meta["new.str.a"]
115 # Ensure that the full key hierarchy is reported in the error message.
116 self.assertIn("new.str.a", str(cm.exception))
118 with self.assertRaises(KeyError) as cm:
119 # Something that only exists at level 1.
120 meta["new.str3"]
121 # Ensure that the full key hierarchy is reported in the error message.
122 self.assertIn("new.str3", str(cm.exception))
124 with self.assertRaises(KeyError) as cm:
125 # Something that only exists at level 1 but as an array.
126 meta.getArray("new.str3")
127 # Ensure that the full key hierarchy is reported in the error message.
128 self.assertIn("new.str3", str(cm.exception))
130 with self.assertRaises(ValueError):
131 meta.add("new", 1)
133 with self.assertRaises(KeyError):
134 meta[42]
136 with self.assertRaises(KeyError):
137 meta["not.present"]
139 with self.assertRaises(KeyError):
140 meta["not_present"]
142 with self.assertRaises(KeyError):
143 meta.getScalar("not_present")
145 with self.assertRaises(KeyError):
146 meta.getArray("not_present")
148 def testValidation(self):
149 """Test that validation works."""
150 meta = TaskMetadata()
152 class BadThing:
153 pass
155 with self.assertRaises(ValueError):
156 meta["bad"] = BadThing()
158 with self.assertRaises(ValueError):
159 meta["bad_list"] = [BadThing()]
161 meta.add("int", 4)
162 with self.assertRaises(ValueError):
163 meta.add("int", "string")
165 with self.assertRaises(ValueError):
166 meta.add("mapping", {})
168 with self.assertRaises(ValueError):
169 meta.add("int", ["string", "array"])
171 with self.assertRaises(ValueError):
172 meta["mixed"] = [1, "one"]
174 def testDict(self):
175 """Construct a TaskMetadata from a dictionary."""
176 d = {"a": "b", "c": 1, "d": [1, 2], "e": {"f": "g", "h": {"i": [3, 4]}}}
178 meta = TaskMetadata.from_dict(d)
179 self.assertEqual(meta["a"], "b")
180 self.assertEqual(meta["e.f"], "g")
181 self.assertEqual(meta.getArray("d"), [1, 2])
182 self.assertEqual(meta["e.h.i"], 4)
184 j = meta.json()
185 meta2 = TaskMetadata.parse_obj(json.loads(j))
186 self.assertEqual(meta2, meta)
188 # Round trip.
189 meta3 = TaskMetadata.from_metadata(meta)
190 self.assertEqual(meta3, meta)
192 # Add a new element that would be a single-element array.
193 # This will not equate as equal because from_metadata will move
194 # the item to the scalar part of the model and pydantic does not
195 # see them as equal.
196 meta3.add("e.new", 5)
197 meta4 = TaskMetadata.from_metadata(meta3)
198 self.assertNotEqual(meta4, meta3)
199 self.assertEqual(meta4["e.new"], meta3["e.new"])
200 del meta4["e.new"]
201 del meta3["e.new"]
202 self.assertEqual(meta4, meta3)
204 def testDeprecated(self):
205 """Test the deprecated interface issues warnings."""
206 meta = TaskMetadata.from_dict({"a": 1, "b": 2})
208 with self.assertWarns(FutureWarning):
209 meta.set("c", 3)
210 self.assertEqual(meta["c"], 3)
211 with self.assertWarns(FutureWarning):
212 self.assertEqual(meta.getAsDouble("c"), 3.0)
214 with self.assertWarns(FutureWarning):
215 meta.remove("c")
216 self.assertNotIn("c", meta)
217 with self.assertWarns(FutureWarning):
218 meta.remove("d")
220 with self.assertWarns(FutureWarning):
221 self.assertEqual(meta.names(topLevelOnly=True), set(meta.keys()))
223 @unittest.skipIf(not numpy, "Numpy is required for this test.")
224 def testNumpy(self):
225 meta = TaskMetadata()
226 meta["int"] = numpy.int64(42)
227 self.assertEqual(meta["int"], 42)
228 self.assertEqual(type(meta["int"]), int)
230 meta["float"] = numpy.float64(3.14)
231 self.assertEqual(meta["float"], 3.14)
232 self.assertEqual(type(meta["float"]), float)
234 meta.add("floatArray", [numpy.float64(1.5), numpy.float64(3.0)])
235 self.assertEqual(meta.getArray("floatArray"), [1.5, 3.0])
236 self.assertEqual(type(meta["floatArray"]), float)
238 meta.add("intArray", [numpy.int64(1), numpy.int64(3)])
239 self.assertEqual(meta.getArray("intArray"), [1, 3])
240 self.assertEqual(type(meta["intArray"]), int)
242 with self.assertRaises(ValueError):
243 meta.add("mixed", [1.5, numpy.float64(4.5)])
245 with self.assertRaises(ValueError):
246 meta["numpy"] = numpy.zeros(5)
249if __name__ == "__main__": 249 ↛ 250line 249 didn't jump to line 250, because the condition on line 249 was never true
250 unittest.main()