Coverage for tests/test_Storable.py: 39%
135 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-14 03:43 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-14 03:43 -0800
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
23from dataclasses import dataclass, asdict
24import copy
25import gc
26import unittest
27import yaml
29import lsst.utils.tests
31from lsst.afw.typehandling import Storable, StorableHelperFactory
32import testGenericMapLib as cppLib
33from lsst.afw.image import ExposureF, ExposureFitsReader
36class DemoStorable(Storable):
37 """Test that we can inherit from Storable in Python.
38 """
40 def __init__(self, state):
41 super().__init__()
42 self._state = state
44 def __str__(self):
45 return f"value = {self._state}"
47 def __repr__(self):
48 return f"DemoStorable({self._state!r})"
50 def __hash__(self):
51 return hash(self._state)
53 def __copy__(self):
54 return DemoStorable(self._state)
56 def __deepcopy__(self, memo=None):
57 return DemoStorable(copy.deepcopy(self._state, memo))
59 def __eq__(self, other):
60 return self._state == other._state
63class SpecializedStorable(cppLib.CppStorable):
64 """Test that we can inherit from C++ subclasses of Storable that
65 are not Storable itself.
66 """
68 def __repr__(self):
69 return "Pythonic " + super().__repr__()
72class PythonStorableTestSuite(lsst.utils.tests.TestCase):
74 def setUp(self):
75 self.aList = [42]
76 self.testbed = DemoStorable(self.aList)
78 def testCopy(self):
79 shallow = copy.copy(self.testbed)
80 self.assertIsNot(shallow, self.testbed)
81 self.assertEqual(shallow, self.testbed)
83 deep = copy.deepcopy(self.testbed)
84 self.assertIsNot(deep, self.testbed)
85 self.assertEqual(deep, self.testbed)
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)
93 self.aList.append(43)
94 self.assertEqual(shallow, DemoStorable([42, 43]))
95 self.assertEqual(deep, DemoStorable([42]))
96 self.assertEqual(cpp, DemoStorable([42]))
98 def testStr(self):
99 self.assertEqual(str(self.testbed), "value = [42]")
101 def testRepr(self):
102 self.assertEqual(repr(self.testbed), "DemoStorable([42])")
103 cppLib.assertPythonStorable(self.testbed, "DemoStorable([42])")
105 def testHash(self):
106 with self.assertRaises(TypeError):
107 hash(self.testbed)
109 def testEq(self):
110 self.assertEqual(self.testbed, DemoStorable([42]))
111 self.assertNotEqual(self.testbed, DemoStorable(0))
113 def testGarbageCollection(self):
114 cppLib.keepStaticStorable(DemoStorable(3))
116 gc.collect()
118 retrieved = cppLib.keepStaticStorable()
119 self.assertIsInstance(retrieved, Storable)
120 self.assertIsInstance(retrieved, DemoStorable)
121 self.assertEqual(retrieved, DemoStorable(3))
123 def testInheritedGarbageCollection(self):
124 cppLib.keepStaticStorable(SpecializedStorable("Foo"))
126 gc.collect()
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")
136class CppStorableTestSuite(lsst.utils.tests.TestCase):
138 def setUp(self):
139 self.initstr = "Just a string"
140 self.testbed = cppLib.CppStorable(self.initstr)
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)
148 newstr = "Stringly typed"
149 self.testbed.value = newstr
151 self.assertEqual(self.testbed.value, newstr)
152 cppLib.assertCppValue(self.testbed, newstr)
155@dataclass
156class Blob(Storable):
157 _persistence_name = 'Blob'
158 _factory = StorableHelperFactory(__name__, _persistence_name)
160 an_int: int
161 a_float: float
163 def __post_init__(self):
164 Storable.__init__(self)
166 def isPersistable(self):
167 return True
169 def _getPersistenceName(self):
170 return self._persistence_name
172 def _getPythonModule(self):
173 return __name__
175 def _write(self):
176 return yaml.dump(asdict(self), encoding='utf-8')
178 @staticmethod
179 def _read(bytes):
180 return Blob(**yaml.load(bytes, Loader=yaml.SafeLoader))
183class ExposureStorableBlobTestSuite(lsst.utils.tests.TestCase):
184 def setUp(self):
185 self.blob = Blob(1, 2.0)
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)
194 im2 = im.clone()
195 im3 = copy.deepcopy(im)
196 im4 = ExposureF(im, deep=False)
197 im5 = ExposureF(im, deep=True)
199 for i in [im2, im3, im4, im5]:
200 self.assertEqual(i.getInfo().getComponent("BLOB"), self.blob)
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)
211 newIm = ExposureF(tmpFile)
212 self.assertEqual(newIm.getInfo().getComponent("BLOB"), self.blob)
214 reader = ExposureFitsReader(tmpFile)
215 newBlob = reader.readComponent("BLOB")
216 self.assertEqual(newBlob, self.blob)
219class MemoryTester(lsst.utils.tests.MemoryTestCase):
220 pass
223def setup_module(module):
224 lsst.utils.tests.init()
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()