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 

24__all__ = ( 

25 "GovernorDimension", 

26) 

27 

28from types import MappingProxyType 

29from typing import ( 

30 AbstractSet, 

31 Iterable, 

32 Mapping, 

33 Optional, 

34 TYPE_CHECKING, 

35) 

36 

37from lsst.utils import doImport 

38 

39from .. import ddl 

40from ..named import NamedValueAbstractSet, NamedValueSet 

41from .._topology import TopologicalFamily, TopologicalSpace 

42 

43from ._elements import Dimension 

44from .construction import DimensionConstructionBuilder, DimensionConstructionVisitor 

45 

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

47 from ...registry.interfaces import ( 

48 Database, 

49 GovernorDimensionRecordStorage, 

50 StaticTablesContext, 

51 ) 

52 

53 

54class GovernorDimension(Dimension): 

55 """A special `Dimension` with no dependencies and a small number of rows, 

56 used to group the dimensions that depend on it. 

57 

58 Parameters 

59 ---------- 

60 name : `str` 

61 Name of the dimension. 

62 storage : `dict` 

63 Fully qualified name of the `GovernorDimensionRecordStorage` subclass 

64 that will back this element in the registry (in a "cls" key) along 

65 with any other construction keyword arguments (in other keys). 

66 metadata : `NamedValueAbstractSet` [ `ddl.FieldSpec` ] 

67 Field specifications for all non-key fields in this dimension's table. 

68 uniqueKeys : `NamedValueAbstractSet` [ `ddl.FieldSpec` ] 

69 Fields that can each be used to uniquely identify this dimension (given 

70 values for all required dimensions). The first of these is used as 

71 (part of) this dimension's table's primary key, while others are used 

72 to define unique constraints. 

73 

74 Notes 

75 ----- 

76 Most dimensions have exactly one governor dimension as a required 

77 dependency, and queries that involve those dimensions are always expected 

78 to explicitly identify the governor dimension value(s), rather than 

79 retrieve all matches from the database. Because governor values are thus 

80 almost always known at query-generation time, they can be used there to 

81 simplify queries, provide sensible defaults, or check in advance for common 

82 mistakes that might otherwise yield confusing (albeit formally correct) 

83 results instead of straightforward error messages. 

84 

85 Governor dimensions may not be associated with any kind of topological 

86 extent. 

87 

88 Governor dimension rows are often affiliated with a Python class or 

89 instance (e.g. `lsst.obs.base.Instrument`) that is capable of generating 

90 the rows of at least some dependent dimensions or providing other related 

91 functionality. In the future, we hope to attach these instances to 

92 governor dimension records (instantiating them from information in the 

93 database row when it is fetched), and use those objects to add additional 

94 functionality to governor dimensions, but a number of (code) dependency 

95 relationships would need to be reordered first. 

96 """ 

97 def __init__( 

98 self, 

99 name: str, 

100 storage: dict, *, 

101 metadata: NamedValueAbstractSet[ddl.FieldSpec], 

102 uniqueKeys: NamedValueAbstractSet[ddl.FieldSpec], 

103 ): 

104 self._name = name 

105 self._storage = storage 

106 self._required = NamedValueSet({self}).freeze() 

107 self._metadata = metadata 

108 self._uniqueKeys = uniqueKeys 

109 if self.primaryKey.getPythonType() is not str: 

110 raise TypeError(f"Governor dimension '{name}' must have a string primary key (configured type " 

111 f"is {self.primaryKey.dtype.__name__}).") 

112 if self.primaryKey.length is not None and self.primaryKey.length > self.MAX_KEY_LENGTH: 

113 raise TypeError(f"Governor dimension '{name}' must have a string primary key with length <= " 

114 f"{self.MAX_KEY_LENGTH} (configured value is {self.primaryKey.length}).") 

115 

116 MAX_KEY_LENGTH = 128 

117 

118 @property 

119 def name(self) -> str: 

