Coverage for python/lsst/obs/base/formatters/filter.py: 48%

46 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-06-02 03:53 -0700

1# This file is part of obs_base. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://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 <http://www.gnu.org/licenses/>. 

21 

22# TODO: remove this entire file in DM-27177 

23 

24from __future__ import annotations 

25 

26__all__ = ( 

27 "FilterFormatter", 

28 "FilterTranslator", 

29) 

30 

31from typing import Any, Optional, Type 

32 

33import yaml 

34from lsst.afw.image import Filter, FilterLabel 

35from lsst.daf.butler import StorageClassDelegate 

36from lsst.daf.butler.formatters.file import FileFormatter 

37 

38 

39class FilterFormatter(FileFormatter): 

40 """Read and write `~lsst.afw.image.Filter` filter information.""" 

41 

42 extension = ".yaml" 

43 

44 unsupportedParameters = None 

45 """This formatter does not support any parameters.""" 

46 

47 def _readFile(self, path: str, pytype: Type[Any] = None) -> Any: 

48 """Read a file from the path in YAML format. 

49 

50 Parameters 

51 ---------- 

52 path : `str` 

53 Path to use to open the file. 

54 pytype : `class`, optional 

55 The type expected to be returned. 

56 

57 Returns 

58 ------- 

59 data : `object` 

60 Either data as Python object read from YAML file, or None 

61 if the file could not be opened. 

62 """ 

63 try: 

64 with open(path, "rb") as fd: 

65 data = self._fromBytes(fd.read(), pytype) 

66 except FileNotFoundError: 

67 data = None 

68 

69 return data 

70 

71 def _fromBytes(self, serializedDataset: bytes, pytype: Optional[Type[Any]] = None) -> Any: 

72 """Read the bytes object as a python object. 

73 

74 Parameters 

75 ---------- 

76 serializedDataset : `bytes` 

77 Bytes object to unserialize. 

78 pytype : `type`, optional 

79 Expected python type to be returned. 

80 

81 Returns 

82 ------- 

83 inMemoryDataset : `lsst.afw.image.Filter` 

84 The requested data as an object. 

85 """ 

86 data = yaml.load(serializedDataset, Loader=yaml.SafeLoader) 

87 

88 if pytype is None: 

89 pytype = Filter 

90 

91 # This will be a simple dict so we need to convert it to 

92 # the Filter type -- just needs the name 

93 filter = pytype(data["canonicalName"], force=True) 

94 

95 return filter 

96 

97 def _writeFile(self, inMemoryDataset: Any) -> None: 

98 """Write the in memory dataset to file on disk. 

99 

100 Parameters 

101 ---------- 

102 inMemoryDataset : `lsst.afw.image.Filter` 

103 Filter to serialize. 

104 

105 Raises 

106 ------ 

107 Exception 

108 Raised if the file could not be written or the dataset could not be 

109 serialized. 

110 """ 

111 with open(self.fileDescriptor.location.path, "wb") as fd: 

112 fd.write(self._toBytes(inMemoryDataset)) 

113 

114 def _toBytes(self, inMemoryDataset: Any) -> bytes: 

115 """Write the in memory dataset to a bytestring. 

116 

117 Parameters 

118 ---------- 

119 inMemoryDataset : `lsst.afw.image.Filter` 

120 Object to serialize. 

121 

122 Returns 

123 ------- 

124 serializedDataset : `bytes` 

125 YAML string encoded to bytes. 

126 

127 Raises 

128 ------ 

129 Exception 

130 Raised if the object could not be serialized. 

131 """ 

132 

133 # Convert the Filter to a dict for dumping 

134 # Given the singleton situation, only the name is really 

135 # needed but it does not hurt to put some detail in the file 

136 # to aid debugging. 

137 filter = {} 

138 filter["canonicalName"] = inMemoryDataset.getCanonicalName() 

139 filter["name"] = inMemoryDataset.getName() 

140 filter["aliases"] = inMemoryDataset.getAliases() 

141 

142 return yaml.dump(filter).encode() 

143 

144 

145class FilterTranslator(StorageClassDelegate): 

146 """Derived-component converter for a Filter that has been stored as 

147 a FilterLabel. 

148 """ 

149 

150 # More complex than a Formatter that can read both Filter and FilterLabel, 

151 # but can be phased out once Filter is gone without breaking compatibility 

152 # with old FilterLabels. 

153 

154 def getComponent(self, label, derivedName): 

155 """Derive a Filter from a FilterLabel. 

156 

157 Parameters 

158 ---------- 

159 label : `~lsst.afw.image.FilterLabel` 

160 The object to convert. 

161 derivedName : `str` 

162 Name of type to convert to. Only "filter" is supported. 

163 

164 Returns 

165 ------- 

166 derived : `object` 

167 The converted type. Can be `None`. 

168 

169 Raises 

170 ------ 

171 AttributeError 

172 An unknown component was requested. 

173 """ 

174 if derivedName == "filter": 174 ↛ 194line 174 didn't jump to line 194, because the condition on line 174 was never false

175 # Port of backwards-compatibility code in afw; don't want to 

176 # expose it as API. 

177 

178 # Filters still have standard aliases, so can use almost any name 

179 # to define them. Prefer afw_name or band because that's what most 

180 # code assumes is Filter.getName(). 

181 if label == FilterLabel(band="r", physical="HSC-R2"): 181 ↛ 182line 181 didn't jump to line 182, because the condition on line 181 was never true

182 return Filter("r2", force=True) 

183 elif label == FilterLabel(band="i", physical="HSC-I2"): 183 ↛ 184line 183 didn't jump to line 184, because the condition on line 183 was never true

184 return Filter("i2", force=True) 

185 elif label == FilterLabel(physical="solid plate 0.0 0.0"): 185 ↛ 186line 185 didn't jump to line 186, because the condition on line 185 was never true

186 return Filter("SOLID", force=True) 

187 elif label.hasBandLabel(): 187 ↛ 188line 187 didn't jump to line 188, because the condition on line 187 was never true

188 return Filter(label.bandLabel, force=True) 

189 else: 

190 # FilterLabel guarantees at least one of band or physical 

191 # is defined. 

192 return Filter(label.physicalLabel, force=True) 

193 else: 

194 raise AttributeError(f"Do not know how to convert {type(label)} to {derivedName}")