Coverage for python/lsst/daf/butler/registry/summaries.py : 35%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
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/>.
21from __future__ import annotations
23__all__ = (
24 "CollectionSummary",
25 "GovernorDimensionRestriction",
26)
28from dataclasses import dataclass
29import itertools
30from typing import (
31 AbstractSet,
32 Any,
33 ItemsView,
34 Iterable,
35 Iterator,
36 Mapping,
37 Set,
38 Union,
39 ValuesView,
40)
42from ..core import (
43 DataCoordinate,
44 DatasetType,
45 DimensionUniverse,
46 GovernorDimension,
47 NamedKeyDict,
48 NamedKeyMapping,
49 NamedValueAbstractSet,
50 NamedValueSet,
51)
52from ..core.utils import iterable
55class GovernorDimensionRestriction(NamedKeyMapping[GovernorDimension, AbstractSet[str]]):
56 """A custom mapping that represents a restriction on the values one or
57 more governor dimensions may take in some context.
59 Parameters
60 ----------
61 mapping : `NamedKeyDict` [ `GovernorDimension`, `Set` [ `str` ]]
62 Mapping from governor dimension to the values it may take. Dimensions
63 not present in the mapping are not constrained at all.
64 """
65 def __init__(self, mapping: NamedKeyDict[GovernorDimension, Set[str]]):
66 self._mapping = mapping
68 @classmethod
69 def makeEmpty(cls, universe: DimensionUniverse) -> GovernorDimensionRestriction:
70 """Construct a `GovernorDimensionRestriction` that allows no values
71 for any governor dimension in the given `DimensionUniverse`.
73 Parameters
74 ----------
75 universe : `DimensionUniverse`
76 Object that manages all dimensions.
78 Returns
79 -------
80 restriction : `GovernorDimensionRestriction`
81 Restriction instance that maps all governor dimensions to an empty
82 set.
83 """
84 return cls(NamedKeyDict((k, set()) for k in universe.getGovernorDimensions()))
86 @classmethod
87 def makeFull(cls) -> GovernorDimensionRestriction:
88 """Construct a `GovernorDimensionRestriction` that allows any value
89 for any governor dimension.
91 Returns
92 -------
93 restriction : `GovernorDimensionRestriction`
94 Restriction instance that contains no keys, and hence contains
95 allows any value for any governor dimension.
96 """
97 return cls(NamedKeyDict())
99 def __eq__(self, other: Any) -> bool:
100 if not isinstance(other, GovernorDimensionRestriction):
101 return False
102 return self._mapping == other._mapping
104 def __str__(self) -> str:
105 return "({})".format(
106 ", ".join(f"{dimension.name}: {values}" for dimension, values in self._mapping.items())
107 )
109 def __repr__(self) -> str:
110 return "GovernorDimensionRestriction({})".format(
111 ", ".join(f"{dimension.name}={values}" for dimension, values in self._mapping.items())
112 )
114 def __iter__(self) -> Iterator[GovernorDimension]:
115 return iter(self._mapping)
117 def __len__(self) -> int:
118 return len(self._mapping)
120 @property
121 def names(self) -> AbstractSet[str]:
122 # Docstring inherited.
123 return self._mapping.names
125 def keys(self) -> NamedValueAbstractSet[GovernorDimension]:
126 return self._mapping.keys()
128 def values(self) -> ValuesView[AbstractSet[str]]:
129 return self._mapping.values()
131 def items(self) -> ItemsView[GovernorDimension, AbstractSet[str]]:
132 return self._mapping.items()
134 def __getitem__(self, key: Union[str, GovernorDimension]) -> AbstractSet[str]:
135 return self._mapping[key]
137 def copy(self) -> GovernorDimensionRestriction:
138 """Return a deep copy of this object.
140 Returns
141 -------
142 copy : `GovernorDimensionRestriction`
143 A copy of ``self`` that can be modified without modifying ``self``
144 at all.
145 """
146 return GovernorDimensionRestriction(NamedKeyDict((k, set(v)) for k, v in self.items()))
148 def add(self, dimension: GovernorDimension, value: str) -> None:
149 """Add a single dimension value to the restriction.
151 Parameters
152 ----------
153 dimension : `GovernorDimension`
154 Dimension to update.
155 value : `str`
156 Value to allow for this dimension.
157 """
158 current = self._mapping.get(dimension)
159 if current is not None:
160 current.add(value)
162 def update(self, other: Mapping[GovernorDimension, Union[str, Iterable[str]]]) -> None:
163 """Update ``self`` to include all dimension values in either ``self``
164 or ``other``.
166 Parameters
167 ----------
168 other : `Mapping` [ `Dimension`, `str` or `Iterable` [ `str` ] ]
169 Mapping to union into ``self``. This may be another
170 `GovernorDimensionRestriction` or any other mapping from dimension
171 to `str` or iterable of `str`.
172 """
173 for dimension in (self.keys() - other.keys()):
174 self._mapping.pop(dimension, None)
175 for dimension in (self.keys() & other.keys()):
176 self._mapping[dimension].update(iterable(other[dimension]))
177 # Dimensions that are in 'other' but not in 'self' are ignored, because
178 # 'self' says they are already unconstrained.
180 def union(self, *others: Mapping[GovernorDimension, Union[str, Iterable[str]]]
181 ) -> GovernorDimensionRestriction:
182 """Construct a restriction that permits any values permitted by any of
183 the input restrictions.
185 Parameters
186 ----------
187 *others : `Mapping` [ `Dimension`, `str` or `Iterable` [ `str` ] ]
188 Mappings to union into ``self``. These may be other
189 `GovernorDimensionRestriction` instances or any other kind of
190 mapping from dimension to `str` or iterable of `str`.
192 Returns
193 -------
194 unioned : `GovernorDimensionRestriction`
195 New restriction object that represents the union of ``self`` with
196 ``others``.
197 """
198 result = self.copy()
199 for other in others:
200 result.update(other)
201 return result
203 def intersection_update(self, other: Mapping[GovernorDimension, Union[str, Iterable[str]]]) -> None:
204 """Update ``self`` to include only dimension values in both ``self``
205 and ``other``.
207 Parameters
208 ----------
209 other : `Mapping` [ `Dimension`, `str` or `Iterable` [ `str` ] ]
210 Mapping to intersect into ``self``. This may be another
211 `GovernorDimensionRestriction` or any other mapping from dimension
212 to `str` or iterable of `str`.
213 """
214 for dimension, values in other.items():
215 self._mapping.setdefault(dimension, set()).intersection_update(iterable(values))
217 def intersection(self, *others: Mapping[GovernorDimension, Union[str, Iterable[str]]]
218 ) -> GovernorDimensionRestriction:
219 """Construct a restriction that permits only values permitted by all of
220 the input restrictions.
222 Parameters
223 ----------
224 *others : `Mapping` [ `Dimension`, `str` or `Iterable` [ `str` ] ]
225 Mappings to intersect with ``self``. These may be other
226 `GovernorDimensionRestriction` instances or any other kind of
227 mapping from dimension to `str` or iterable of `str`.
228 Returns
229 -------
230 intersection : `GovernorDimensionRestriction`
231 New restriction object that represents the intersection of ``self``
232 with ``others``.
233 """
234 result = self.copy()
235 for other in others:
236 result.intersection_update(other)
237 return result
239 def update_extract(self, data_id: DataCoordinate) -> None:
240 """Update ``self`` to include all governor dimension values in the
241 given data ID (in addition to those already in ``self``).
243 Parameters
244 ----------
245 data_id : `DataCoordinate`
246 Data ID from which governor dimension values should be extracted.
247 Values for non-governor dimensions are ignored.
248 """
249 for dimension in data_id.graph.governors:
250 current = self._mapping.get(dimension)
251 if current is not None:
252 current.add(data_id[dimension])
255@dataclass
256class CollectionSummary:
257 """A summary of the datasets that can be found in a collection.
258 """
260 @classmethod
261 def makeEmpty(cls, universe: DimensionUniverse) -> CollectionSummary:
262 """Construct a `CollectionSummary` for a collection with no
263 datasets.
265 Parameters
266 ----------
267 universe : `DimensionUniverse`
268 Object that manages all dimensions.
270 Returns
271 -------
272 summary : `CollectionSummary`
273 Summary object with no dataset types and no governor dimension
274 values.
275 """
276 return cls(
277 datasetTypes=NamedValueSet(),
278 dimensions=GovernorDimensionRestriction.makeEmpty(universe),
279 )
281 def copy(self) -> CollectionSummary:
282 """Return a deep copy of this object.
284 Returns
285 -------
286 copy : `CollectionSummary`
287 A copy of ``self`` that can be modified without modifying ``self``
288 at all.
289 """
290 return CollectionSummary(datasetTypes=self.datasetTypes.copy(), dimensions=self.dimensions.copy())
292 def union(self, *others: CollectionSummary) -> CollectionSummary:
293 """Construct a summary that contains all dataset types and governor
294 dimension values in any of the inputs.
296 Parameters
297 ----------
298 *others : `CollectionSummary`
299 Restrictions to combine with ``self``.
301 Returns
302 -------
303 unioned : `CollectionSummary`
304 New summary object that represents the union of ``self`` with
305 ``others``.
306 """
307 if not others:
308 return self
309 datasetTypes = NamedValueSet(self.datasetTypes)
310 datasetTypes.update(itertools.chain.from_iterable(o.datasetTypes for o in others))
311 dimensions = self.dimensions.union(*[o.dimensions for o in others])
312 return CollectionSummary(datasetTypes, dimensions)
314 datasetTypes: NamedValueSet[DatasetType]
315 """Dataset types that may be present in the collection
316 (`NamedValueSet` [ `DatasetType` ]).
318 A dataset type not in this set is definitely not in the collection, but
319 the converse is not necessarily true.
320 """
322 dimensions: GovernorDimensionRestriction
323 """Governor dimension values that may be present in the collection
324 (`GovernorDimensionRestriction`).
326 A dimension value not in this restriction is definitely not in the
327 collection, but the converse is not necessarily true.
328 """