Coverage for tests / test_wrappers.py: 12%
348 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-28 08:31 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-28 08:31 +0000
1# This file is part of utils.
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
24import numpy as np
26import lsst.utils
27import lsst.utils.tests
30class MockClass: # continued class needs to be at module scope
31 """A test class that can be continued."""
33 def method1(self):
34 return self
36 @classmethod
37 def method2(cls):
38 return cls
40 @staticmethod
41 def method3():
42 return True
44 @property
45 def property1(self):
46 return False
49class DecoratorsTestCase(lsst.utils.tests.TestCase):
50 """Test the decorators."""
52 def setUp(self):
53 @lsst.utils.continueClass
54 class MockClass:
55 def method1a(self):
56 return self
58 @classmethod
59 def method2a(cls):
60 return cls
62 @staticmethod
63 def method3a():
64 return True
66 @property
67 def property1a(self):
68 return False
70 @lsst.utils.inClass(MockClass)
71 def method1b(self):
72 return self
74 @lsst.utils.inClass(MockClass)
75 @classmethod
76 def method2b(cls):
77 return cls
79 @lsst.utils.inClass(MockClass)
80 @staticmethod
81 def method3b():
82 return True
84 @lsst.utils.inClass(MockClass)
85 @property
86 def property1b(self):
87 return False
89 def testAttributeCopying(self):
90 x = MockClass()
91 self.assertIs(x.method1(), x)
92 self.assertIs(x.method1a(), x)
93 self.assertIs(x.method1b(), x)
94 self.assertIs(x.method2(), MockClass)
95 self.assertIs(x.method2a(), MockClass)
96 self.assertIs(x.method2b(), MockClass)
97 self.assertIs(MockClass.method2(), MockClass)
98 self.assertIs(MockClass.method2a(), MockClass)
99 self.assertIs(MockClass.method2b(), MockClass)
100 self.assertTrue(x.method3())
101 self.assertTrue(x.method3a())
102 self.assertTrue(x.method3b())
103 self.assertTrue(MockClass.method3())
104 self.assertTrue(MockClass.method3a())
105 self.assertTrue(MockClass.method3b())
106 self.assertFalse(x.property1)
107 self.assertFalse(x.property1a)
108 self.assertFalse(x.property1b)
111class TemplateMetaSimpleTestCase(lsst.utils.tests.TestCase):
112 """Test TemplateMeta on a mockup of a template with a single dtype
113 template parameter.
114 """
116 def setUp(self):
117 class Example(metaclass=lsst.utils.TemplateMeta):
118 def method1(self):
119 return self
121 @classmethod
122 def method2(cls):
123 return cls
125 @staticmethod
126 def method3():
127 return True
129 @property
130 def property1(self):
131 return False
133 class ExampleF:
134 pass
136 class ExampleD:
137 pass
139 self.Example = Example
140 self.ExampleF = ExampleF
141 self.ExampleD = ExampleD
143 def register(self):
144 self.Example.register(np.float32, self.ExampleF)
145 self.Example.register(np.float64, self.ExampleD)
147 def alias(self):
148 self.Example.alias("F", self.ExampleF)
149 self.Example.alias("D", self.ExampleD)
151 def testCorrectRegistration(self):
152 self.register()
153 self.assertEqual(self.ExampleF.dtype, np.float32)
154 self.assertEqual(self.ExampleD.dtype, np.float64)
155 self.assertIn(np.float32, self.Example)
156 self.assertIn(np.float64, self.Example)
157 self.assertEqual(self.Example[np.float32], self.ExampleF)
158 self.assertEqual(self.Example[np.float64], self.ExampleD)
160 def testAliases(self):
161 self.register()
162 self.alias()
163 self.assertEqual(self.ExampleF.dtype, np.float32)
164 self.assertEqual(self.ExampleD.dtype, np.float64)
165 self.assertIn("F", self.Example)
166 self.assertIn("D", self.Example)
167 self.assertEqual(self.Example["F"], self.ExampleF)
168 self.assertEqual(self.Example["D"], self.ExampleD)
169 self.assertEqual(self.Example["F"], self.Example[np.float32])
170 self.assertEqual(self.Example["D"], self.Example[np.float64])
172 def testInheritanceHooks(self):
173 self.register()
174 self.assertTrue(issubclass(self.ExampleF, self.Example))
175 self.assertTrue(issubclass(self.ExampleD, self.Example))
176 f = self.ExampleF()
177 d = self.ExampleD()
178 self.assertIsInstance(f, self.Example)
179 self.assertIsInstance(d, self.Example)
180 self.assertEqual(set(self.Example.__subclasses__()), {self.ExampleF, self.ExampleD})
182 # To test fallback code path, ensure that there are multiple
183 # examples to check.
184 class ExampleSub(self.ExampleD):
185 # A subclass that is not itself registered.
186 pass
188 class Example2(metaclass=lsst.utils.TemplateMeta):
189 # A new independent class.
190 pass
192 class Example2I:
193 # Something that will be registered in independent hierarchy.
194 pass
196 Example2.register(np.int32, Example2I)
198 sub = ExampleSub()
199 self.assertIsInstance(sub, self.Example)
200 self.assertNotIsInstance(sub, Example2)
201 self.assertTrue(issubclass(ExampleSub, self.Example))
202 self.assertFalse(issubclass(ExampleSub, Example2))
204 def testConstruction(self):
205 self.register()
206 f1 = self.Example(dtype=np.float32)
207 # Test that numpy dtype objects resolve to their underlying type
208 f2 = self.Example(dtype=np.dtype(np.float32))
209 for f in (f1, f2):
210 self.assertIsInstance(f, self.Example)
211 self.assertIsInstance(f, self.ExampleF)
212 self.assertNotIsInstance(f, self.ExampleD)
214 with self.assertRaises(TypeError):
215 self.Example()
216 with self.assertRaises(TypeError):
217 self.Example(dtype=np.int32)
219 def testAttributeCopying(self):
220 self.register()
221 f = self.ExampleF()
222 d = self.ExampleD()
223 self.assertIs(f.method1(), f)
224 self.assertIs(d.method1(), d)
225 self.assertIs(f.method2(), self.ExampleF)
226 self.assertIs(d.method2(), self.ExampleD)
227 self.assertIs(self.ExampleF.method2(), self.ExampleF)
228 self.assertIs(self.ExampleD.method2(), self.ExampleD)
229 self.assertTrue(f.method3())
230 self.assertTrue(d.method3())
231 self.assertTrue(self.ExampleF.method3())
232 self.assertTrue(self.ExampleD.method3())
233 self.assertFalse(f.property1)
234 self.assertFalse(d.property1)
236 def testDictBehavior(self):
237 self.register()
238 self.assertIn(np.float32, self.Example)
239 self.assertEqual(self.Example[np.float32], self.ExampleF)
240 self.assertEqual(set(self.Example.keys()), {np.float32, np.float64})
241 self.assertEqual(set(self.Example.values()), {self.ExampleF, self.ExampleD})
242 self.assertEqual(
243 set(self.Example.items()), {(np.float32, self.ExampleF), (np.float64, self.ExampleD)}
244 )
245 self.assertEqual(len(self.Example), 2)
246 self.assertEqual(set(iter(self.Example)), {np.float32, np.float64})
247 self.assertEqual(self.Example.get(np.float64), self.ExampleD)
248 self.assertEqual(self.Example.get(np.int32, False), False)
250 def testNoInheritedDictBehavior(self):
251 self.register()
252 f = self.ExampleF()
253 with self.assertRaises(TypeError):
254 len(f)
255 with self.assertRaises(TypeError):
256 f["F"]
257 with self.assertRaises(TypeError):
258 for _ in f:
259 pass
260 with self.assertRaises(TypeError):
261 len(self.ExampleF)
262 with self.assertRaises(TypeError):
263 self.ExampleF["F"]
264 with self.assertRaises(TypeError):
265 for _ in self.ExampleF:
266 pass
268 def testAliasUnregistered(self):
269 with self.assertRaises(ValueError):
270 self.Example.alias("F", self.ExampleF)
271 self.assertEqual(len(self.Example), 0)
272 self.assertEqual(len(self.Example), 0)
274 def testRegisterDTypeTwice(self):
275 with self.assertRaises(KeyError):
276 self.Example.register("F", self.ExampleF)
277 self.Example.register("F", self.ExampleD)
278 self.assertEqual(len(self.Example), 1)
280 def testRegisterTemplateTwice(self):
281 with self.assertRaises(ValueError):
282 self.Example.register("F", self.ExampleF)
283 self.Example.register("D", self.ExampleF)
284 self.assertEqual(len(self.Example), 1)
287class TemplateMetaHardTestCase(lsst.utils.tests.TestCase):
288 """Test TemplateMeta with a mockup of a template with multiple
289 template parameters.
290 """
292 def setUp(self):
293 class Example(metaclass=lsst.utils.TemplateMeta):
294 TEMPLATE_PARAMS = ("d", "u")
295 TEMPLATE_DEFAULTS = (2, None)
297 class Example2F:
298 pass
300 class Example2D:
301 pass
303 class Example3F:
304 pass
306 class Example3D:
307 pass
309 self.Example = Example
310 self.Example2F = Example2F
311 self.Example2D = Example2D
312 self.Example3F = Example3F
313 self.Example3D = Example3D
315 def register(self):
316 self.Example.register((2, np.float32), self.Example2F)
317 self.Example.register((2, np.float64), self.Example2D)
318 self.Example.register((3, np.float32), self.Example3F)
319 self.Example.register((3, np.float64), self.Example3D)
321 def alias(self):
322 self.Example.alias("2F", self.Example2F)
323 self.Example.alias("2D", self.Example2D)
324 self.Example.alias("3F", self.Example3F)
325 self.Example.alias("3D", self.Example3D)
327 def testCorrectRegistration(self):
328 self.register()
329 self.assertEqual(self.Example2F.d, 2)
330 self.assertEqual(self.Example2F.u, np.float32)
331 self.assertEqual(self.Example2D.d, 2)
332 self.assertEqual(self.Example2D.u, np.float64)
333 self.assertEqual(self.Example3F.d, 3)
334 self.assertEqual(self.Example3F.u, np.float32)
335 self.assertEqual(self.Example3D.d, 3)
336 self.assertEqual(self.Example3D.u, np.float64)
337 self.assertIn((2, np.float32), self.Example)
338 self.assertIn((2, np.float64), self.Example)
339 self.assertIn((3, np.float32), self.Example)
340 self.assertIn((3, np.float64), self.Example)
341 self.assertEqual(self.Example[2, np.float32], self.Example2F)
342 self.assertEqual(self.Example[2, np.float64], self.Example2D)
343 self.assertEqual(self.Example[3, np.float32], self.Example3F)
344 self.assertEqual(self.Example[3, np.float64], self.Example3D)
346 def testAliases(self):
347 self.register()
348 self.alias()
349 self.assertEqual(self.Example2F.d, 2)
350 self.assertEqual(self.Example2F.u, np.float32)
351 self.assertEqual(self.Example2D.d, 2)
352 self.assertEqual(self.Example2D.u, np.float64)
353 self.assertEqual(self.Example3F.d, 3)
354 self.assertEqual(self.Example3F.u, np.float32)
355 self.assertEqual(self.Example3D.d, 3)
356 self.assertEqual(self.Example3D.u, np.float64)
357 self.assertIn("2F", self.Example)
358 self.assertIn("2D", self.Example)
359 self.assertIn("3F", self.Example)
360 self.assertIn("3D", self.Example)
361 self.assertEqual(self.Example["2F"], self.Example2F)
362 self.assertEqual(self.Example["2D"], self.Example2D)
363 self.assertEqual(self.Example["3F"], self.Example3F)
364 self.assertEqual(self.Example["3D"], self.Example3D)
366 def testInheritanceHooks(self):
367 self.register()
368 self.assertTrue(issubclass(self.Example2F, self.Example))
369 self.assertTrue(issubclass(self.Example3D, self.Example))
370 f = self.Example2F()
371 d = self.Example3D()
372 self.assertIsInstance(f, self.Example)
373 self.assertIsInstance(d, self.Example)
374 self.assertEqual(
375 set(self.Example.__subclasses__()),
376 {self.Example2F, self.Example2D, self.Example3F, self.Example3D},
377 )
379 def testConstruction(self):
380 self.register()
381 f = self.Example(u=np.float32)
382 self.assertIsInstance(f, self.Example)
383 self.assertIsInstance(f, self.Example2F)
384 with self.assertRaises(TypeError):
385 self.Example()
386 with self.assertRaises(TypeError):
387 self.Example(u=np.int32, d=1)
389 def testDictBehavior(self):
390 self.register()
391 self.assertIn((2, np.float32), self.Example)
392 self.assertEqual(self.Example[2, np.float32], self.Example2F)
393 self.assertEqual(
394 set(self.Example.keys()),
395 {(2, np.float32), (2, np.float64), (3, np.float32), (3, np.float64)},
396 )
397 self.assertEqual(
398 set(self.Example.values()), {self.Example2F, self.Example2D, self.Example3F, self.Example3D}
399 )
400 self.assertEqual(
401 set(self.Example.items()),
402 {
403 ((2, np.float32), self.Example2F),
404 ((2, np.float64), self.Example2D),
405 ((3, np.float32), self.Example3F),
406 ((3, np.float64), self.Example3D),
407 },
408 )
409 self.assertEqual(len(self.Example), 4)
410 self.assertEqual(
411 set(iter(self.Example)), {(2, np.float32), (2, np.float64), (3, np.float32), (3, np.float64)}
412 )
413 self.assertEqual(self.Example.get((3, np.float64)), self.Example3D)
414 self.assertEqual(self.Example.get((2, np.int32), False), False)
416 def testRegisterBadKey(self):
417 with self.assertRaises(ValueError):
418 self.Example.register("F", self.Example2F)
420 def testRegisterDTypeTwice(self):
421 with self.assertRaises(KeyError):
422 self.Example.register((2, "F"), self.Example2F)
423 self.Example.register((2, "F"), self.Example2D)
424 self.assertEqual(len(self.Example), 1)
426 def testRegisterTemplateTwice(self):
427 with self.assertRaises(ValueError):
428 self.Example.register((2, "F"), self.Example2F)
429 self.Example.register((2, "D"), self.Example2F)
430 self.assertEqual(len(self.Example), 1)
433class TestDefaultMethodCopying(lsst.utils.tests.TestCase):
434 """Test to determine if static and class methods from a class which is
435 registered as a default type in a type ABC are properly copied.
436 """
438 def setUp(self):
439 class Example(metaclass=lsst.utils.TemplateMeta):
440 TEMPLATE_PARAMS = ("dtype",)
441 TEMPLATE_DEFAULTS = (np.float32,)
443 class ExampleF:
444 @staticmethod
445 def staticCall():
446 return 6
448 @classmethod
449 def classCall(cls):
450 return cls
452 def regularCall(self):
453 return self
455 class ExampleI:
456 @staticmethod
457 def notTransferedStaticCall():
458 return 8
460 @classmethod
461 def notTransferedClassCall(cls):
462 return cls
464 # Add in a built in function to ExampleF to mimic how pybind11 treats
465 # static methods from c++.
466 ExampleF.pow = pow
468 Example.register(np.float32, ExampleF)
469 Example.register(np.int32, ExampleI)
470 self.Example = Example
471 self.ExampleF = ExampleF
472 self.ExampleI = ExampleI
474 def testMethodCopyForDefaultType(self):
475 # Check that the methods for the default type were transfered and that
476 # the regular method was not
477 self.assertTrue(hasattr(self.Example, "staticCall"))
478 self.assertTrue(hasattr(self.Example, "pow"))
479 self.assertTrue(hasattr(self.Example, "classCall"))
480 self.assertFalse(hasattr(self.Example, "regularCall"))
482 # Verify the default static and class method defaults return the
483 # correct values
484 self.assertEqual(self.Example.staticCall(), 6)
485 self.assertEqual(self.Example.pow(2, 2), 4)
486 self.assertIs(self.Example.classCall(), self.ExampleF)
488 # Verify static and class methods for non default keys are not
489 # transfered
490 self.assertFalse(hasattr(self.Example, "notTransferedStaticCall"))
491 self.assertFalse(hasattr(self.Example, "notTransferedClassCall"))
494if __name__ == "__main__":
495 unittest.main()