Coverage for python/lsst/verify/spec/base.py: 44%
47 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-08 04:06 -0700
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-08 04:06 -0700
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/>.
21__all__ = ["Specification"]
23import abc
25from ..jsonmixin import JsonSerializationMixin
26from ..metaquery import MetadataQuery
27from ..naming import Name
30class Specification(JsonSerializationMixin, metaclass=abc.ABCMeta):
31 """Specification base class.
33 Specification classes must implement:
35 - `type`
36 - `_serialize_type`
37 - `check`
39 Subclasses should also call ``Specification.__init__`` to initialize
40 the specifications ``name`` attribute (a `~lsst.verify.Name`
41 instance).
42 """
44 def __init__(self, name, **kwargs):
45 # interal object behind self.tags
46 self._tags = set()
48 # name attibute must be a Name instance representing a specification
49 if not isinstance(name, Name):
50 self._name = Name(spec=name)
51 else:
52 self._name = name
53 if not self._name.is_spec:
54 message = 'name {0!r} does not represent a specification'
55 raise TypeError(message.format(self._name))
57 if 'metadata_query' in kwargs:
58 self._metadata_query = MetadataQuery(kwargs['metadata_query'])
59 else:
60 self._metadata_query = MetadataQuery()
62 if 'tags' in kwargs:
63 self.tags = kwargs['tags']
65 @property
66 def name(self):
67 """Specification name (`lsst.verify.Name`)."""
68 return self._name
70 @property
71 def metric_name(self):
72 """Name of the metric this specification corresponds to
73 (`lsst.verify.Name`)."""
74 return Name(package=self.name.package, metric=self.name.metric)
76 @property
77 def tags(self):
78 """Tag labels (`set` of `str`)."""
79 return self._tags
81 @tags.setter
82 def tags(self, t):
83 # Ensure that tags is always a set.
84 if isinstance(t, str):
85 t = [t]
86 self._tags = set(t)
88 @abc.abstractproperty
89 def type(self):
90 """Specification type (`str`)."""
91 pass
93 @abc.abstractmethod
94 def _serialize_type(self):
95 """Serialize type-specific specification data to a JSON-serializable
96 `dict`.
98 This method is used by the `json` property as the value associated
99 with the key named for `type`.
100 """
101 pass
103 @property
104 def json(self):
105 """`dict` that can be serialized as semantic JSON, compatible with
106 the SQUASH metric service.
107 """
108 return JsonSerializationMixin.jsonify_dict(
109 {
110 'name': str(self.name),
111 'type': self.type,
112 self.type: self._serialize_type(),
113 'metadata_query': self._metadata_query,
114 'tags': self.tags
115 }
116 )
118 @abc.abstractmethod
119 def check(self, measurement):
120 """Check if a measurement passes this specification.
122 Parameters
123 ----------
124 measurement : `astropy.units.Quantity`
125 The measurement value. The measurement `~astropy.units.Quantity`
126 must have units *compatible* with the specification.
128 Returns
129 -------
130 passed : `bool`
131 `True` if the measurement meets the specification,
132 `False` otherwise.
133 """
134 pass
136 def query_metadata(self, metadata, arg_driven=False):
137 """Query a Job's metadata to determine if this specification applies.
139 Parameters
140 ----------
141 metadata : `lsst.verify.Metadata` or `dict`-type
142 Metadata mapping. Typically this is the `lsst.verify.Job.meta`
143 attribute.
144 arg_driven : `bool`, optional
145 If `False` (default), ``metadata`` matches the ``MetadataQuery``
146 if ``metadata`` has all the terms defined in ``MetadataQuery``,
147 and those terms match. If ``metadata`` has more terms than
148 ``MetadataQuery``, it can still match. This behavior is
149 appropriate for finding if a specification applies to a Job
150 given metadata.
152 If `True`, the orientation of the matching is reversed. Now
153 ``metadata`` matches the ``MetadataQuery`` if ``MetadataQuery``
154 has all the terms defined in ``metadata`` and those terms match.
155 If ``MetadataQuery`` has more terms than ``metadata``, it can
156 still match. This behavior is appropriate for discovering
157 specifications.
159 Returns
160 -------
161 matched : `bool`
162 `True` if this specification matches, `False` otherwise.
164 See also
165 --------
166 lsst.verify.MetadataQuery
167 """
168 return self._metadata_query(metadata, arg_driven=arg_driven)