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

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/>. 

27 

28from __future__ import annotations 

29 

30__all__ = ("DimensionPacker",) 

31 

32from abc import ABCMeta, abstractmethod 

33from typing import TYPE_CHECKING, Any 

34 

35from ._coordinate import DataCoordinate, DataId 

36from ._group import DimensionGroup 

37 

38if TYPE_CHECKING: # Imports needed only for type annotations; may be circular. 

39 from ._universe import DimensionUniverse 

40 

41 

42class DimensionPacker(metaclass=ABCMeta): 

43 """Class for going from `DataCoordinate` to packed integer ID and back. 

44 

45 An abstract base class for bidirectional mappings between a 

46 `DataCoordinate` and a packed integer ID. 

47 

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 """ 

58 

59 def __init__(self, fixed: DataCoordinate, dimensions: DimensionGroup): 

60 self.fixed = fixed 

61 self._dimensions = self.fixed.universe.conform(dimensions) 

62 

63 @property 

64 def universe(self) -> DimensionUniverse: 

65 """Graph containing all known dimensions (`DimensionUniverse`).""" 

66 return self.fixed.universe 

67 

68 @property 

69 def dimensions(self) -> DimensionGroup: 

70 """The dimensions of data IDs packed by this instance 

71 (`DimensionGroup`). 

72 """ 

73 return self._dimensions 

74 

75 @property 

76 @abstractmethod 

77 def maxBits(self) -> int: 

78 """Return The maximum number of nonzero bits in the packed ID. 

79 

80 This packed ID will be returned by 

81 `~DimensionPacker.pack` (`int`). 

82 

83 Must be implemented by all concrete derived classes. May return 

84 `None` to indicate that there is no maximum. 

85 """ 

86 raise NotImplementedError() 

87 

88 @abstractmethod 

89 def _pack(self, dataId: DataCoordinate) -> int: 

90 """Abstract implementation for `~DimensionPacker.pack`. 

91 

92 Must be implemented by all concrete derived classes. 

93 

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 

100 

101 Returns 

102 ------- 

103 packed : `int` 

104 Packed integer ID. 

105 """ 

106 raise NotImplementedError() 

107 

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. 

112 

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``. 

125 

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`. 

133 

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 

149 

150 @abstractmethod 

151 def unpack(self, packedId: int) -> DataCoordinate: 

152 """Unpack an ID produced by `pack` into a full `DataCoordinate`. 

153 

154 Must be implemented by all concrete derived classes. 

155 

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. 

161 

162 Returns 

163 ------- 

164 dataId : `DataCoordinate` 

165 Dictionary-like ID that uniquely identifies all covered 

166 dimensions. 

167 """ 

168 raise NotImplementedError() 

169 

170 # Class attributes below are shadowed by instance attributes, and are 

171 # present just to hold the docstrings for those instance attributes. 

172 

173 fixed: DataCoordinate 

174 """The dimensions provided to the packer at construction 

175 (`DataCoordinate`) 

176 

177 The packed ID values are only unique and reversible with these 

178 dimensions held fixed. 

179 """