Coverage for python / lsst / daf / butler / dimensions / _packer.py: 59%
33 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:37 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:37 +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 ._group import 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`
56 The dimensions of data IDs packed by this instance.
57 """
59 def __init__(self, fixed: DataCoordinate, dimensions: DimensionGroup):
60 self.fixed = fixed
61 self._dimensions = self.fixed.universe.conform(dimensions)
63 @property
64 def universe(self) -> DimensionUniverse:
65 """Graph containing all known dimensions (`DimensionUniverse`)."""
66 return self.fixed.universe
68 @property
69 def dimensions(self) -> DimensionGroup:
70 """The dimensions of data IDs packed by this instance
71 (`DimensionGroup`).
72 """
73 return self._dimensions
75 @property
76 @abstractmethod
77 def maxBits(self) -> int:
78 """Return The maximum number of nonzero bits in the packed ID.
80 This packed ID will be returned by
81 `~DimensionPacker.pack` (`int`).
83 Must be implemented by all concrete derived classes. May return
84 `None` to indicate that there is no maximum.
85 """
86 raise NotImplementedError()
88 @abstractmethod
89 def _pack(self, dataId: DataCoordinate) -> int:
90 """Abstract implementation for `~DimensionPacker.pack`.
92 Must be implemented by all concrete derived classes.
94 Parameters
95 ----------
96 dataId : `DataCoordinate`
97 Dictionary-like object identifying (at least) all packed
98 dimensions associated with this packer. Guaranteed to be a true
99 `DataCoordinate`, not an informal data ID
101 Returns
102 -------
103 packed : `int`
104 Packed integer ID.
105 """
106 raise NotImplementedError()
108 def pack(
109 self, dataId: DataId | None = None, *, returnMaxBits: bool = False, **kwargs: Any
110 ) -> tuple[int, int] | int:
111 """Pack the given data ID into a single integer.
113 Parameters
114 ----------
115 dataId : `DataId`
116 Data ID to pack. Values for any keys also present in the "fixed"
117 data ID passed at construction must be the same as the values
118 passed at construction, but in general you must still specify
119 those keys.
120 returnMaxBits : `bool`
121 If `True`, return a tuple of ``(packed, self.maxBits)``.
122 **kwargs
123 Additional keyword arguments are treated like additional key-value
124 pairs in ``dataId``.
126 Returns
127 -------
128 packed : `int`
129 Packed integer ID.
130 maxBits : `int`, optional
131 Maximum number of nonzero bits in ``packed``. Not returned unless
132 ``returnMaxBits`` is `True`.
134 Notes
135 -----
136 Should not be overridden by derived class
137 (`~DimensionPacker._pack` should be overridden instead).
138 """
139 dataId = DataCoordinate.standardize(
140 dataId, **kwargs, universe=self.fixed.universe, defaults=self.fixed
141 )
142 if dataId.subset(self.fixed.dimensions) != self.fixed:
143 raise ValueError(f"Data ID packer expected a data ID consistent with {self.fixed}, got {dataId}.")
144 packed = self._pack(dataId)
145 if returnMaxBits:
146 return packed, self.maxBits
147 else:
148 return packed
150 @abstractmethod
151 def unpack(self, packedId: int) -> DataCoordinate:
152 """Unpack an ID produced by `pack` into a full `DataCoordinate`.
154 Must be implemented by all concrete derived classes.
156 Parameters
157 ----------
158 packedId : `int`
159 The result of a call to `~DimensionPacker.pack` on either
160 ``self`` or an identically-constructed packer instance.
162 Returns
163 -------
164 dataId : `DataCoordinate`
165 Dictionary-like ID that uniquely identifies all covered
166 dimensions.
167 """
168 raise NotImplementedError()
170 # Class attributes below are shadowed by instance attributes, and are
171 # present just to hold the docstrings for those instance attributes.
173 fixed: DataCoordinate
174 """The dimensions provided to the packer at construction
175 (`DataCoordinate`)
177 The packed ID values are only unique and reversible with these
178 dimensions held fixed.
179 """