Coverage for python/lsst/daf/butler/dimensions/_packer.py: 71%
33 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-07 11:04 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-07 11:04 +0000
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 software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28from __future__ import annotations
30__all__ = ("DimensionPacker",)
32from abc import ABCMeta, abstractmethod
33from typing import TYPE_CHECKING, Any
35from ._coordinate import DataCoordinate, DataId
36from ._graph import DimensionGraph, DimensionGroup
38if TYPE_CHECKING: # Imports needed only for type annotations; may be circular.
39 from ._universe import DimensionUniverse
42class DimensionPacker(metaclass=ABCMeta):
43 """Class for going from `DataCoordinate` to packed integer ID and back.
45 An abstract base class for bidirectional mappings between a
46 `DataCoordinate` and a packed integer ID.
48 Parameters
49 ----------
50 fixed : `DataCoordinate`
51 Expanded data ID for the dimensions whose values must remain fixed
52 (to these values) in all calls to `pack`, and are used in the results
53 of calls to `unpack`. Subclasses may ignore particular dimensions, and
54 are permitted to require that ``fixed.hasRecords()`` return `True`.
55 dimensions : `DimensionGroup` or `DimensionGraph`
56 The dimensions of data IDs packed by this instance. Only
57 `DimensionGroup` will be supported after v27.
58 """
60 def __init__(self, fixed: DataCoordinate, dimensions: DimensionGroup | DimensionGraph):
61 self.fixed = fixed
62 self._dimensions = self.fixed.universe.conform(dimensions)
64 @property
65 def universe(self) -> DimensionUniverse:
66 """Graph containing all known dimensions (`DimensionUniverse`)."""
67 return self.fixed.universe
69 @property
70 def dimensions(self) -> DimensionGraph:
71 """The dimensions of data IDs packed by this instance
72 (`DimensionGraph`).
74 After v27 this will be a `DimensionGroup`.
75 """
76 return self._dimensions._as_graph()
78 @property
79 @abstractmethod
80 def maxBits(self) -> int:
81 """Return The maximum number of nonzero bits in the packed ID.
83 This packed ID will be returned by
84 `~DimensionPacker.pack` (`int`).
86 Must be implemented by all concrete derived classes. May return
87 `None` to indicate that there is no maximum.
88 """
89 raise NotImplementedError()
91 @abstractmethod
92 def _pack(self, dataId: DataCoordinate) -> int:
93 """Abstract implementation for `~DimensionPacker.pack`.
95 Must be implemented by all concrete derived classes.
97 Parameters
98 ----------
99 dataId : `DataCoordinate`
100 Dictionary-like object identifying (at least) all packed
101 dimensions associated with this packer. Guaranteed to be a true
102 `DataCoordinate`, not an informal data ID
104 Returns
105 -------
106 packed : `int`
107 Packed integer ID.
108 """
109 raise NotImplementedError()
111 def pack(
112 self, dataId: DataId | None = None, *, returnMaxBits: bool = False, **kwargs: Any
113 ) -> tuple[int, int] | int:
114 """Pack the given data ID into a single integer.
116 Parameters
117 ----------
118 dataId : `DataId`
119 Data ID to pack. Values for any keys also present in the "fixed"
120 data ID passed at construction must be the same as the values
121 passed at construction, but in general you must still specify
122 those keys.
123 returnMaxBits : `bool`
124 If `True`, return a tuple of ``(packed, self.maxBits)``.
125 **kwargs
126 Additional keyword arguments are treated like additional key-value
127 pairs in ``dataId``.
129 Returns
130 -------
131 packed : `int`
132 Packed integer ID.
133 maxBits : `int`, optional
134 Maximum number of nonzero bits in ``packed``. Not returned unless
135 ``returnMaxBits`` is `True`.
137 Notes
138 -----
139 Should not be overridden by derived class
140 (`~DimensionPacker._pack` should be overridden instead).
141 """
142 dataId = DataCoordinate.standardize(
143 dataId, **kwargs, universe=self.fixed.universe, defaults=self.fixed
144 )
145 if dataId.subset(self.fixed.dimensions) != self.fixed:
146 raise ValueError(f"Data ID packer expected a data ID consistent with {self.fixed}, got {dataId}.")
147 packed = self._pack(dataId)
148 if returnMaxBits:
149 return packed, self.maxBits
150 else:
151 return packed
153 @abstractmethod
154 def unpack(self, packedId: int) -> DataCoordinate:
155 """Unpack an ID produced by `pack` into a full `DataCoordinate`.
157 Must be implemented by all concrete derived classes.
159 Parameters
160 ----------
161 packedId : `int`
162 The result of a call to `~DimensionPacker.pack` on either
163 ``self`` or an identically-constructed packer instance.
165 Returns
166 -------
167 dataId : `DataCoordinate`
168 Dictionary-like ID that uniquely identifies all covered
169 dimensions.
170 """
171 raise NotImplementedError()
173 # Class attributes below are shadowed by instance attributes, and are
174 # present just to hold the docstrings for those instance attributes.
176 fixed: DataCoordinate
177 """The dimensions provided to the packer at construction
178 (`DataCoordinate`)
180 The packed ID values are only unique and reversible with these
181 dimensions held fixed.
182 """