Coverage for python/lsst/obs/base/formatters/filter.py: 48%
46 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-27 11:20 +0000
« prev ^ index » next coverage.py v6.4, created at 2022-05-27 11:20 +0000
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/>.
22# TODO: remove this entire file in DM-27177
24from __future__ import annotations
26__all__ = (
27 "FilterFormatter",
28 "FilterTranslator",
29)
31from typing import Any, Optional, Type
33import yaml
34from lsst.afw.image import Filter, FilterLabel
35from lsst.daf.butler import StorageClassDelegate
36from lsst.daf.butler.formatters.file import FileFormatter
39class FilterFormatter(FileFormatter):
40 """Read and write `~lsst.afw.image.Filter` filter information."""
42 extension = ".yaml"
44 unsupportedParameters = None
45 """This formatter does not support any parameters."""
47 def _readFile(self, path: str, pytype: Type[Any] = None) -> Any:
48 """Read a file from the path in YAML format.
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.
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
69 return data
71 def _fromBytes(self, serializedDataset: bytes, pytype: Optional[Type[Any]] = None) -> Any:
72 """Read the bytes object as a python object.
74 Parameters
75 ----------
76 serializedDataset : `bytes`
77 Bytes object to unserialize.
78 pytype : `type`, optional
79 Expected python type to be returned.
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)
88 if pytype is None:
89 pytype = Filter
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)
95 return filter
97 def _writeFile(self, inMemoryDataset: Any) -> None:
98 """Write the in memory dataset to file on disk.
100 Parameters
101 ----------
102 inMemoryDataset : `lsst.afw.image.Filter`
103 Filter to serialize.
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))
114 def _toBytes(self, inMemoryDataset: Any) -> bytes:
115 """Write the in memory dataset to a bytestring.
117 Parameters
118 ----------
119 inMemoryDataset : `lsst.afw.image.Filter`
120 Object to serialize.
122 Returns
123 -------
124 serializedDataset : `bytes`
125 YAML string encoded to bytes.
127 Raises
128 ------
129 Exception
130 Raised if the object could not be serialized.
131 """
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()
142 return yaml.dump(filter).encode()
145class FilterTranslator(StorageClassDelegate):
146 """Derived-component converter for a Filter that has been stored as
147 a FilterLabel.
148 """
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.
154 def getComponent(self, label, derivedName):
155 """Derive a Filter from a FilterLabel.
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.
164 Returns
165 -------
166 derived : `object`
167 The converted type. Can be `None`.
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.
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}")