Hide keyboard shortcuts

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

21 

22from __future__ import annotations 

23 

24from abc import ABC, abstractmethod 

25from typing import ( 

26 AbstractSet, 

27 Dict, 

28 Iterable, 

29 TYPE_CHECKING, 

30) 

31 

32from ..named import NamedValueSet 

33from .._topology import TopologicalFamily, TopologicalSpace 

34 

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

36 from ._elements import Dimension, DimensionElement 

37 from ._packer import DimensionPackerFactory 

38 

39 

40class DimensionConstructionVisitor(ABC): 

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

42 

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

44 `DimensionConstructionBuilder`. 

45 

46 Parameters 

47 ---------- 

48 name : `str` 

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

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

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

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

53 entities in the universe. 

54 """ 

55 

56 def __init__(self, name: str): 

57 self.name = name 

58 

59 def __str__(self) -> str: 

60 return self.name 

61 

62 @abstractmethod 

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

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

65 

66 Tests whether other entities this visitor depends on have already 

67 been constructed. 

68 

69 Parameters 

70 ---------- 

71 others : `AbstractSet` [ `str` ] 

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

73 

74 Returns 

75 ------- 

76 blocked : `bool` 

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

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

79 be called. 

80 """ 

81 raise NotImplementedError() 

82 

83 @abstractmethod 

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

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

86 

87 Parameters 

88 ---------- 

89 builder : `DimensionConstructionBuilder` 

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

91 obtained. 

92 

93 Notes 

94 ----- 

95 Subclasses may assume (and callers must guarantee) that 

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

97 called. 

98 """ 

99 raise NotImplementedError() 

100 

101 

102class DimensionConstructionBuilder: 

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

104 

105 `DimensionConstructionVisitor` objects can be added to a 

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

107 in a deterministic order consistent with their dependency relationships 

108 by a single call (by the `DimensionUnvierse`) to the `finish` method. 

109 

110 Parameters 

111 ---------- 

112 version : `int` 

113 Version for the `DimensionUniverse`. 

114 commonSkyPixName : `str` 

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

116 spatial `TopologicalRelationshipEndpoint` objects. 

117 visitors : `Iterable` [ `DimensionConstructionVisitor` ] 

118 Visitor instances to include from the start. 

119 """ 

120 

121 def __init__( 

122 self, 

123 version: int, 

124 commonSkyPixName: str, *, 

125 visitors: Iterable[DimensionConstructionVisitor] = () 

126 ) -> None: 

127 self.dimensions = NamedValueSet() 

128 self.elements = NamedValueSet() 

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

130 self.packers = {} 

131 self.version = version 

132 self.commonSkyPixName = commonSkyPixName 

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

134 

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

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

137 

138 Parameters 

139 ---------- 

140 visitor : `DimensionConstructionVisitor` 

141 Visitor instance to add. 

142 """ 

143 self._todo[visitor.name] = visitor 

144 

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

146 """Add multiple visitors to the builder. 

147 

148 Parameters 

149 ---------- 

150 visitors : `Iterable` [ `DimensionConstructionVisitor` ] 

151 Visitor instances to add. 

152 """ 

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

154 

155 def finish(self) -> None: 

156 """Complete construction of the builder. 

157 

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

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

160 called only once, by `DimensionUniverse` itself. 

161 """ 

162 while self._todo: 

163 unblocked = [name for name, visitor in self._todo.items() 

164 if not visitor.hasDependenciesIn(self._todo.keys())] 

165 unblocked.sort() # Break ties lexicographically. 

166 if not unblocked: 

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

168 for name in unblocked: 

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

170 

171 version: int 

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

173 

174 Populated at builder construction. 

175 """ 

176 

177 commonSkyPixName: str 

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

179 `TopologicalRelationshipEndpoint` objects (`str`). 

180 

181 Populated at builder construction. 

182 """ 

183 

184 dimensions: NamedValueSet[Dimension] 

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

186 

187 Populated by `finish`. 

188 """ 

189 

190 elements: NamedValueSet[DimensionElement] 

191 """Set of all `DimensionElement` objects 

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

193 

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

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

196 set as well as `dimensions`. 

197 """ 

198 

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

200 """Dictionary containing all `TopologicalFamily` objects 

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

202 """ 

203 

204 packers: Dict[str, DimensionPackerFactory] 

205 """Dictionary containing all `DimensionPackerFactory` objects 

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

207 """