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

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/>. 

21 

22import unittest 

23 

24import numpy as np 

25 

26import lsst.utils 

27import lsst.utils.tests 

28 

29 

30class MockClass: # continued class needs to be at module scope 

31 """A test class that can be continued.""" 

32 

33 def method1(self): 

34 return self 

35 

36 @classmethod 

37 def method2(cls): 

38 return cls 

39 

40 @staticmethod 

41 def method3(): 

42 return True 

43 

44 @property 

45 def property1(self): 

46 return False 

47 

48 

49class DecoratorsTestCase(lsst.utils.tests.TestCase): 

50 """Test the decorators.""" 

51 

52 def setUp(self): 

53 @lsst.utils.continueClass 

54 class MockClass: 

55 def method1a(self): 

56 return self 

57 

58 @classmethod 

59 def method2a(cls): 

60 return cls 

61 

62 @staticmethod 

63 def method3a(): 

64 return True 

65 

66 @property 

67 def property1a(self): 

68 return False 

69 

70 @lsst.utils.inClass(MockClass) 

71 def method1b(self): 

72 return self 

73 

74 @lsst.utils.inClass(MockClass) 

75 @classmethod 

76 def method2b(cls): 

77 return cls 

78 

79 @lsst.utils.inClass(MockClass) 

80 @staticmethod 

81 def method3b(): 

82 return True 

83 

84 @lsst.utils.inClass(MockClass) 

85 @property 

86 def property1b(self): 

87 return False 

88 

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) 

109 

110 

111class TemplateMetaSimpleTestCase(lsst.utils.tests.TestCase): 

112 """Test TemplateMeta on a mockup of a template with a single dtype 

113 template parameter. 

114 """ 

115 

116 def setUp(self): 

117 class Example(metaclass=lsst.utils.TemplateMeta): 

118 def method1(self): 

119 return self 

120 

121 @classmethod 

122 def method2(cls): 

123 return cls 

124 

125 @staticmethod 

126 def method3(): 

127 return True 

128 

129 @property 

130 def property1(self): 

131 return False 

132 

133 class ExampleF: 

134 pass 

135 

136 class ExampleD: 

137 pass 

138 

139 self.Example = Example 

140 self.ExampleF = ExampleF 

141 self.ExampleD = ExampleD 

142 

143 def register(self): 

144 self.Example.register(np.float32, self.ExampleF) 

145 self.Example.register(np.float64, self.ExampleD) 

146 

147 def alias(self): 

148 self.Example.alias("F", self.ExampleF) 

149 self.Example.alias("D", self.ExampleD) 

150 

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) 

159 

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]) 

171 

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}) 

181 

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 

187 

188 class Example2(metaclass=lsst.utils.TemplateMeta): 

189 # A new independent class. 

190 pass 

191 

192 class Example2I: 

193 # Something that will be registered in independent hierarchy. 

194 pass 

195 

196 Example2.register(np.int32, Example2I) 

197 

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)) 

203 

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) 

213 

214 with self.assertRaises(TypeError): 

215 self.Example() 

216 with self.assertRaises(TypeError): 

217 self.Example(dtype=np.int32) 

218 

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) 

235 

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) 

249 

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 

267 

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) 

273 

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) 

279 

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) 

285 

286 

287class TemplateMetaHardTestCase(lsst.utils.tests.TestCase): 

288 """Test TemplateMeta with a mockup of a template with multiple 

289 template parameters. 

290 """ 

291 

292 def setUp(self): 

293 class Example(metaclass=lsst.utils.TemplateMeta): 

294 TEMPLATE_PARAMS = ("d", "u") 

295 TEMPLATE_DEFAULTS = (2, None) 

296 

297 class Example2F: 

298 pass 

299 

300 class Example2D: 

301 pass 

302 

303 class Example3F: 

304 pass 

305 

306 class Example3D: 

307 pass 

308 

309 self.Example = Example 

310 self.Example2F = Example2F 

311 self.Example2D = Example2D 

312 self.Example3F = Example3F 

313 self.Example3D = Example3D 

314 

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) 

320 

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) 

326 

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) 

345 

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) 

365 

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 ) 

378 

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) 

388 

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) 

415 

416 def testRegisterBadKey(self): 

417 with self.assertRaises(ValueError): 

418 self.Example.register("F", self.Example2F) 

419 

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) 

425 

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) 

431 

432 

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 """ 

437 

438 def setUp(self): 

439 class Example(metaclass=lsst.utils.TemplateMeta): 

440 TEMPLATE_PARAMS = ("dtype",) 

441 TEMPLATE_DEFAULTS = (np.float32,) 

442 

443 class ExampleF: 

444 @staticmethod 

445 def staticCall(): 

446 return 6 

447 

448 @classmethod 

449 def classCall(cls): 

450 return cls 

451 

452 def regularCall(self): 

453 return self 

454 

455 class ExampleI: 

456 @staticmethod 

457 def notTransferedStaticCall(): 

458 return 8 

459 

460 @classmethod 

461 def notTransferedClassCall(cls): 

462 return cls 

463 

464 # Add in a built in function to ExampleF to mimic how pybind11 treats 

465 # static methods from c++. 

466 ExampleF.pow = pow 

467 

468 Example.register(np.float32, ExampleF) 

469 Example.register(np.int32, ExampleI) 

470 self.Example = Example 

471 self.ExampleF = ExampleF 

472 self.ExampleI = ExampleI 

473 

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")) 

481 

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) 

487 

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")) 

492 

493 

494if __name__ == "__main__": 

495 unittest.main()