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 ( 

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 from ._config import DimensionConfig 

39 

40 

41class DimensionConstructionVisitor(ABC): 

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

43 

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

45 `DimensionConstructionBuilder`. 

46 

47 Parameters 

48 ---------- 

49 name : `str` 

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

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

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

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

54 entities in the universe. 

55 """ 

56 

57 def __init__(self, name: str): 

58 self.name = name 

59 

60 def __str__(self) -> str: 

61 return self.name 

62 

63 @abstractmethod 

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

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

66 

67 Tests whether other entities this visitor depends on have already 

68 been constructed. 

69 

70 Parameters 

71 ---------- 

72 others : `AbstractSet` [ `str` ] 

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

74 

75 Returns 

76 ------- 

77 blocked : `bool` 

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

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

80 be called. 

81 """ 

82 raise NotImplementedError() 

83 

84 @abstractmethod 

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

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

87 

88 Parameters 

89 ---------- 

90 builder : `DimensionConstructionBuilder` 

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

92 obtained. 

93 

94 Notes 

95 ----- 

96 Subclasses may assume (and callers must guarantee) that 

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

98 called. 

99 """ 

100 raise NotImplementedError() 

101 

102 

103class DimensionConstructionBuilder: 

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

105 

106 `DimensionConstructionVisitor` objects can be added to a 

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

108 in a deterministic order consistent with their dependency relationships 

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

110 

111 Parameters 

112 ---------- 

113 version : `int` 

114 Version for the `DimensionUniverse`. 

115 commonSkyPixName : `str` 

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

117 spatial `TopologicalRelationshipEndpoint` objects. 

118 visitors : `Iterable` [ `DimensionConstructionVisitor` ] 

119 Visitor instances to include from the start. 

120 """ 

121 

122 def __init__( 

123 self, 

124 version: int, 

125 commonSkyPixName: str, 

126 config: DimensionConfig, *, 

127 visitors: Iterable[DimensionConstructionVisitor] = () 

128 ) -> None: 

129 self.dimensions = NamedValueSet() 

130 self.elements = NamedValueSet() 

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

132 self.packers = {} 

133 self.version = version 

134 self.config = config 

135 self.commonSkyPixName = commonSkyPixName 

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

137 

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

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

140 

141 Parameters 

142 ---------- 

143 visitor : `DimensionConstructionVisitor` 

144 Visitor instance to add. 

145 """ 

146 self._todo[visitor.name] = visitor 

147 

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

149 """Add multiple visitors to the builder. 

150 

151 Parameters 

152 ---------- 

153 visitors : `Iterable` [ `DimensionConstructionVisitor` ] 

154 Visitor instances to add. 

155 """ 

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

157 

158 def finish(self) -> None: 

159 """Complete construction of the builder. 

160 

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

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

163 called only once, by `DimensionUniverse` itself. 

164 """ 

165 while self._todo: 

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

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

168 unblocked.sort() # Break ties lexicographically. 

169 if not unblocked: 

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

171 for name in unblocked: 

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

173 

174 version: int 

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

176 

177 Populated at builder construction. 

178 """ 

179 

180 commonSkyPixName: str 

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

182 `TopologicalRelationshipEndpoint` objects (`str`). 

183 

184 Populated at builder construction. 

185 """ 

186 

187 dimensions: NamedValueSet[Dimension] 

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

189 

190 Populated by `finish`. 

191 """ 

192 

193 elements: NamedValueSet[DimensionElement] 

194 """Set of all `DimensionElement` objects 

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

196 

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

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

199 set as well as `dimensions`. 

200 """ 

201 

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

203 """Dictionary containing all `TopologicalFamily` objects 

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

205 """ 

206 

207 packers: Dict[str, DimensionPackerFactory] 

208 """Dictionary containing all `DimensionPackerFactory` objects 

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

210 """