Coverage for python/lsst/daf/butler/core/dimensions/construction.py: 43%

Shortcuts 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

49 statements  

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

21 

22from __future__ import annotations 

23 

24from abc import ABC, abstractmethod 

25from typing import TYPE_CHECKING, AbstractSet, Dict, Iterable 

26 

27from .._topology import TopologicalFamily, TopologicalSpace 

28from ..named import NamedValueSet 

29 

30if TYPE_CHECKING: 30 ↛ 31line 30 didn't jump to line 31, because the condition on line 30 was never true

31 from ._config import DimensionConfig 

32 from ._elements import Dimension, DimensionElement 

33 from ._packer import DimensionPackerFactory 

34 

35 

36class DimensionConstructionVisitor(ABC): 

37 """For adding entities to a builder class. 

38 

39 An abstract base class for adding one or more entities to a 

40 `DimensionConstructionBuilder`. 

41 

42 Parameters 

43 ---------- 

44 name : `str` 

45 Name of an entity being added. This must be unique across all 

46 entities, which include `DimensionElement`, `TopologicalFamily`, and 

47 `DimensionPackerFactory` objects. The visitor may add other entities 

48 as well, as long as only the named entity is referenced by other 

49 entities in the universe. 

50 """ 

51 

52 def __init__(self, name: str): 

53 self.name = name 

54 

55 def __str__(self) -> str: 

56 return self.name 

57 

58 @abstractmethod 

59 def hasDependenciesIn(self, others: AbstractSet[str]) -> bool: 

60 """Test if dependencies have already been constructed. 

61 

62 Tests whether other entities this visitor depends on have already 

63 been constructed. 

64 

65 Parameters 

66 ---------- 

67 others : `AbstractSet` [ `str` ] 

68 The names of other visitors that have not yet been invoked. 

69 

70 Returns 

71 ------- 

72 blocked : `bool` 

73 If `True`, this visitor has dependencies on other visitors that 

74 must be invoked before this one can be. If `False`, `visit` may 

75 be called. 

76 """ 

77 raise NotImplementedError() 

78 

79 @abstractmethod 

80 def visit(self, builder: DimensionConstructionBuilder) -> None: 

81 """Modify the given builder object to include responsible entities. 

82 

83 Parameters 

84 ---------- 

85 builder : `DimensionConstructionBuilder` 

86 Builder to modify in-place and from which dependencies can be 

87 obtained. 

88 

89 Notes 

90 ----- 

91 Subclasses may assume (and callers must guarantee) that 

92 `hasDependenciesIn` would return `False` prior to `visit` being 

93 called. 

94 """ 

95 raise NotImplementedError() 

96 

97 

98class DimensionConstructionBuilder: 

99 """A builder object for constructing `DimensionUniverse` instances. 

100 

101 `DimensionConstructionVisitor` objects can be added to a 

102 `DimensionConstructionBuilder` object in any order, and are invoked 

103 in a deterministic order consistent with their dependency relationships 

104 by a single call (by the `DimensionUniverse`) to the `finish` method. 

105 

106 Parameters 

107 ---------- 

108 version : `int` 

109 Version for the `DimensionUniverse`. 

110 commonSkyPixName : `str` 

111 Name of the "common" skypix dimension that is used to relate all other 

112 spatial `TopologicalRelationshipEndpoint` objects. 

113 visitors : `Iterable` [ `DimensionConstructionVisitor` ] 

114 Visitor instances to include from the start. 

115 """ 

116 

117 def __init__( 

118 self, 

119 version: int, 

120 commonSkyPixName: str, 

121 config: DimensionConfig, 

122 *, 

123 visitors: Iterable[DimensionConstructionVisitor] = (), 

124 ) -> None: 

125 self.dimensions = NamedValueSet() 

126 self.elements = NamedValueSet() 

127 self.topology = {space: NamedValueSet() for space in TopologicalSpace.__members__.values()} 

128 self.packers = {} 

129 self.version = version 

130 self.config = config 

131 self.commonSkyPixName = commonSkyPixName 

132 self._todo: Dict[str, DimensionConstructionVisitor] = {v.name: v for v in visitors} 

133 

134 def add(self, visitor: DimensionConstructionVisitor) -> None: 

135 """Add a single visitor to the builder. 

136 

137 Parameters 

138 ---------- 

139 visitor : `DimensionConstructionVisitor` 

140 Visitor instance to add. 

141 """ 

142 self._todo[visitor.name] = visitor 

143 

144 def update(self, visitors: Iterable[DimensionConstructionVisitor]) -> None: 

145 """Add multiple visitors to the builder. 

146 

147 Parameters 

148 ---------- 

149 visitors : `Iterable` [ `DimensionConstructionVisitor` ] 

150 Visitor instances to add. 

151 """ 

152 self._todo.update((v.name, v) for v in visitors) 

153 

154 def finish(self) -> None: 

155 """Complete construction of the builder. 

156 

157 This method invokes all visitors in an order consistent with their 

158 dependencies, fully populating all public attributes. It should be 

159 called only once, by `DimensionUniverse` itself. 

160 """ 

161 while self._todo: 

162 unblocked = [ 

163 name 

164 for name, visitor in self._todo.items() 

165 if not visitor.hasDependenciesIn(self._todo.keys()) 

166 ] 

167 unblocked.sort() # Break ties lexicographically. 

168 if not unblocked: 

169 raise RuntimeError(f"Cycle or unmet dependency in dimension elements: {self._todo.keys()}.") 

170 for name in unblocked: 

171 self._todo.pop(name).visit(self) 

172 

173 version: int 

174 """Version number for the `DimensionUniverse` (`int`). 

175 

176 Populated at builder construction. 

177 """ 

178 

179 commonSkyPixName: str 

180 """Name of the common skypix dimension used to connect other spatial 

181 `TopologicalRelationshipEndpoint` objects (`str`). 

182 

183 Populated at builder construction. 

184 """ 

185 

186 dimensions: NamedValueSet[Dimension] 

187 """Set of all `Dimension` objects (`NamedValueSet` [ `Dimension` ]). 

188 

189 Populated by `finish`. 

190 """ 

191 

192 elements: NamedValueSet[DimensionElement] 

193 """Set of all `DimensionElement` objects 

194 (`NamedValueSet` [ `DimensionElement` ]). 

195 

196 Populated by `finish`. `DimensionConstructionVisitor` classes that 

197 construct `Dimension` objects are responsible for adding them to this 

198 set as well as `dimensions`. 

199 """ 

200 

201 topology: Dict[TopologicalSpace, NamedValueSet[TopologicalFamily]] 

202 """Dictionary containing all `TopologicalFamily` objects 

203 (`dict` [ `TopologicalSpace`, `NamedValueSet` [ `TopologicalFamily` ] ] ). 

204 """ 

205 

206 packers: Dict[str, DimensionPackerFactory] 

207 """Dictionary containing all `DimensionPackerFactory` objects 

208 (`dict` [ `str`, `DimensionPackerFactory` ] ). 

209 """