Coverage for python/lsst/verify/yamlpersistance.py: 42%
64 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-03 11:04 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-03 11:04 +0000
1# This file is part of verify.
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/>.
22"""Shared code for persisting verify objects to YAML.
24The YAML code is centralized in one module to simplify registration code.
25"""
27__all__ = [] # code defined by this module only called indirectly
30import astropy.units as u
31import yaml
33from .measurement import Datum, Blob, Measurement
36def _getValidLoaders():
37 """Return a list of supported YAML loaders.
39 For YAML >= 5.1 need a different Loader for the constructor
41 Returns
42 -------
43 loaderList : `sequence`
44 A list of loaders that are supported by the current PyYAML version.
45 """
46 loaderList = [yaml.Loader, yaml.CLoader]
47 try:
48 loaderList.append(yaml.FullLoader)
49 except AttributeError:
50 pass
51 try:
52 loaderList.append(yaml.UnsafeLoader)
53 except AttributeError:
54 pass
55 try:
56 loaderList.append(yaml.SafeLoader)
57 except AttributeError:
58 pass
59 return loaderList
62def _registerTypes():
63 yaml.add_representer(Datum, datum_representer)
64 yaml.add_representer(Blob, blob_representer)
65 yaml.add_representer(Measurement, measurement_representer)
67 for loader in _getValidLoaders():
68 yaml.add_constructor(
69 "lsst.verify.Datum", datum_constructor, Loader=loader)
70 yaml.add_constructor(
71 "lsst.verify.Blob", blob_constructor, Loader=loader)
72 yaml.add_constructor(
73 "lsst.verify.Measurement", measurement_constructor, Loader=loader)
76# Based on Measurement.json, but provides self-contained representation
77def measurement_representer(dumper, measurement):
78 """Persist a Measurement as a mapping.
79 """
80 if measurement.quantity is None:
81 normalized_value = None
82 normalized_unit_str = None
83 elif measurement.metric is not None:
84 # ensure metrics are normalized to metric definition's units
85 metric = measurement.metric
86 normalized_value = float(measurement.quantity.to(metric.unit).value)
87 normalized_unit_str = metric.unit_str
88 else:
89 normalized_value = float(measurement.quantity.value)
90 normalized_unit_str = str(measurement.quantity.unit)
92 return dumper.represent_mapping(
93 "lsst.verify.Measurement",
94 {"metric": str(measurement.metric_name),
95 "identifier": measurement.identifier,
96 "value": normalized_value,
97 "unit": normalized_unit_str,
98 "notes": dict(measurement.notes),
99 # extras included in blobs
100 "blobs": list(measurement.blobs.values()),
101 },
102 )
105# Based on Measurement.deserialize
106def measurement_constructor(loader, node):
107 state = loader.construct_mapping(node, deep=True)
109 quantity = u.Quantity(state["value"], u.Unit(state["unit"]))
111 instance = Measurement(
112 state["metric"],
113 quantity=quantity,
114 notes=state["notes"],
115 blobs=state["blobs"],
116 )
117 instance._id = state["identifier"] # re-wire id from serialization
118 return instance
121# Port of Blob.json to yaml
122def blob_representer(dumper, blob):
123 """Persist a Blob as a mapping.
124 """
125 return dumper.represent_mapping(
126 "lsst.verify.Blob",
127 {"identifier": blob.identifier,
128 "name": blob.name,
129 "data": blob._datums,
130 }
131 )
134# Port of Blob.deserialize to yaml
135def blob_constructor(loader, node):
136 state = loader.construct_mapping(node, deep=True)
138 data = state["data"] if state["data"] is not None else {}
139 instance = Blob(state["name"], **data)
140 instance._id = state["identifier"] # re-wire id from serialization
141 return instance
144# Port of Datum.json to yaml
145def datum_representer(dumper, datum):
146 """Persist a Datum as a mapping.
147 """
148 if datum._is_non_quantity_type(datum.quantity):
149 v = datum.quantity
150 elif len(datum.quantity.shape) > 0:
151 v = datum.quantity.value.tolist()
152 else:
153 # Some versions of astropy return numpy scalars,
154 # which pyyaml can't handle safely
155 v = float(datum.quantity.value)
157 return dumper.represent_mapping(
158 "lsst.verify.Datum",
159 {"value": v,
160 "unit": datum.unit_str,
161 "label": datum.label,
162 "description": datum.description,
163 }
164 )
167# Port of Datum.deserialize to yaml
168def datum_constructor(loader, node):
169 state = loader.construct_mapping(node, deep=True)
171 return Datum(quantity=state["value"],
172 unit=state["unit"],
173 label=state["label"],
174 description=state["description"],
175 )
178_registerTypes()