Coverage for tests/test_Storable.py: 39%

135 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-15 02:24 -0700

1# This file is part of afw. 

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

21from __future__ import annotations 

22 

23from dataclasses import dataclass, asdict 

24import copy 

25import gc 

26import unittest 

27import yaml 

28 

29import lsst.utils.tests 

30 

31from lsst.afw.typehandling import Storable, StorableHelperFactory 

32import testGenericMapLib as cppLib 

33from lsst.afw.image import ExposureF, ExposureFitsReader 

34 

35 

36class DemoStorable(Storable): 

37 """Test that we can inherit from Storable in Python. 

38 """ 

39 

40 def __init__(self, state): 

41 super().__init__() 

42 self._state = state 

43 

44 def __str__(self): 

45 return f"value = {self._state}" 

46 

47 def __repr__(self): 

48 return f"DemoStorable({self._state!r})" 

49 

50 def __hash__(self): 

51 return hash(self._state) 

52 

53 def __copy__(self): 

54 return DemoStorable(self._state) 

55 

56 def __deepcopy__(self, memo=None): 

57 return DemoStorable(copy.deepcopy(self._state, memo)) 

58 

59 def __eq__(self, other): 

60 return self._state == other._state 

61 

62 

63class SpecializedStorable(cppLib.CppStorable): 

64 """Test that we can inherit from C++ subclasses of Storable that 

65 are not Storable itself. 

66 """ 

67 

68 def __repr__(self): 

69 return "Pythonic " + super().__repr__() 

70 

71 

72class PythonStorableTestSuite(lsst.utils.tests.TestCase): 

73 

74 def setUp(self): 

75 self.aList = [42] 

76 self.testbed = DemoStorable(self.aList) 

77 

78 def testCopy(self): 

79 shallow = copy.copy(self.testbed) 

80 self.assertIsNot(shallow, self.testbed) 

81 self.assertEqual(shallow, self.testbed) 

82 

83 deep = copy.deepcopy(self.testbed) 

84 self.assertIsNot(deep, self.testbed) 

85 self.assertEqual(deep, self.testbed) 

86 

87 cpp = cppLib.duplicate(self.testbed) 

88 self.assertIsInstance(cpp, Storable) 

89 self.assertIsInstance(cpp, DemoStorable) 

90 self.assertIsNot(cpp, self.testbed) 

91 self.assertEqual(cpp, self.testbed) 

92 

93 self.aList.append(43) 

94 self.assertEqual(shallow, DemoStorable([42, 43])) 

95 self.assertEqual(deep, DemoStorable([42])) 

96 self.assertEqual(cpp, DemoStorable([42])) 

97 

98 def testStr(self): 

99 self.assertEqual(str(self.testbed), "value = [42]") 

100 

101 def testRepr(self): 

102 self.assertEqual(repr(self.testbed), "DemoStorable([42])") 

103 cppLib.assertPythonStorable(self.testbed, "DemoStorable([42])") 

104 

105 def testHash(self): 

106 with self.assertRaises(TypeError): 

107 hash(self.testbed) 

108 

109 def testEq(self): 

110 self.assertEqual(self.testbed, DemoStorable([42])) 

111 self.assertNotEqual(self.testbed, DemoStorable(0)) 

112 

113 def testGarbageCollection(self): 

114 cppLib.keepStaticStorable(DemoStorable(3)) 

115 

116 gc.collect() 

117 

118 retrieved = cppLib.keepStaticStorable() 

119 self.assertIsInstance(retrieved, Storable) 

120 self.assertIsInstance(retrieved, DemoStorable) 

121 self.assertEqual(retrieved, DemoStorable(3)) 

122 

123 def testInheritedGarbageCollection(self): 

124 cppLib.keepStaticStorable(SpecializedStorable("Foo")) 

125 

126 gc.collect() 

127 

128 retrieved = cppLib.keepStaticStorable() 

129 self.assertIsInstance(retrieved, Storable) 

130 self.assertIsInstance(retrieved, cppLib.CppStorable) 

131 self.assertIsInstance(retrieved, SpecializedStorable) 

132 self.assertEqual(repr(retrieved), "Pythonic Foo") 

133 cppLib.assertPythonStorable(retrieved, "Pythonic Foo") 

134 

135 

136class CppStorableTestSuite(lsst.utils.tests.TestCase): 

137 

138 def setUp(self): 

139 self.initstr = "Just a string" 

140 self.testbed = cppLib.CppStorable(self.initstr) 

141 

142 def testNewValue(self): 

143 """Test a Python-side state change in both C++ and Python. 

144 """ 

145 self.assertEqual(self.testbed.value, self.initstr) 

146 cppLib.assertCppValue(self.testbed, self.initstr) 

147 

148 newstr = "Stringly typed" 

149 self.testbed.value = newstr 

150 

151 self.assertEqual(self.testbed.value, newstr) 

152 cppLib.assertCppValue(self.testbed, newstr) 

153 

154 

155@dataclass 

156class Blob(Storable): 

157 _persistence_name = 'Blob' 

158 _factory = StorableHelperFactory(__name__, _persistence_name) 

159 

160 an_int: int 

161 a_float: float 

162 

163 def __post_init__(self): 

164 Storable.__init__(self) 

165 

166 def isPersistable(self): 

167 return True 

168 

169 def _getPersistenceName(self): 

170 return self._persistence_name 

171 

172 def _getPythonModule(self): 

173 return __name__ 

174 

175 def _write(self): 

176 return yaml.dump(asdict(self), encoding='utf-8') 

177 

178 @staticmethod 

179 def _read(bytes): 

180 return Blob(**yaml.load(bytes, Loader=yaml.SafeLoader)) 

181 

182 

183class ExposureStorableBlobTestSuite(lsst.utils.tests.TestCase): 

184 def setUp(self): 

185 self.blob = Blob(1, 2.0) 

186 

187 def testClone(self): 

188 """Test copying an exposure with an attached Blob. 

189 """ 

190 im = ExposureF(10, 10) 

191 # Extra components must be ALL CAPS for fits storage. 

192 im.getInfo().setComponent("BLOB", self.blob) 

193 

194 im2 = im.clone() 

195 im3 = copy.deepcopy(im) 

196 im4 = ExposureF(im, deep=False) 

197 im5 = ExposureF(im, deep=True) 

198 

199 for i in [im2, im3, im4, im5]: 

200 self.assertEqual(i.getInfo().getComponent("BLOB"), self.blob) 

201 

202 def testPersistence(self): 

203 """Test persisting an exposure with an attached Blob. 

204 """ 

205 im = ExposureF(10, 10) 

206 # Extra components must be ALL CAPS for fits storage. 

207 im.getInfo().setComponent("BLOB", self.blob) 

208 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

209 im.writeFits(tmpFile) 

210 

211 newIm = ExposureF(tmpFile) 

212 self.assertEqual(newIm.getInfo().getComponent("BLOB"), self.blob) 

213 

214 reader = ExposureFitsReader(tmpFile) 

215 newBlob = reader.readComponent("BLOB") 

216 self.assertEqual(newBlob, self.blob) 

217 

218 

219class MemoryTester(lsst.utils.tests.MemoryTestCase): 

220 pass 

221 

222 

223def setup_module(module): 

224 lsst.utils.tests.init() 

225 

226 

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

228 lsst.utils.tests.init() 

229 unittest.main()