120 # Docstring inherited from TopoogicalRelationshipEndpoint. 

121 return self._name 

122 

123 @property 

124 def required(self) -> NamedValueAbstractSet[Dimension]: 

125 # Docstring inherited from DimensionElement. 

126 return self._required 

127 

128 @property 

129 def implied(self) -> NamedValueAbstractSet[Dimension]: 

130 # Docstring inherited from DimensionElement. 

131 return NamedValueSet().freeze() 

132 

133 @property 

134 def topology(self) -> Mapping[TopologicalSpace, TopologicalFamily]: 

135 # Docstring inherited from TopologicalRelationshipEndpoint 

136 return MappingProxyType({}) 

137 

138 @property 

139 def metadata(self) -> NamedValueAbstractSet[ddl.FieldSpec]: 

140 # Docstring inherited from DimensionElement. 

141 return self._metadata 

142 

143 @property 

144 def uniqueKeys(self) -> NamedValueAbstractSet[ddl.FieldSpec]: 

145 # Docstring inherited from Dimension. 

146 return self._uniqueKeys 

147 

148 def makeStorage( 

149 self, 

150 db: Database, *, 

151 context: Optional[StaticTablesContext] = None, 

152 ) -> GovernorDimensionRecordStorage: 

153 """Construct the `DimensionRecordStorage` instance that should 

154 be used to back this element in a registry. 

155 

156 Parameters 

157 ---------- 

158 db : `Database` 

159 Interface to the underlying database engine and namespace. 

160 context : `StaticTablesContext`, optional 

161 If provided, an object to use to create any new tables. If not 

162 provided, ``db.ensureTableExists`` should be used instead. 

163 

164 Returns 

165 ------- 

166 storage : `GovernorDimensionRecordStorage` 

167 Storage object that should back this element in a registry. 

168 """ 

169 from ...registry.interfaces import GovernorDimensionRecordStorage 

170 cls = doImport(self._storage["cls"]) 

171 assert issubclass(cls, GovernorDimensionRecordStorage) 

172 return cls.initialize(db, self, context=context, config=self._storage) 

173 

174 

175class GovernorDimensionConstructionVisitor(DimensionConstructionVisitor): 

176 """A construction visitor for `GovernorDimension`. 

177 

178 Parameters 

179 ---------- 

180 name : `str` 

181 Name of the dimension. 

182 storage : `dict` 

183 Fully qualified name of the `GovernorDimensionRecordStorage` subclass 

184 that will back this element in the registry (in a "cls" key) along 

185 with any other construction keyword arguments (in other keys). 

186 metadata : `Iterable` [ `ddl.FieldSpec` ] 

187 Field specifications for all non-key fields in this element's table. 

188 uniqueKeys : `Iterable` [ `ddl.FieldSpec` ] 

189 Fields that can each be used to uniquely identify this dimension (given 

190 values for all required dimensions). The first of these is used as 

191 (part of) this dimension's table's primary key, while others are used 

192 to define unique constraints. 

193 """ 

194 def __init__( 

195 self, 

196 name: str, 

197 storage: dict, *, 

198 metadata: Iterable[ddl.FieldSpec] = (), 

199 uniqueKeys: Iterable[ddl.FieldSpec] = (), 

200 ): 

201 super().__init__(name) 

202 self._storage = storage 

203 self._metadata = NamedValueSet(metadata).freeze() 

204 self._uniqueKeys = NamedValueSet(uniqueKeys).freeze() 

205 

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

207 # Docstring inherited from DimensionConstructionVisitor. 

208 return False 

209 

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

211 # Docstring inherited from DimensionConstructionVisitor. 

212 # Special handling for creating Dimension instances. 

213 dimension = GovernorDimension( 

214 self.name, 

215 storage=self._storage, 

216 metadata=self._metadata, 

217 uniqueKeys=self._uniqueKeys, 

218 ) 

219 builder.dimensions.add(dimension) 

220 builder.elements.add(dimension)