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

43 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-03-12 10:07 +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 

30from abc import ABC, abstractmethod 

31from collections.abc import Set 

32from typing import TYPE_CHECKING 

33 

34from .._named import NamedValueSet 

35from .._topology import TopologicalFamily, TopologicalSpace 

36 

37if TYPE_CHECKING: 

38 from ._config import DimensionConfig 

39 from ._elements import Dimension, DimensionElement 

40 

41 

42class DimensionConstructionVisitor(ABC): 

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

44 

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

46 `DimensionConstructionBuilder`. 

47 """ 

48 

49 @abstractmethod 

50 def has_dependencies_in(self, others: Set[str]) -> bool: 

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

52 

53 Tests whether other entities this visitor depends on have already 

54 been constructed. 

55 

56 Parameters 

57 ---------- 

58 others : `~collections.abc.Set` [ `str` ] 

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

60 

61 Returns 

62 ------- 

63 blocked : `bool` 

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

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

66 be called. 

67 """ 

68 raise NotImplementedError() 

69 

70 @abstractmethod 

71 def visit(self, name: str, builder: DimensionConstructionBuilder) -> None: 

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

73 

74 Parameters 

75 ---------- 

76 name : `str` 

77 Name of the entity being added. 

78 builder : `DimensionConstructionBuilder` 

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

80 obtained. 

81 

82 Notes 

83 ----- 

84 Subclasses may assume (and callers must guarantee) that 

85 `hasDependenciesIn` would return `False` prior to `visit` being called. 

86 """ 

87 raise NotImplementedError() 

88 

89 

90class DimensionConstructionBuilder: 

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

92 

93 `DimensionConstructionVisitor` objects can be added to a 

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

95 in a deterministic order consistent with their dependency relationships 

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

97 

98 Parameters 

99 ---------- 

100 version : `int` 

101 Version for the `DimensionUniverse`. 

102 commonSkyPixName : `str` 

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

104 spatial `TopologicalRelationshipEndpoint` objects. 

105 config : `DimensionConfig` 

106 The dimension universe to be used. 

107 namespace : `str`, optional 

108 The namespace to assign to this universe. 

109 """ 

110 

111 def __init__( 

112 self, 

113 version: int, 

114 commonSkyPixName: str, 

115 config: DimensionConfig, 

116 *, 

117 namespace: str | None = None, 

118 ) -> None: 

119 self.dimensions = NamedValueSet() 

120 self.elements = NamedValueSet() 

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

122 self.version = version 

123 self.namespace = namespace 

124 self.config = config 

125 self.commonSkyPixName = commonSkyPixName 

126 self._todo: dict[str, DimensionConstructionVisitor] = {} 

127 

128 def add(self, name: str, visitor: DimensionConstructionVisitor) -> None: 

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

130 

131 Parameters 

132 ---------- 

133 name : `str` 

134 Name of the object the visitor creates. 

135 visitor : `DimensionConstructionVisitor` 

136 Visitor instance to add. 

137 """ 

138 self._todo[name] = visitor 

139 

140 def finish(self) -> None: 

141 """Complete construction of the builder. 

142 

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

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

145 called only once, by `DimensionUniverse` itself. 

146 """ 

147 while self._todo: 

148 unblocked = [ 

149 name 

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

151 if not visitor.has_dependencies_in(self._todo.keys()) 

152 ] 

153 unblocked.sort() # Break ties lexicographically. 

154 if not unblocked: 

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

156 for name in unblocked: 

157 self._todo.pop(name).visit(name, self) 

158 

159 version: int 

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

161 

162 Populated at builder construction. 

163 """ 

164 

165 namespace: str | None 

166 """Namespace for the `DimensionUniverse` (`str`) 

167 

168 Populated at builder construction. 

169 """ 

170 

171 commonSkyPixName: str 

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

173 `TopologicalRelationshipEndpoint` objects (`str`). 

174 

175 Populated at builder construction. 

176 """ 

177 

178 dimensions: NamedValueSet[Dimension] 

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

180 

181 Populated by `finish`. 

182 """ 

183 

184 elements: NamedValueSet[DimensionElement] 

185 """Set of all `DimensionElement` objects 

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

187 

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

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

190 set as well as `dimensions`. 

191 """ 

192 

193 topology: dict[TopologicalSpace, NamedValueSet[TopologicalFamily]] 

194 """Dictionary containing all `TopologicalFamily` objects 

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

196 """